Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • AdditionalMenuWidget_Item
  • AdvancedSliderHomepageWidget_Item
  • AssetManagerFolder
  • BannerWidget_Item
  • BaseObjectDecorator
  • BookingOrder
  • BookingPaymentMethod
  • BookingService
  • Boolean
  • ButtonsBlockHomepageWidget_Item
  • CarouselHomepageWidget_Item
  • CatalogRubricsHomepageWidget_CatalogDecorator
  • ClientEmailOrderNotification
  • ClientVKOrderNotification
  • ComponentSet
  • Currency
  • DatabaseAdmin
  • DataObject
  • DataObjectDecorator
  • DataObjectLog
  • DataObjectSet
  • DataObjectSet_Iterator
  • Date
  • DB
  • DBField
  • Decimal
  • DocumentItem
  • DocumentPage_File
  • Double
  • Enum
  • ErrorPageSubsite
  • FileDataObjectTrackingDecorator
  • FileImportDecorator
  • Float
  • ForeignKey
  • Hierarchy
  • HTMLText
  • HTMLVarchar
  • ImportLog_Item
  • Int
  • ManagerEmailOrderNotification
  • Material3D_File
  • MediawebPage_File
  • MediawebPage_Photo
  • MobileContentDecorator
  • Money
  • MultiEnum
  • MySQLDatabase
  • MySQLQuery
  • OrderDataObject
  • OrderHandlersDecorator
  • OrderItemVariationDecorator
  • OrderService
  • OrderServiceOrder
  • OrdersExportDecorator
  • PageIcon
  • PageWidgets
  • Payment
  • PaymentMethodShippingDecorator
  • PaymentOrderExtension
  • Percentage
  • PhotoAlbumItem
  • PhotoAlbumProductLinkDecorator
  • PhotoAlbumWidgetLinkDecorator
  • PhotoGalleryHomepageWidget_Item
  • PrimaryKey
  • Product3DDecorator
  • ProductCatalogCatalogLinkedDecorator
  • RatePeriod
  • RealtyImportLog
  • RealtyImportLog_Item
  • RedirectEntry
  • RoomOrder
  • RoomOrderPerson
  • RoomRate
  • RoomService
  • RoomServiceOrder
  • SberbankPaymentDecorator
  • SeoOpenGraphPageDecorator
  • ServiceOrder
  • ShippingMethodPaymentDecorator
  • ShopCountry
  • SimpleOrderCatalogDecorator
  • SimpleOrderProductDecorator
  • SiteConfigWidgets
  • SiteTreeDecorator
  • SiteTreeImportDecorator
  • SliderHomepageWidget_Item
  • SMSCOrderNotification
  • SMSOrderNotification
  • SortableDataObject
  • SQLMap
  • SQLMap_Iterator
  • SQLQuery
  • SS_Database
  • SS_Datetime
  • SS_Query
  • StringField
  • SubsiteDomain
  • Text
  • TextAnonsWidget_Item
  • Texture3D_File
  • Time
  • Varchar
  • Versioned
  • Versioned_Version
  • VideoCategory
  • VideoEntry
  • VKNotificationQueue
  • WebylonWidget_Item
  • YaMoneyPaymentDecorator
  • Year

Interfaces

  • CompositeDBField
  • CurrentPageIdentifier
  • DataObjectInterface
  1 <?php
  2 
  3 /**
  4  * DataObjects that use the Hierachy decorator can be be organised as a hierachy, with children and parents.
  5  * The most obvious example of this is SiteTree.
  6  * @package sapphire
  7  * @subpackage model
  8  */
  9 class Hierarchy extends DataObjectDecorator {
 10 
 11     protected $markedNodes;
 12     protected $markingFilter;
 13     /**
 14      * @var Int
 15      */
 16     protected $_cache_numChildren;
 17 
 18     function augmentSQL(SQLQuery &$query) {
 19         
 20     }
 21 
 22     function augmentDatabase() {
 23         
 24     }
 25 
 26     function augmentWrite(&$manipulation) {
 27         
 28     }
 29 
 30     /**
 31      * Returns the children of this DataObject as an XHTML UL. This will be called recursively on each child,
 32      * so if they have children they will be displayed as a UL inside a LI.
 33      * @param string $attributes Attributes to add to the UL.
 34      * @param string $titleEval PHP code to evaluate to start each child - this should include '<li>'
 35      * @param string $extraArg Extra arguments that will be passed on to children, for if they overload this function.
 36      * @param boolean $limitToMarked Display only marked children.
 37      * @param string $childrenMethod The name of the method used to get children from each object
 38      * @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You should not change this.
 39      * @param int $minNodeCount
 40      * @return string
 41      */
 42     public function getChildrenAsUL($attributes = "", $titleEval = '"<li>" . $child->Title', $extraArg = null, $limitToMarked = false, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren", $rootCall = true, $minNodeCount = 30) {
 43         if ($limitToMarked && $rootCall) {
 44             $this->markingFinished($numChildrenMethod);
 45         }
 46 
 47         if ($this->owner->hasMethod($childrenMethod)) {
 48             $children = $this->owner->$childrenMethod($extraArg);
 49         } else {
 50             user_error(sprintf("Can't find the method '%s' on class '%s' for getting tree children",
 51                             $childrenMethod, get_class($this->owner)), E_USER_ERROR);
 52         }
 53 
 54         if ($children) {
 55             if ($attributes) {
 56                 $attributes = " $attributes";
 57             }
 58 
 59             $output = "<ul$attributes>\n";
 60 
 61             foreach ($children as $child) {
 62                 if (!$limitToMarked || $child->isMarked()) {
 63                     $foundAChild = true;
 64                     $output .= eval("return $titleEval;") . "\n" .
 65                             $child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked, $childrenMethod, $numChildrenMethod, false, $minNodeCount) . "</li>\n";
 66                 }
 67             }
 68 
 69             $output .= "</ul>\n";
 70         }
 71 
 72         if (isset($foundAChild) && $foundAChild) {
 73             return $output;
 74         }
 75     }
 76 
 77     /**
 78      * Mark a segment of the tree, by calling mark().
 79      * The method performs a breadth-first traversal until the number of nodes is more than minCount.
 80      * This is used to get a limited number of tree nodes to show in the CMS initially.
 81      * 
 82      * This method returns the number of nodes marked.  After this method is called other methods 
 83      * can check isExpanded() and isMarked() on individual nodes.
 84      * 
 85      * @param int $minNodeCount The minimum amount of nodes to mark.
 86      * @return int The actual number of nodes marked.
 87      */
 88     public function markPartialTree($minNodeCount = 30, $context = null, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren") {
 89         if (!is_numeric($minNodeCount))
 90             $minNodeCount = 30;
 91 
 92         $this->markedNodes = array($this->owner->ID => $this->owner);
 93         $this->owner->markUnexpanded();
 94 
 95         // foreach can't handle an ever-growing $nodes list
 96         while (list($id, $node) = each($this->markedNodes)) {
 97             $this->markChildren($node, $context, $childrenMethod, $numChildrenMethod);
 98             if ($minNodeCount && sizeof($this->markedNodes) >= $minNodeCount) {
 99                 break;
100             }
101         }
102         return sizeof($this->markedNodes);
103     }
104 
105     /**
106      * Filter the marking to only those object with $node->$parameterName = $parameterValue
107      * @param string $parameterName The parameter on each node to check when marking.
108      * @param mixed $parameterValue The value the parameter must be to be marked.
109      */
110     public function setMarkingFilter($parameterName, $parameterValue) {
111         $this->markingFilter = array(
112             "parameter" => $parameterName,
113             "value" => $parameterValue
114         );
115     }
116 
117     /**
118      * Filter the marking to only those where the function returns true.
119      * The node in question will be passed to the function.
120      * @param string $funcName The function name.
121      */
122     public function setMarkingFilterFunction($funcName) {
123         $this->markingFilter = array(
124             "func" => $funcName,
125         );
126     }
127 
128     /**
129      * Returns true if the marking filter matches on the given node.
130      * @param DataObject $node Node to check.
131      * @return boolean
132      */
133     public function markingFilterMatches($node) {
134         if (!$this->markingFilter) {
135             return true;
136         }
137 
138         if (isset($this->markingFilter['parameter']) && $parameterName = $this->markingFilter['parameter']) {
139             if (is_array($this->markingFilter['value'])) {
140                 $ret = false;
141                 foreach ($this->markingFilter['value'] as $value) {
142                     $ret = $ret || $node->$parameterName == $value;
143                     if ($ret == true) {
144                         break;
145                     }
146                 }
147                 return $ret;
148             } else {
149                 return ($node->$parameterName == $this->markingFilter['value']);
150             }
151         } else if ($func = $this->markingFilter['func']) {
152             return call_user_func($func, $node);
153         }
154     }
155 
156     /**
157      * Mark all children of the given node that match the marking filter.
158      * @param DataObject $node Parent node.
159      */
160     public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren") {
161         if ($node->hasMethod($childrenMethod)) {
162             $children = $node->$childrenMethod($context);
163         } else {
164             user_error(sprintf("Can't find the method '%s' on class '%s' for getting tree children",
165                             $childrenMethod, get_class($this->owner)), E_USER_ERROR);
166         }
167 
168         $node->markExpanded();
169         if ($children) {
170             foreach ($children as $child) {
171                 if (!$this->markingFilter || $this->markingFilterMatches($child)) {
172                     if ($child->$numChildrenMethod()) {
173                         $child->markUnexpanded();
174                     } else {
175                         $child->markExpanded();
176                     }
177                     $this->markedNodes[$child->ID] = $child;
178                 }
179             }
180         }
181     }
182 
183     /**
184      * Ensure marked nodes that have children are also marked expanded.
185      * Call this after marking but before iterating over the tree.
186      */
187     protected function markingFinished($numChildrenMethod = "numChildren") {
188         // Mark childless nodes as expanded.
189         if ($this->markedNodes) {
190             foreach ($this->markedNodes as $id => $node) {
191                 if (!$node->isExpanded() && !$node->$numChildrenMethod()) {
192                     $node->markExpanded();
193                 }
194             }
195         }
196     }
197 
198     /**
199      * Return CSS classes of 'unexpanded', 'closed', both, or neither, depending on
200      * the marking of this DataObject.
201      */
202     public function markingClasses() {
203         $classes = '';
204         if (!$this->isExpanded()) {
205             $classes .= " unexpanded";
206         }
207         if (!$this->isTreeOpened()) {
208             $classes .= " closed";
209         }
210         return $classes;
211     }
212 
213     /**
214      * Mark the children of the DataObject with the given ID.
215      * @param int $id ID of parent node.
216      * @param boolean $open If this is true, mark the parent node as opened.
217      */
218     public function markById($id, $open = false) {
219         if (isset($this->markedNodes[$id])) {
220             $this->markChildren($this->markedNodes[$id]);
221             if ($open) {
222                 $this->markedNodes[$id]->markOpened();
223             }
224             return true;
225         } else {
226             return false;
227         }
228     }
229 
230     /**
231      * Expose the given object in the tree, by marking this page and all it ancestors.
232      * @param DataObject $childObj 
233      */
234     public function markToExpose($childObj) {
235         if (is_object($childObj)) {
236             $stack = array_reverse($childObj->parentStack());
237             foreach ($stack as $stackItem) {
238                 $this->markById($stackItem->ID, true);
239             }
240         }
241     }
242 
243     /**
244      * Return the IDs of all the marked nodes
245      */
246     public function markedNodeIDs() {
247         return array_keys($this->markedNodes);
248     }
249 
250     /**
251      * Return an array of this page and its ancestors, ordered item -> root.
252      * @return array
253      */
254     public function parentStack() {
255         $p = $this->owner;
256 
257         while ($p) {
258             $stack[] = $p;
259             $p = $p->ParentID ? $p->Parent() : null;
260         }
261 
262         return $stack;
263     }
264 
265     /**
266      * True if this DataObject is marked.
267      * @var boolean
268      */
269     protected static $marked = array();
270     /**
271      * True if this DataObject is expanded.
272      * @var boolean
273      */
274     protected static $expanded = array();
275     /**
276      * True if this DataObject is opened.
277      * @var boolean
278      */
279     protected static $treeOpened = array();
280 
281     /**
282      * Mark this DataObject as expanded.
283      */
284     public function markExpanded() {
285         self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
286         self::$expanded[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
287     }
288 
289     /**
290      * Mark this DataObject as unexpanded.
291      */
292     public function markUnexpanded() {
293         self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
294         self::$expanded[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = false;
295     }
296 
297     /**
298      * Mark this DataObject's tree as opened.
299      */
300     public function markOpened() {
301         self::$marked[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
302         self::$treeOpened[ClassInfo::baseDataClass($this->owner->class)][$this->owner->ID] = true;
303     }
304 
305     /**
306      * Check if this DataObject is marked.
307      * @return boolean
308      */
309     public function isMarked() {
310         $baseClass = ClassInfo::baseDataClass($this->owner->class);
311         $id = $this->owner->ID;
312         return isset(self::$marked[$baseClass][$id]) ? self::$marked[$baseClass][$id] : false;
313     }
314 
315     /**
316      * Check if this DataObject is expanded.
317      * @return boolean
318      */
319     public function isExpanded() {
320         $baseClass = ClassInfo::baseDataClass($this->owner->class);
321         $id = $this->owner->ID;
322         return isset(self::$expanded[$baseClass][$id]) ? self::$expanded[$baseClass][$id] : false;
323     }
324 
325     /**
326      * Check if this DataObject's tree is opened.
327      */
328     public function isTreeOpened() {
329         $baseClass = ClassInfo::baseDataClass($this->owner->class);
330         $id = $this->owner->ID;
331         return isset(self::$treeOpened[$baseClass][$id]) ? self::$treeOpened[$baseClass][$id] : false;
332     }
333 
334     /**
335      * Return a partial tree as an HTML UL.
336      */
337     public function partialTreeAsUL($minCount = 50) {
338         $children = $this->owner->AllChildren();
339         if ($children) {
340             if ($attributes)
341                 $attributes = " $attributes";
342             $output = "<ul$attributes>\n";
343 
344             foreach ($children as $child) {
345                 $output .= eval("return $titleEval;") . "\n" .
346                         $child->getChildrenAsUL("", $titleEval, $extraArg) . "</li>\n";
347             }
348             $output .= "</ul>\n";
349         }
350         return $output;
351     }
352 
353     /**
354      * Get a list of this DataObject's and all it's descendants IDs.
355      * @return int
356      */
357     public function getDescendantIDList() {
358         $idList = array();
359         $this->loadDescendantIDListInto($idList);
360         return $idList;
361     }
362 
363     /**
364      * Get a list of this DataObject's and all it's descendants ID, and put it in $idList.
365      * @var array $idList Array to put results in.
366      */
367     public function loadDescendantIDListInto(&$idList) {
368         if ($children = $this->AllChildren()) {
369             foreach ($children as $child) {
370                 if (in_array($child->ID, $idList)) {
371                     continue;
372                 }
373                 $idList[] = $child->ID;
374                 $ext = $child->getExtensionInstance('Hierarchy');
375                 $ext->setOwner($child);
376                 $ext->loadDescendantIDListInto($idList);
377                 $ext->clearOwner();
378             }
379         }
380     }
381 
382     /**
383      * Get the children for this DataObject.
384      * @return DataObjectSet
385      */
386     public function Children() {
387         if (!(isset($this->_cache_children) && $this->_cache_children)) {
388             $result = $this->owner->stageChildren(false);
389             if (isset($result)) {
390                 $this->_cache_children = new DataObjectSet();
391                 foreach ($result as $child) {
392                     if ($child->canView()) {
393                         $this->_cache_children->push($child);
394                     }
395                 }
396             }
397         }
398         return $this->_cache_children;
399     }
400 
401     /**
402      * Return all children, including those 'not in menus'.
403      * @return DataObjectSet
404      */
405     public function AllChildren($st=1) {
406         return $this->owner->stageChildren($st);
407     }
408 
409     /**
410      * Return all children, including those that have been deleted but are still in live.
411      * Deleted children will be marked as "DeletedFromStage"
412      * Added children will be marked as "AddedToStage"
413      * Modified children will be marked as "ModifiedOnStage"
414      * Everything else has "SameOnStage" set, as an indicator that this information has been looked up.
415      * @return DataObjectSet
416      */
417     public function AllChildrenIncludingDeleted($context = null) {
418         return $this->doAllChildrenIncludingDeleted($context);
419     }
420 
421     /**
422      * @see AllChildrenIncludingDeleted
423      *
424      * @param unknown_type $context
425      * @return DataObjectSet
426      */
427     public function doAllChildrenIncludingDeleted($context = null) {
428         if (!$this->owner)
429             user_error('Hierarchy::doAllChildrenIncludingDeleted() called without $this->owner');
430 
431         $idxStageChildren = array();
432         $idxLiveChildren = array();
433 
434         $baseClass = ClassInfo::baseDataClass($this->owner->class);
435         if ($baseClass) {
436             $stageChildren = $this->owner->stageChildren(true);
437 
438             // Add live site content that doesn't exist on the stage site, if required.
439             if ($this->owner->hasExtension('Versioned')) {
440                 // Next, go through the live children.  Only some of these will be listed
441                 $liveChildren = $this->owner->liveChildren(true, true);
442                 if ($liveChildren) {
443                     foreach ($liveChildren as $child) {
444                         $stageChildren->push($child);
445                     }
446                 }
447             }
448 
449             $this->owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren, $context);
450         } else {
451             user_error("Hierarchy::AllChildren() Couldn't determine base class for '{$this->owner->class}'", E_USER_ERROR);
452         }
453 
454         return $stageChildren;
455     }
456 
457     /**
458      * Return all the children that this page had, including pages that were deleted
459      * from both stage & live.
460      */
461     public function AllHistoricalChildren() {
462         $baseClass = ClassInfo::baseDataClass($this->owner->class);
463         return Versioned::get_including_deleted($baseClass,
464                 "\"ParentID\" = " . (int) $this->owner->ID, "\"$baseClass\".\"ID\" ASC");
465     }
466 
467     /**
468      * Return the number of children that this page ever had, including pages that were deleted
469      */
470     public function numHistoricalChildren() {
471         $query = Versioned::get_including_deleted_query(ClassInfo::baseDataClass($this->owner->class),
472                         "\"ParentID\" = " . (int) $this->owner->ID);
473 
474         return $query->unlimitedRowCount();
475     }
476 
477     /**
478      * Return the number of direct children.
479      * By default, values are cached after the first invocation.
480      * Can be augumented by {@link augmentNumChildrenCountQuery()}.
481      * 
482      * @param Boolean $cache
483      * @return int
484      */
485     public function numChildren($cache = true) {
486         $baseClass = ClassInfo::baseDataClass($this->owner->class);
487 
488         // Build the cache for this class if it doesn't exist.
489         if (!$cache || !is_numeric($this->_cache_numChildren)) {
490             // We build the query in an extension-friendly way.
491             $query = new SQLQuery(
492                             "COUNT(*)",
493                             "\"$baseClass\"",
494                             sprintf('"ParentID" = %d', $this->owner->ID)
495             );
496             $this->owner->extend('augmentSQL', $query);
497             $this->owner->extend('augmentNumChildrenCountQuery', $query);
498 
499             $this->_cache_numChildren = (int) $query->execute()->value();
500         }
501 
502         // If theres no value in the cache, it just means that it doesn't have any children.
503         return $this->_cache_numChildren;
504     }
505 
506     /**
507      * Return children from the stage site
508      * 
509      * @param showAll Inlcude all of the elements, even those not shown in the menus.
510      *   (only applicable when extension is applied to {@link SiteTree}).
511      * @return DataObjectSet
512      */
513     public function stageChildren($showAll = false) {
514         if ($this->owner->db('ShowInMenus')) {
515             $extraFilter = ($showAll) ? '' : " AND \"ShowInMenus\"=1";
516         } else {
517             $extraFilter = '';
518         }
519         if (!isset($_REQUEST['filter']) || $_REQUEST['filter'] != 'CMSSiteTreeFilter_Search') {
520             if ((($showAll === false) || ($showAll == 1)) && $this->owner->db('ShowOnlyInTab')) {
521                 $extraFilter .= " AND \"ShowOnlyInTab\"=0";
522             }
523         }
524 
525         $baseClass = ClassInfo::baseDataClass($this->owner->class);
526         // hide files and photos from import (Hidden = 1)
527         if (is_a($this->owner, 'File') && $this->owner->hasDatabaseField('Hidden')) {
528             $extraFilter .= " AND \"Hidden\"=0";
529         }
530 
531         $limit = '';
532         $sort = '';
533 
534         // check if the request comes from the cms-admin section, allow for content and getsubtree only
535         $cms = preg_match("/admin\/?$/", $_SERVER['REQUEST_URI']) || preg_match("/admin\/getsubtree/", $_SERVER['REQUEST_URI']);
536 
537         // if searched, make $cms filter false
538         if (preg_match("/admin\/filterSiteTree/", $_SERVER['REQUEST_URI']))
539             $cms = false;
540 
541         // get limit
542         $limit = $this->owner->NumberCMSChildren;
543 
544         if ($cms && $limit)
545             $sort = "Created DESC";
546         else
547             $limit = '';
548 
549         $staged = DataObject::get($baseClass, "\"{$baseClass}\".\"ParentID\" = "
550                         . (int) $this->owner->ID . " AND \"{$baseClass}\".\"ID\" != " . (int) $this->owner->ID
551                         . $extraFilter, $sort, "", $limit);
552 
553         if (!$staged)
554             $staged = new DataObjectSet();
555         $this->owner->extend("augmentStageChildren", $staged, $showAll);
556         return $staged;
557     }
558 
559     /**
560      * Return children from the live site, if it exists.
561      * 
562      * @param boolean $showAll Include all of the elements, even those not shown in the menus.
563      *   (only applicable when extension is applied to {@link SiteTree}).
564      * @param boolean $onlyDeletedFromStage Only return items that have been deleted from stage
565      * @return DataObjectSet
566      */
567     public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
568         if ($this->owner->db('ShowInMenus')) {
569             $extraFilter = ($showAll) ? '' : " AND \"ShowInMenus\"=1";
570         } else {
571             $extraFilter = '';
572         }
573         if (!isset($_REQUEST['filter']) || $_REQUEST['filter'] != 'CMSSiteTreeFilter_Search') {
574             if ($this->owner->db('ShowOnlyInTab')) {
575                 $extraFilter .= " AND \"ShowOnlyInTab\"=0";
576             }
577         }
578         $join = "";
579 
580         $baseClass = ClassInfo::baseDataClass($this->owner->class);
581 
582         $filter = "\"{$baseClass}\".\"ParentID\" = " . (int) $this->owner->ID
583                 . " AND \"{$baseClass}\".\"ID\" != " . (int) $this->owner->ID
584                 . $extraFilter;
585 
586         if ($onlyDeletedFromStage) {
587             // Note that the lack of double-quotes around $baseClass are the only thing preventing
588             // it from being rewritten to {$baseClass}_Live.  This is brittle and a little clumsy
589             $join = "LEFT JOIN {$baseClass} ON {$baseClass}.\"ID\" = \"{$baseClass}\".\"ID\"";
590             $filter .= " AND {$baseClass}.\"ID\" IS NULL";
591         }
592 
593         $oldStage = Versioned::current_stage();
594         Versioned::reading_stage('Live');
595 
596         // Singleton is necessary and not $this->owner so as not to muck with Translatable's
597         // behaviour.
598         $query = singleton($baseClass)->extendedSQL($filter, null, null, $join);
599 
600         // Since we didn't include double quotes in the join & filter, we need to add them into the
601         // SQL now, after Versioned has done is query rewriting
602         $correctedSQL = str_replace(array("LEFT JOIN {$baseClass}", "{$baseClass}.\"ID\""),
603                         array("LEFT JOIN \"{$baseClass}\"", "\"{$baseClass}\".\"ID\""), $query->sql());
604 
605         $result = $this->owner->buildDataObjectSet(DB::query($correctedSQL));
606 
607         Versioned::reading_stage($oldStage);
608 
609         return $result;
610     }
611 
612     /**
613      * Get the parent of this class.
614      * @return DataObject
615      */
616     public function getParent($filter = '') {
617         if ($p = $this->owner->__get("ParentID")) {
618             $tableClasses = ClassInfo::dataClassesFor($this->owner->class);
619             $baseClass = array_shift($tableClasses);
620             $filter .= ( $filter) ? " AND " : "" . "\"$baseClass\".\"ID\" = $p";
621             return DataObject::get_one($this->owner->class, $filter);
622         }
623     }
624 
625     /**
626      * Return all the parents of this class in a set ordered from the lowest to highest parent.
627      *
628      * @return DataObjectSet
629      */
630     public function getAncestors() {
631         $ancestors = new DataObjectSet();
632         $object = $this->owner;
633 
634         while ($object = $object->getParent()) {
635             $ancestors->push($object);
636         }
637 
638         return $ancestors;
639     }
640 
641     /**
642      * Get the next node in the tree of the type. If there is no instance of the className descended from this node,
643      * then search the parents.
644      *
645      * @todo Write!
646      */
647     public function naturalPrev($className, $afterNode = null) {
648         return null;
649     }
650 
651     /**
652      * Get the next node in the tree of the type. If there is no instance of the className descended from this node,
653      * then search the parents.
654      * @param string $className Class name of the node to find.
655      * @param string|int $root ID/ClassName of the node to limit the search to
656      * @param DataObject afterNode Used for recursive calls to this function
657      * @return DataObject
658      */
659     public function naturalNext($className = null, $root = 0, $afterNode = null) {
660         // If this node is not the node we are searching from, then we can possibly return this
661         // node as a solution
662         if ($afterNode && $afterNode->ID != $this->owner->ID) {
663             if (!$className || ($className && $this->owner->class == $className)) {
664                 return $this->owner;
665             }
666         }
667 
668         $nextNode = null;
669         $baseClass = ClassInfo::baseDataClass($this->owner->class);
670 
671         $children = DataObject::get(ClassInfo::baseDataClass($this->owner->class), "\"$baseClass\".\"ParentID\"={$this->owner->ID}" . ( ( $afterNode ) ? " AND \"Sort\" > " . sprintf('%d', $afterNode->Sort) : "" ), '"Sort" ASC');
672 
673         // Try all the siblings of this node after the given node
674         /* if( $siblings = DataObject::get( ClassInfo::baseDataClass($this->owner->class), "\"ParentID\"={$this->owner->ParentID}" . ( $afterNode ) ? "\"Sort\" > {$afterNode->Sort}" : "" , '\"Sort\" ASC' ) )
675           $searchNodes->merge( $siblings ); */
676 
677         if ($children) {
678             foreach ($children as $node) {
679                 if ($nextNode = $node->naturalNext($className, $node->ID, $this->owner)) {
680                     break;
681                 }
682             }
683 
684             if ($nextNode) {
685                 return $nextNode;
686             }
687         }
688 
689         // if this is not an instance of the root class or has the root id, search the parent
690         if (!(is_numeric($root) && $root == $this->owner->ID || $root == $this->owner->class) && ($parent = $this->owner->Parent())) {
691             return $parent->naturalNext($className, $root, $this->owner);
692         }
693 
694         return null;
695     }
696 
697     function flushCache() {
698         $this->_cache_children = null;
699         $this->_cache_numChildren = null;
700     }
701 
702 }
703 ?>
704 
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0