Ejemplo n.º 1
0
 /**
  * Eager-loads additional elements onto a given set of elements.
  *
  * @param BaseElementType    $elementType The root element type
  * @param BaseElementModel[] $elements    The root element models that should be updated with the eager-loaded elements
  * @param string|array       $with        Dot-delimited paths of the elements that should be eager-loaded into the root elements
  *
  * @return void
  */
 public function eagerLoadElements(BaseElementType $elementType, $elements, $with)
 {
     // Bail if there aren't even any elements
     if (!$elements) {
         return;
     }
     // Normalize the paths and find any custom path criterias
     $with = ArrayHelper::stringToArray($with);
     $paths = array();
     $pathCriterias = array();
     foreach ($with as $path) {
         // Using the array syntax?
         // ['foo.bar'] or ['foo.bar', criteria]
         if (is_array($path)) {
             if (!empty($path[1])) {
                 $pathCriterias['__root__.' . $path[0]] = $path[1];
             }
             $paths[] = $path[0];
         } else {
             $paths[] = $path;
         }
     }
     // Load 'em up!
     $elementsByPath = array('__root__' => $elements);
     $elementTypesByPath = array('__root__' => $elementType->getClassHandle());
     foreach ($paths as $path) {
         $pathSegments = explode('.', $path);
         $sourcePath = '__root__';
         foreach ($pathSegments as $segment) {
             $targetPath = $sourcePath . '.' . $segment;
             // Figure out the path mapping wants a custom order
             $useCustomOrder = !empty($pathCriterias[$targetPath]['order']);
             // Make sure we haven't already eager-loaded this target path
             if (!isset($elementsByPath[$targetPath])) {
                 // Guilty until proven innocent
                 $elementsByPath[$targetPath] = $targetElements = $targetElementsById = $targetElementIdsBySourceIds = false;
                 // Get the eager-loading map from the source element type
                 $sourceElementType = $this->getElementType($elementTypesByPath[$sourcePath]);
                 $map = $sourceElementType->getEagerLoadingMap($elementsByPath[$sourcePath], $segment);
                 if ($map && !empty($map['map'])) {
                     // Remember the element type in case there are more segments after this
                     $elementTypesByPath[$targetPath] = $map['elementType'];
                     // Loop through the map to find:
                     // - unique target element IDs
                     // - target element IDs indexed by source element IDs
                     $uniqueTargetElementIds = array();
                     $targetElementIdsBySourceIds = array();
                     foreach ($map['map'] as $mapping) {
                         if (!in_array($mapping['target'], $uniqueTargetElementIds)) {
                             $uniqueTargetElementIds[] = $mapping['target'];
                         }
                         $targetElementIdsBySourceIds[$mapping['source']][] = $mapping['target'];
                     }
                     // Get the target elements
                     $customParams = array_merge(array('order' => null, 'limit' => null), isset($map['criteria']) ? $map['criteria'] : array(), isset($pathCriterias[$targetPath]) ? $pathCriterias[$targetPath] : array());
                     $criteria = $this->getCriteria($map['elementType'], $customParams);
                     $criteria->id = $uniqueTargetElementIds;
                     $targetElements = $this->findElements($criteria);
                     if ($targetElements) {
                         // Success! Store those elements on $elementsByPath FFR
                         $elementsByPath[$targetPath] = $targetElements;
                         // Index the target elements by their IDs if we are using the map-defined order
                         if (!$useCustomOrder) {
                             $targetElementsById = array();
                             foreach ($targetElements as $targetElement) {
                                 $targetElementsById[$targetElement->id] = $targetElement;
                             }
                         }
                     }
                 }
                 // Tell the source elements about their eager-loaded elements (or lack thereof, as the case may be)
                 foreach ($elementsByPath[$sourcePath] as $sourceElement) {
                     $sourceElementId = $sourceElement->id;
                     $targetElementsForSource = array();
                     if (isset($targetElementIdsBySourceIds[$sourceElementId])) {
                         if ($useCustomOrder) {
                             // Assign the elements in the order they were returned from the query
                             foreach ($targetElements as $targetElement) {
                                 if (in_array($targetElement->id, $targetElementIdsBySourceIds[$sourceElementId])) {
                                     $targetElementsForSource[] = $targetElement;
                                 }
                             }
                         } else {
                             // Assign the elements in the order defined by the map
                             foreach ($targetElementIdsBySourceIds[$sourceElementId] as $targetElementId) {
                                 if (isset($targetElementsById[$targetElementId])) {
                                     $targetElementsForSource[] = $targetElementsById[$targetElementId];
                                 }
                             }
                         }
                     }
                     $sourceElement->setEagerLoadedElements($segment, $targetElementsForSource);
                 }
             }
             if (!$elementsByPath[$targetPath]) {
                 // Dead end - stop wasting time on this path
                 break;
             }
             // Update the source path
             $sourcePath = $targetPath;
         }
     }
 }