-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(slb-457): add usage count field on media list view
- Loading branch information
Showing
3 changed files
with
267 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
packages/drupal/custom/src/Plugin/views/field/UsageCount.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
<?php | ||
|
||
namespace Drupal\custom\Plugin\views\field; | ||
|
||
use Drupal\Component\Render\MarkupInterface; | ||
use Drupal\Core\Entity\EntityFieldManagerInterface; | ||
use Drupal\Core\Entity\EntityInterface; | ||
use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
use Drupal\Core\Render\Markup; | ||
use Drupal\Core\Url; | ||
use Drupal\entity_usage\EntityUsageInterface; | ||
use Drupal\media\MediaInterface; | ||
use Drupal\views\Plugin\views\field\FieldPluginBase; | ||
use Drupal\views\ResultRow; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
/** | ||
* Provides Usage count field handler. | ||
* | ||
* @ViewsField("custom_usage_count") | ||
* | ||
* @DCG | ||
* The plugin needs to be assigned to a specific table column through | ||
* hook_views_data() or hook_views_data_alter(). | ||
* Put the following code to custom.views.inc file. | ||
* @code | ||
* function foo_views_data_alter(array &$data): void { | ||
* $data['node']['foo_example']['field'] = [ | ||
* 'title' => t('Example'), | ||
* 'help' => t('Custom example field.'), | ||
* 'id' => 'foo_example', | ||
* ]; | ||
* } | ||
* @endcode | ||
*/ | ||
final class UsageCount extends FieldPluginBase { | ||
|
||
/** | ||
* Constructs a new UsageCount instance. | ||
*/ | ||
public function __construct( | ||
array $configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
private readonly EntityTypeManagerInterface $entityTypeManager, | ||
private readonly EntityFieldManagerInterface $entityFieldManager, | ||
private readonly EntityUsageInterface $entityUsage, | ||
) { | ||
parent::__construct($configuration, $plugin_id, $plugin_definition); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self { | ||
return new self( | ||
$configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
$container->get('entity_type.manager'), | ||
$container->get('entity_field.manager'), | ||
entityUsage: $container->get('entity_usage.usage'), | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function query(): void { | ||
// For non-existent columns (i.e. computed fields) this method must be | ||
// empty. | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function render(ResultRow $values): string|MarkupInterface { | ||
// @todo Modify or replace the rendered value here. | ||
$count = 0; | ||
$media = $values->_entity; | ||
if ($media instanceof MediaInterface) { | ||
$language = $media->language(); | ||
$all_usages = $this->entityUsage->listSources($media); | ||
foreach ($all_usages as $source_type => $ids) { | ||
$type_storage = $this->entityTypeManager->getStorage($source_type); | ||
foreach ($ids as $source_id => $records) { | ||
// We will show a single row per source entity. If the target is not | ||
// referenced on its default revision on the default language, we will | ||
// just show indicate that in a specific column. | ||
$source_entity = $type_storage->load($source_id); | ||
if (!$source_entity) { | ||
// If for some reason this record is broken, just skip it. | ||
continue; | ||
} | ||
|
||
$link = $this->getSourceEntityLink($source_entity); | ||
// If the label is empty it means this usage shouldn't be shown | ||
// on the UI, just skip this row. | ||
if (empty($link)) { | ||
continue; | ||
} | ||
|
||
$count++; | ||
} | ||
} | ||
|
||
$url = Url::fromUserInput("/{$language->getId()}/media/{$media->id()}/edit/usage", []); | ||
} | ||
|
||
return $count ? Markup::create("<a href='{$url->toString()}'>{$count}</a>") : 0; | ||
} | ||
|
||
/** | ||
* Retrieve a link to the source entity. | ||
* | ||
* Note that some entities are special-cased, since they don't have canonical | ||
* template and aren't expected to be re-usable. For example, if the entity | ||
* passed in is a paragraph or a block content, the link we produce will point | ||
* to this entity's parent (host) entity instead. | ||
* | ||
* @param \Drupal\Core\Entity\EntityInterface $source_entity | ||
* The source entity. | ||
* @param string|null $text | ||
* (optional) The link text for the anchor tag as a translated string. | ||
* If NULL, it will use the entity's label. Defaults to NULL. | ||
* | ||
* @return \Drupal\Core\Link|string|false | ||
* A link to the entity, or its non-linked label, in case it was impossible | ||
* to correctly build a link. Will return FALSE if this item should not be | ||
* shown on the UI (for example when dealing with an orphan paragraph). | ||
*/ | ||
protected function getSourceEntityLink(EntityInterface $source_entity, $text = NULL) { | ||
// Note that $paragraph_entity->label() will return a string of type: | ||
// "{parent label} > {parent field}", which is actually OK for us. | ||
$entity_label = $source_entity->access('view label') ? $source_entity->label() : $this->t('- Restricted access -'); | ||
|
||
$rel = NULL; | ||
if ($source_entity->hasLinkTemplate('revision')) { | ||
$rel = 'revision'; | ||
} | ||
elseif ($source_entity->hasLinkTemplate('canonical')) { | ||
$rel = 'canonical'; | ||
} | ||
|
||
// Block content likely used in Layout Builder inline blocks. | ||
if ($source_entity instanceof BlockContentInterface && !$source_entity->isReusable()) { | ||
$rel = NULL; | ||
} | ||
|
||
$link_text = $text ?: $entity_label; | ||
if ($rel) { | ||
// Prevent 404s by exposing the text unlinked if the user has no access | ||
// to view the entity. | ||
return $source_entity->access('view') ? $source_entity->toLink($link_text, $rel) : $link_text; | ||
} | ||
|
||
// Treat paragraph entities in a special manner. Normal paragraph entities | ||
// only exist in the context of their host (parent) entity. For this reason | ||
// we will use the link to the parent's entity label instead. | ||
/** @var \Drupal\paragraphs\ParagraphInterface $source_entity */ | ||
if ($source_entity->getEntityTypeId() == 'paragraph') { | ||
$parent = $source_entity->getParentEntity(); | ||
if ($parent) { | ||
return $this->getSourceEntityLink($parent, $link_text); | ||
} | ||
} | ||
// Treat block_content entities in a special manner. Block content | ||
// relationships are stored as serialized data on the host entity. This | ||
// makes it difficult to query parent data. Instead we look up relationship | ||
// data which may exist in entity_usage tables. This requires site builders | ||
// to set up entity usage on host-entity-type -> block_content manually. | ||
// @todo this could be made more generic to support other entity types with | ||
// difficult to handle parent -> child relationships. | ||
elseif ($source_entity->getEntityTypeId() === 'block_content') { | ||
$sources = $this->entityUsage->listSources($source_entity, FALSE); | ||
$source = reset($sources); | ||
if ($source !== FALSE) { | ||
$parent = $this->entityTypeManager->getStorage($source['source_type'])->load($source['source_id']); | ||
if ($parent) { | ||
return $this->getSourceEntityLink($parent); | ||
} | ||
} | ||
} | ||
|
||
// As a fallback just return a non-linked label. | ||
return $link_text; | ||
} | ||
|
||
} |