Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 62 |
EntityFieldRenderer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 10 |
930 | |
0.00% |
0 / 62 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
getCacheContexts | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getEntityTypeId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getEntityManager | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getLanguageManager | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getView | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
query | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
render | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 15 |
|||
buildFields | |
0.00% |
0 / 1 |
182 | |
0.00% |
0 / 31 |
|||
getRenderableFieldIds | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 5 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\views\Entity\Render\EntityFieldRenderer. | |
*/ | |
namespace Drupal\views\Entity\Render; | |
use Drupal\Core\Entity\Entity\EntityViewDisplay; | |
use Drupal\Core\Entity\EntityManagerInterface; | |
use Drupal\Core\Entity\EntityTypeInterface; | |
use Drupal\Core\Language\LanguageManagerInterface; | |
use Drupal\views\Plugin\views\field\Field; | |
use Drupal\views\Plugin\views\query\QueryPluginBase; | |
use Drupal\views\ResultRow; | |
use Drupal\views\ViewExecutable; | |
/** | |
* Renders entity fields. | |
* | |
* This is used to build render arrays for all entity field values of a view | |
* result set sharing the same relationship. An entity translation renderer is | |
* used internally to handle entity language properly. | |
*/ | |
class EntityFieldRenderer extends RendererBase { | |
use EntityTranslationRenderTrait; | |
/** | |
* The relationship being handled. | |
* | |
* @var string | |
*/ | |
protected $relationship; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
*/ | |
protected $entityManager; | |
/** | |
* A list of indexes of rows whose fields have already been rendered. | |
* | |
* @var int[] | |
*/ | |
protected $processedRows = []; | |
/** | |
* Constructs an EntityFieldRenderer object. | |
* | |
* @param \Drupal\views\ViewExecutable $view | |
* The view whose fields are being rendered. | |
* @param string $relationship | |
* The relationship to be handled. | |
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
* The language manager. | |
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | |
* The entity type. | |
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
* The entity manager. | |
*/ | |
public function __construct(ViewExecutable $view, $relationship, LanguageManagerInterface $language_manager, EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager) { | |
parent::__construct($view, $language_manager, $entity_type); | |
$this->relationship = $relationship; | |
$this->entityManager = $entity_manager; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getCacheContexts() { | |
return $this->getEntityTranslationRenderer()->getCacheContexts(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getEntityTypeId() { | |
return $this->entityType->id(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function getEntityManager() { | |
return $this->entityManager; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function getLanguageManager() { | |
return $this->languageManager; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function getView() { | |
return $this->view; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function query(QueryPluginBase $query, $relationship = NULL) { | |
$this->getEntityTranslationRenderer()->query($query, $relationship); | |
} | |
/** | |
* Renders entity field data. | |
* | |
* @param \Drupal\views\ResultRow $row | |
* A single row of the query result. | |
* @param \Drupal\views\Plugin\views\field\Field $field | |
* (optional) A field to be rendered. | |
* | |
* @return array | |
* A renderable array for the entity data contained in the result row. | |
*/ | |
public function render(ResultRow $row, Field $field = NULL) { | |
// The method is called for each field in each result row. In order to | |
// leverage multiple-entity building of formatter output, we build the | |
// render arrays for all fields in all rows on the first call. | |
if (!isset($this->build)) { | |
$this->build = $this->buildFields($this->view->result); | |
} | |
if (isset($field)) { | |
$field_id = $field->options['id']; | |
// Pick the render array for the row / field we are being asked to render, | |
// and remove it from $this->build to free memory as we progress. | |
if (isset($this->build[$row->index][$field_id])) { | |
$build = $this->build[$row->index][$field_id]; | |
unset($this->build[$row->index][$field_id]); | |
} | |
elseif (isset($this->build[$row->index])) { | |
// In the uncommon case where a field gets rendered several times | |
// (typically through direct Views API calls), the pre-computed render | |
// array was removed by the unset() above. We have to manually rebuild | |
// the render array for the row. | |
$build = $this->buildFields([$row])[$row->index][$field_id]; | |
} | |
else { | |
// In case the relationship is optional, there might not be any fields | |
// to render for this row. | |
$build = []; | |
} | |
} | |
else { | |
// Same logic as above, in the case where we are being called for a whole | |
// row. | |
if (isset($this->build[$row->index])) { | |
$build = $this->build[$row->index]; | |
unset($this->build[$row->index]); | |
} | |
else { | |
$build = $this->buildFields([$row])[$row->index]; | |
} | |
} | |
return $build; | |
} | |
/** | |
* Builds the render arrays for all fields of all result rows. | |
* | |
* The output is built using EntityViewDisplay objects to leverage | |
* multiple-entity building and ensure a common code path with regular entity | |
* view. | |
* - Each relationship is handled by a separate EntityFieldRenderer instance, | |
* since it operates on its own set of entities. This also ensures different | |
* entity types are handled separately, as they imply different | |
* relationships. | |
* - Within each relationship, the fields to render are arranged in unique | |
* sets containing each field at most once (an EntityViewDisplay can | |
* only process a field once with given display options, but a View can | |
* contain the same field several times with different display options). | |
* - For each set of fields, entities are processed by bundle, so that | |
* formatters can operate on the proper field definition for the bundle. | |
* | |
* @param \Drupal\views\ResultRow[] $values | |
* An array of all ResultRow objects returned from the query. | |
* | |
* @return array | |
* A renderable array for the fields handled by this renderer. | |
* | |
* @see \Drupal\Core\Entity\Entity\EntityViewDisplay | |
*/ | |
protected function buildFields(array $values) { | |
$build = []; | |
if ($values && ($field_ids = $this->getRenderableFieldIds())) { | |
$entity_type_id = $this->getEntityTypeId(); | |
// Collect the entities for the relationship, fetch the right translation, | |
// and group by bundle. For each result row, the corresponding entity can | |
// be obtained from any of the fields handlers, so we arbitrarily use the | |
// first one. | |
$entities_by_bundles = []; | |
$field = $this->view->field[current($field_ids)]; | |
foreach ($values as $result_row) { | |
if ($entity = $field->getEntity($result_row)) { | |
$entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row); | |
} | |
} | |
// Determine unique sets of fields that can be processed by the same | |
// display. Fields that appear several times in the View open additional | |
// "overflow" displays. | |
$display_sets = []; | |
foreach ($field_ids as $field_id) { | |
$field = $this->view->field[$field_id]; | |
$field_name = $field->definition['field_name']; | |
$index = 0; | |
while (isset($display_sets[$index]['field_names'][$field_name])) { | |
$index++; | |
} | |
$display_sets[$index]['field_names'][$field_name] = $field; | |
$display_sets[$index]['field_ids'][$field_id] = $field; | |
} | |
// For each set of fields, build the output by bundle. | |
foreach ($display_sets as $display_fields) { | |
foreach ($entities_by_bundles as $bundle => $bundle_entities) { | |
// Create the display, and configure the field display options. | |
$display = EntityViewDisplay::create([ | |
'targetEntityType' => $entity_type_id, | |
'bundle' => $bundle, | |
'status' => TRUE, | |
]); | |
foreach ($display_fields['field_ids'] as $field) { | |
$display->setComponent($field->definition['field_name'], [ | |
'type' => $field->options['type'], | |
'settings' => $field->options['settings'], | |
]); | |
} | |
// Let the display build the render array for the entities. | |
$display_build = $display->buildMultiple($bundle_entities); | |
// Collect the field render arrays and index them using our internal | |
// row indexes and field IDs. | |
foreach ($display_build as $row_index => $entity_build) { | |
foreach ($display_fields['field_ids'] as $field_id => $field) { | |
$build[$row_index][$field_id] = !empty($entity_build[$field->definition['field_name']]) ? $entity_build[$field->definition['field_name']] : []; | |
} | |
} | |
} | |
} | |
} | |
return $build; | |
} | |
/** | |
* Returns a list of names of entity fields to be rendered. | |
* | |
* @return string[] | |
* An associative array of views fields. | |
*/ | |
protected function getRenderableFieldIds() { | |
$field_ids = []; | |
foreach ($this->view->field as $field_id => $field) { | |
if ($field instanceof Field && $field->relationship == $this->relationship) { | |
$field_ids[] = $field_id; | |
} | |
} | |
return $field_ids; | |
} | |
} |