1<?php
2/*
3 *  $Id: Node.php 7490 2010-03-29 19:53:27Z jwage $
4 *
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 *
17 * This software consists of voluntary contributions made by many individuals
18 * and is licensed under the LGPL. For more information, see
19 * <http://www.doctrine-project.org>.
20 */
21
22/**
23 * Doctrine_Node
24 *
25 * @package     Doctrine
26 * @subpackage  Node
27 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
28 * @link        www.doctrine-project.org
29 * @since       1.0
30 * @version     $Revision: 7490 $
31 * @author      Joe Simms <joe.simms@websites4.com>
32 */
33class Doctrine_Node implements IteratorAggregate
34{
35    /**
36     * @param object    $record   reference to associated Doctrine_Record instance
37     */
38    protected $record;
39
40    /**
41     * @param array     $options
42     */
43    protected $options;
44
45    /**
46     * @param string     $iteratorType  (Pre | Post | Level)
47     */
48    protected $iteratorType;
49
50    /**
51     * @param array     $iteratorOptions
52     */
53    protected $iteratorOptions;
54
55    /**
56     * The tree to which the node belongs.
57     *
58     * @var unknown_type
59     */
60    protected $_tree;
61
62    /**
63     * contructor, creates node with reference to record and any options
64     *
65     * @param object $record                    instance of Doctrine_Record
66     * @param array $options                    options
67     */
68    public function __construct(Doctrine_Record $record, $options)
69    {
70        $this->record = $record;
71        $this->options = $options;
72
73        // Make sure that the tree object of the root class is used in the case
74        // of column aggregation inheritance (single table inheritance).
75        $class = $record->getTable()->getComponentName();
76        $thisTable = $record->getTable();
77        $table = $thisTable;
78        if ($thisTable->getOption('inheritanceMap')) {
79            // Move up the hierarchy until we find the "subclasses" option. This option
80            // MUST be set on the root class of the user's hierarchy that uses STI.
81            while ( ! $subclasses = $table->getOption('subclasses')) {
82                $class = get_parent_class($class);
83                $reflectionClass = new ReflectionClass($class);
84                if ($reflectionClass->isAbstract()) {
85                    continue;
86                }
87                if ($class == 'Doctrine_Record') {
88                    throw new Doctrine_Node_Exception("No subclasses specified. You are "
89                            . "using Single Table Inheritance with NestedSet but you have "
90                            . "not specified the subclasses correctly. Make sure you use "
91                            . "setSubclasses() in the root class of your hierarchy.");
92                }
93                $table = $table->getConnection()->getTable($class);
94            }
95        }
96        if ($thisTable !== $table) {
97            $this->_tree = $table->getTree();
98        } else {
99            $this->_tree = $thisTable->getTree();
100        }
101    }
102
103    /**
104     * Factory method for creating a Node.
105     *
106     * This is a factory method that returns node instance based upon chosen
107     * implementation.
108     *
109     * @param object $record                    instance of Doctrine_Record
110     * @param string $implName                  implementation (NestedSet, AdjacencyList, MaterializedPath)
111     * @param array $options                    options
112     * @return Doctrine_Node
113     * @throws Doctrine_Node_Exception          if $implName is not a valid class
114     */
115    public static function factory(Doctrine_Record $record, $implName, $options = array())
116    {
117        $class = 'Doctrine_Node_' . $implName;
118
119        if ( ! class_exists($class)) {
120            throw new Doctrine_Node_Exception("The class $class must exist and extend Doctrine_Node");
121        }
122
123        return new $class($record, $options);
124    }
125
126    /**
127     * setter for record attribute
128     *
129     * @param object $record                    instance of Doctrine_Record
130     */
131    public function setRecord(Doctrine_Record $record)
132    {
133        $this->record = $record;
134    }
135
136    /**
137     * getter for record attribute
138     *
139     * @return Doctrine_Record
140     */
141    public function getRecord()
142    {
143        return $this->record;
144    }
145
146    /**
147     * convenience function for getIterator
148     *
149     * @param string $type                      type of iterator (Pre | Post | Level)
150     * @param array $options                    options
151     */
152    public function traverse($type = 'Pre', $options = array())
153    {
154        return $this->getIterator($type, $options);
155    }
156
157    /**
158     * get iterator
159     *
160     * @param string $type                      type of iterator (Pre | Post | Level)
161     * @param array $options                    options
162     */
163    public function getIterator($type = null, $options = null)
164    {
165        if ($type === null) {
166            $type = (isset($this->iteratorType) ? $this->iteratorType : 'Pre');
167        }
168
169        if ($options === null) {
170            $options = (isset($this->iteratorOptions) ? $this->iteratorOptions : array());
171        }
172
173        $implName = $this->record->getTable()->getOption('treeImpl');
174        $iteratorClass = 'Doctrine_Node_' . $implName . '_' . ucfirst(strtolower($type)) . 'OrderIterator';
175
176        return new $iteratorClass($this->record, $options);
177    }
178
179    /**
180     * sets node's iterator type
181     *
182     * @param int
183     */
184    public function setIteratorType($type)
185    {
186        $this->iteratorType = $type;
187    }
188
189    /**
190     * sets node's iterator options
191     *
192     * @param int
193     */
194    public function setIteratorOptions($options)
195    {
196        $this->iteratorOptions = $options;
197    }
198}
199