depcalc-8.x-1.x-dev/src/EventSubscriber/DependencyCollector/LinkFieldCollector.php
src/EventSubscriber/DependencyCollector/LinkFieldCollector.php
<?php
namespace Drupal\depcalc\EventSubscriber\DependencyCollector;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
use Drupal\depcalc\DependencyCalculatorEvents;
use Drupal\depcalc\DependentEntityWrapper;
use Drupal\depcalc\Event\CalculateEntityDependenciesEvent;
use Drupal\depcalc\FieldExtractor;
/**
* Link Field Collector.
*
* Handles dependency calculation of menu link fields.
*/
class LinkFieldCollector extends BaseDependencyCollector {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* LinkFieldCollector constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* Module handler service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler) {
$this->entityTypeManager = $entity_type_manager;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[DependencyCalculatorEvents::CALCULATE_DEPENDENCIES][] = ['onCalculateDependencies'];
return $events;
}
/**
* Calculates menu link dependencies.
*
* @param \Drupal\depcalc\Event\CalculateEntityDependenciesEvent $event
* The dependency calculation event.
*/
public function onCalculateDependencies(CalculateEntityDependenciesEvent $event) {
// Get the entity.
$entity = $event->getEntity();
// Confirm the entity is an instance of ContentEntityInterface.
if ($entity instanceof ContentEntityInterface) {
$fields = FieldExtractor::getFieldsFromEntity(
$entity,
function (ContentEntityInterface $entity, $field_name, FieldItemListInterface $field) {
return $field->getFieldDefinition()->getType() === 'link';
});
if (!$fields) {
return;
}
// Loop through entity fields.
foreach ($fields as $field) {
// Get event dependencies.
/**
* Loop through field items for relevant dependencies.
*
* @var \Drupal\link\Plugin\Field\FieldType\LinkItem $item
*/
foreach ($field as $item) {
// If values are empty, continue to next menu_link item.
if ($item->isEmpty()) {
continue;
}
// Check if link is external (no deps required).
if ($item->isExternal()) {
continue;
}
// Get values.
$values = $item->getValue();
// Explode the uri first by a colon to retrieve the link type.
[$uri_type, $uri_reference] = explode(':', $values['uri'], 2);
// URI handling switch.
switch ($uri_type) {
// Entity link.
case 'entity':
// Explode entity to get the type and id.
[$entity_type, $entity_id] = explode('/', $uri_reference, 2);
// Load the entity.
$uri_entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
if (!is_null($uri_entity)) {
$entity_wrapper = new DependentEntityWrapper($uri_entity);
// Merge and add dependencies.
$local_dependencies = [];
$this->mergeDependencies($entity_wrapper, $event->getStack(), $this->getCalculator()->calculateDependencies($entity_wrapper, $event->getStack(), $local_dependencies));
$event->addDependency($entity_wrapper);
}
break;
// Internal link.
case 'internal':
// SUPPORT FOR INTERNAL LINKS.
$url = $item->getUrl();
// Only add the dependency for valid routes.
// e.g. /node/123, /admin/config etc.
if ($url && $url->isRouted()) {
$route_params = $url->getRouteParameters();
if (!empty($route_params)) {
try {
$storage = $this->entityTypeManager->getStorage(key($route_params));
}
catch (\Exception $e) {
// If storage is not found for valid route param, it means
// it's not an entity but a plugin or something else.
// In this case we just add dependency
// for module providing the route.
$this->addModuleDependency($event, $url);
continue 2;
}
$uri_entity = !is_null($storage) ? $storage->load(current($route_params)) : NULL;
if (!is_null($uri_entity)) {
$entity_wrapper = new DependentEntityWrapper($uri_entity);
// Merge and add dependencies.
$local_dependencies = [];
$this->mergeDependencies($entity_wrapper, $event->getStack(), $this->getCalculator()->calculateDependencies($entity_wrapper, $event->getStack(), $local_dependencies));
$event->addDependency($entity_wrapper);
}
}
else {
$this->addModuleDependency($event, $url);
}
}
break;
}
}
}
}
}
/**
* Adds module dependency for routes which either are not parameterized.
*
* Or have parameters for non-entities like plugins.
*
* @param \Drupal\depcalc\Event\CalculateEntityDependenciesEvent $event
* The dependency calculation event.
* @param \Drupal\Core\Url $url
* Url object.
*/
protected function addModuleDependency(CalculateEntityDependenciesEvent $event, Url $url): void {
$route_name = $url->getRouteName();
// It's an assumption that all the route names in routing.yml
// will be named like module_name.route_name
// If that module exists and installed, it'll be added as
// a module dependency.
[$module_name] = explode('.', $route_name, 2);
if ($this->moduleHandler->moduleExists($module_name)) {
$event->getWrapper()->addModuleDependencies([$module_name]);
}
}
}
