You are here

protected function SearchApiFieldTrait::getValuesToExtract in Search API 8

Determines and prepares the property values that need to be extracted.

Parameters

\Drupal\views\ResultRow[] $values: The Views result rows from which property values should be extracted.

string|null $datasource_id: The datasource ID of the property to extract (or NULL for datasource- independent properties).

string $property_path: The property path of the property to extract.

string $combined_property_path: The combined property path of the property to extract.

string[] $dependents: The actually required properties (as combined property paths) that depend on this property.

Return value

\Drupal\Core\TypedData\TypedDataInterface[][] The values of the property for each result row, keyed by result row index.

1 call to SearchApiFieldTrait::getValuesToExtract()
SearchApiFieldTrait::preRender in src/Plugin/views/field/SearchApiFieldTrait.php
Runs before any fields are rendered.

File

src/Plugin/views/field/SearchApiFieldTrait.php, line 612

Class

SearchApiFieldTrait
Provides a trait to use for Search API Views field handlers.

Namespace

Drupal\search_api\Plugin\views\field

Code

protected function getValuesToExtract(array $values, $datasource_id, $property_path, $combined_property_path, array $dependents) {

  // Determine the path of the parent property, and the property key to
  // take from it for this property.
  list($parent_path, $name) = Utility::splitPropertyPath($property_path);
  $combined_parent_path = $this
    ->createCombinedPropertyPath($datasource_id, $parent_path);

  // For top-level properties, we need the definition to check whether its
  // a processor-generated property later.
  $property = NULL;
  if (!$parent_path) {
    $datasource_properties = $this
      ->getIndex()
      ->getPropertyDefinitions($datasource_id);
    if (isset($datasource_properties[$name])) {
      $property = $datasource_properties[$name];
    }
  }

  // Now go through all rows and add the property to them, if necessary.
  // We then extract the actual values in a second pass in order to be
  // able to use multi-loading for any encountered entities.

  /** @var \Drupal\Core\TypedData\TypedDataInterface[][] $property_values */
  $property_values = [];
  $entities_to_load = [];
  foreach ($values as $i => $row) {

    // Bail for rows with the wrong datasource for this property, or for
    // which this field doesn't even apply (which will usually be the
    // same, though).
    if ($datasource_id && $datasource_id !== $row->search_api_datasource || !$this
      ->isActiveForRow($row)) {
      continue;
    }

    // Then, make sure we even need this property for the current row. (Will
    // not be the case if all required properties that depend on this property
    // were already set on the row previously.)
    $required = FALSE;
    foreach ($dependents as $dependent) {
      if (!isset($row->{$dependent})) {
        $required = TRUE;
        break;
      }
    }
    if (!$required) {
      continue;
    }

    // Check whether there are parent objects present. Otherwise, nothing we
    // can do here.
    if (empty($row->_relationship_objects[$combined_parent_path])) {
      continue;
    }

    // If the property key is "_object", we only needed to load the parent
    // object(s), so we just copy those to the result row object and we're
    // done.
    if ($name === '_object') {

      // The $row->_object is special, since we also set it in
      // \Drupal\search_api\Plugin\views\query\SearchApiQuery::addResults()
      // (conditionally). To keep it consistent, we make it single-valued
      // here, too.
      if ($combined_property_path !== '_object') {
        $row->{$combined_property_path} = $row->_relationship_objects[$combined_parent_path];
      }
      continue;
    }
    if (empty($row->_relationship_objects[$combined_property_path])) {

      // Check whether this is a processor-generated property and use
      // special code to retrieve it in that case.
      if ($property instanceof ProcessorPropertyInterface) {

        // Determine whether this property is required.
        $is_required = in_array($combined_property_path, $dependents);
        $this
          ->extractProcessorProperty($property, $row, $datasource_id, $property_path, $combined_property_path, $is_required);
        continue;
      }
      foreach ($row->_relationship_objects[$combined_parent_path] as $j => $parent) {

        // Follow references.
        while ($parent instanceof DataReferenceInterface) {
          $parent = $parent
            ->getTarget();
        }

        // At this point we need the parent to be a complex item,
        // otherwise it can't have any children (and thus, our property
        // can't be present).
        if (!$parent instanceof ComplexDataInterface) {
          continue;
        }
        try {

          // Retrieve the actual typed data for the property and add it to
          // our property values.
          $typed_data = $parent
            ->get($name);
          $property_values[$i][$j] = $typed_data;

          // Remember any encountered entity references so we can
          // multi-load them.
          if ($typed_data instanceof DataReferenceInterface) {

            /** @var \Drupal\Core\TypedData\DataReferenceDefinitionInterface $definition */
            $definition = $typed_data
              ->getDataDefinition();
            $definition = $definition
              ->getTargetDefinition();
            if ($definition instanceof EntityDataDefinitionInterface) {
              $entity_type_id = $definition
                ->getEntityTypeId();
              $entity_type = $this
                ->getEntityTypeManager()
                ->getDefinition($entity_type_id);
              if ($entity_type
                ->isStaticallyCacheable()) {
                $entity_id = $typed_data
                  ->getTargetIdentifier();
                if ($entity_id) {
                  $entities_to_load[$entity_type_id][$entity_id] = $entity_id;
                }
              }
            }
          }
        } catch (\InvalidArgumentException $e) {

          // This can easily happen, for example, when requesting a field
          // that only exists on a different bundle. Unfortunately, there
          // is no ComplexDataInterface::hasProperty() method, so we can
          // only catch and ignore the exception.
        }
      }
    }
  }

  // Multi-load all entities we encountered before (to get them into the
  // static cache).
  foreach ($entities_to_load as $entity_type_id => $ids) {
    $this
      ->getEntityTypeManager()
      ->getStorage($entity_type_id)
      ->loadMultiple($ids);
  }
  return $property_values;
}