form_mode_manager-8.x-1.x-dev/src/Routing/EventSubscriber/FormModesSubscriber.php

src/Routing/EventSubscriber/FormModesSubscriber.php
<?php

namespace Drupal\form_mode_manager\Routing\EventSubscriber;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\form_mode_manager\EntityRoutingMapManager;
use Drupal\form_mode_manager\FormModeManagerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route event and add routes using form modes.
 */
class FormModesSubscriber extends RouteSubscriberBase {

  use StringTranslationTrait;

  /**
   * Namespace of transverse entity controller.
   *
   * @var string
   */
  const FORM_MODE_DEFAULT_CONTROLLER = '\Drupal\form_mode_manager\Controller\FormModeManagerEntityController';

  /**
   * The Regex pattern to contextualize process by route path.
   *
   * @var string
   */
  const ROUTE_PATH_CONTEXT_REGEX = '/(^.*?\/edit)|(^.*?\/{block_content})/';

  /**
   * The entity type plugin definition.
   *
   * @var \Drupal\Core\Entity\EntityTypeInterface
   */
  protected $entityDefinition;

  /**
   * The entity display repository.
   *
   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
   */
  protected $entityDisplayRepository;

  /**
   * The current instance of Routing Map plugin for a specific entity type.
   *
   * @var \Drupal\form_mode_manager\EntityRoutingMapBase
   */
  protected $entityRoutingDefinition;

  /**
   * The Routing Map Plugin service.
   *
   * @var \Drupal\form_mode_manager\EntityRoutingMapManager
   */
  protected $entityRoutingMap;

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The entity Form Mode manager service.
   *
   * @var \Drupal\form_mode_manager\FormModeManagerInterface
   */
  protected $formModeManager;

  /**
   * The route collection for adding routes.
   *
   * @var \Symfony\Component\Routing\RouteCollection
   */
  protected $routeCollection;

  /**
   * Constructs a new RouteSubscriber object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
   *   The entity display repository.
   * @param \Drupal\form_mode_manager\FormModeManagerInterface $form_mode_manager
   *   The form mode manager.
   * @param \Drupal\form_mode_manager\EntityRoutingMapManager $plugin_routes_manager
   *   Plugin EntityRoutingMap to retrieve entity form operation routes.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, FormModeManagerInterface $form_mode_manager, EntityRoutingMapManager $plugin_routes_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->entityDisplayRepository = $entity_display_repository;
    $this->formModeManager = $form_mode_manager;
    $this->entityRoutingMap = $plugin_routes_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    $events = parent::getSubscribedEvents();
    // In order to run after ContentTranslationRouteSubscriber.
    $events[RoutingEvents::ALTER] = ['onAlterRoutes', -210];
    return $events;
  }

  /**
   * Add one route collection per entity using form modes.
   *
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    $this->routeCollection = $collection;
    foreach ($this->formModeManager->getAllFormModesDefinitions() as $entity_type_id => $form_modes) {
      $this->entityDefinition = $this->entityTypeManager->getDefinition($entity_type_id);
      $this->entityRoutingDefinition = $this->entityRoutingMap->createInstance($entity_type_id, ['entityTypeId' => $entity_type_id]);
      $this->addFormModesRoutes($form_modes);
    }
  }

  /**
   * Add a collection of route per form mode for current entity.
   *
   * Each entity need to define a collection of routes to be used by Drupal.
   * Each `entity operation` is a form handler present for all,
   * ContentEntityType plugins. Each operation represent a Form to be,
   * displayed by formBuilder service in routes controllers.
   * To respect core logic implemented by Drupal we need to add,
   * a route for each operations (add/edit/add_page/admin_add) dynamically and,
   * a transverse controller compatible to use form modes.
   *
   * Each routes generated by `setFormModeCollection` and,
   * `setAddPageCollection` respect the following naming,
   * `ENTITY_ROUTES_BASIS.FORM_MODE_NAME` eg for Node entity :
   *
   * @code
   *  $routes = [
   *    'add_form' => 'node.add.FORM_MODE_NAME',
   *    'edit_form' => 'entity.node.edit_form.FORM_MODE_NAME',
   *    'add_page' => 'form_mode_manager.node.add_page.FORM_MODE_NAME',
   *    'admin_add' => 'node.add.FORM_MODE_NAME',
   *  ];
   * @endcode
   *
   * @param array $form_modes
   *   All form-modes available for specified entity_type_id.
   */
  protected function addFormModesRoutes(array $form_modes) {
    foreach ($form_modes as $form_mode_infos) {
      $this->setFormModeCollection($form_mode_infos, 'add_form');
      $this->setFormModeCollection($form_mode_infos, 'edit_form');
      $this->setFormModeCollection($form_mode_infos, 'admin_add');
      if (!empty($this->entityDefinition->getKey('bundle'))) {
        $this->setAddPageCollection($form_mode_infos);
      }
      if ($this->entityDefinition->isTranslatable()) {
        $this->setContentTranslationCollection($form_mode_infos);
      }
    }
  }

  /**
   * Create a route for given form mode and operation form handler.
   *
   * This method add a route for given form mode and respect the standard,
   * of parent entity routing naming. All routes added in collection are
   * based on parent route parameters and add only the minimum needed to use
   * form modes. Add the route only if parent entity declares using this
   * operation in the `entityRoutingMap` plugin.
   *
   * @param array $form_mode_infos
   *   A form-mode for specified entity_type_id.
   * @param string $operation_name
   *   The entity operation name.
   */
  public function setFormModeCollection(array $form_mode_infos, $operation_name) {
    if ($this->entityRoutingDefinition->getOperation($operation_name) && $route = $this->getFormModeRoute($form_mode_infos, $operation_name)) {
      $form_mode_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);
      $form_mode_route_name = "{$this->entityRoutingDefinition->getOperation($operation_name)}.$form_mode_name";
      $this->routeCollection->add($form_mode_route_name, $route);
    }
  }

  /**
   * Get the Form Mode Manager route for given operation.
   *
   * @param array $form_mode_infos
   *   The form mode info.
   * @param string $operation_name
   *   The entity operation name.
   *
   * @return \Symfony\Component\Routing\Route|null
   *   The generated route, if available.
   */
  public function getFormModeRoute(array $form_mode_infos, $operation_name) {
    $route_name = $this->entityRoutingDefinition->getOperation($operation_name);
    $form_mode_machine_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);
    $entity_type_id = $this->entityDefinition->id();

    if ($this->formModeManager->hasActiveFormMode($entity_type_id, $form_mode_machine_name) && $entity_edit_route = $this->routeCollection->get($route_name)) {
      return $this->setRoutes($entity_edit_route, $form_mode_infos);
    }

    return NULL;
  }

  /**
   * Generate a routes based on top of given parent routes.
   *
   * @param \Symfony\Component\Routing\Route $parent_route
   *   The route object of entity.
   * @param array $form_mode_infos
   *   The form mode info.
   *
   * @return \Symfony\Component\Routing\Route
   *   Form Mode Manager route to be added on entity collection.
   */
  protected function setRoutes(Route $parent_route, array $form_mode_infos) {
    $route_path = implode('/', [
      $parent_route->getPath(),
      $this->formModeManager->getFormModeMachineName($form_mode_infos['id']),
    ]);
    $route_defaults = NestedArray::mergeDeep($parent_route->getDefaults(), $this->getFormModeRouteDefaults($parent_route, $form_mode_infos));
    $route_options = NestedArray::mergeDeep($parent_route->getOptions(), $this->getFormModeRouteOptions($form_mode_infos));
    $route_requirements = NestedArray::mergeDeep($parent_route->getRequirements(), $this->getFormModeRouteRequirements($form_mode_infos));

    return new Route($route_path, $route_defaults, $route_requirements, $route_options);
  }

  /**
   * Get defaults parameters needed to build Form Mode Manager routes.
   *
   * @param \Symfony\Component\Routing\Route $route
   *   The route object of entity.
   * @param array $form_mode_infos
   *   The form mode info.
   *
   * @return array
   *   Array contain defaults routes parameters.
   */
  protected function getFormModeRouteDefaults(Route $route, array $form_mode_infos) {
    $route_parameters = [
      '_entity_form' => $form_mode_infos['id'],
      '_controller' => static::FORM_MODE_DEFAULT_CONTROLLER . '::entityAdd',
      '_title_callback' => static::FORM_MODE_DEFAULT_CONTROLLER . '::addPageTitle',
    ];

    if (static::isEditRoute($route)) {
      $route_parameters['_title_callback'] = static::FORM_MODE_DEFAULT_CONTROLLER . '::editPageTitle';
      $route_parameters['_controller'] = static::FORM_MODE_DEFAULT_CONTROLLER . '::entityEdit';
    }

    return $route_parameters;
  }

  /**
   * Evaluate if current context is edit.
   *
   * @param \Symfony\Component\Routing\Route $route
   *   The route object of entity.
   *
   * @return bool
   *   True if current route context is edit or False if not.
   */
  public static function isEditRoute(Route $route) {
    return (bool) preg_match_all(self::ROUTE_PATH_CONTEXT_REGEX, $route->getPath(), $matches, PREG_SET_ORDER, 0);
  }

  /**
   * Get options parameters needed to build Form Mode Manager routes.
   *
   * @param array $form_mode_infos
   *   The form mode info.
   *
   * @return array
   *   Array contain options routes parameters.
   */
  protected function getFormModeRouteOptions(array $form_mode_infos) {
    $entity_definition = $this->entityDefinition;
    $entity_type_id = $entity_definition->id();
    $route_definition = [
      '_form_mode_manager_entity_type_id' => $entity_type_id,
      'form_mode_theme' => NULL,
      'parameters' => [
        $entity_type_id => [
          'type' => "entity:$entity_type_id",
        ],
        'form_mode' => $form_mode_infos + ['type' => NULL],
      ],
    ];

    if (!empty($entity_definition->getKey('bundle'))) {
      $route_definition['_form_mode_manager_bundle_entity_type_id'] = $entity_definition->getBundleEntityType();
    }

    return $route_definition;
  }

  /**
   * Get options requirements needed to build Form Mode Manager routes.
   *
   * @param array $form_mode_infos
   *   The form mode info.
   *
   * @return array
   *   Array contain requirements routes parameters.
   */
  protected function getFormModeRouteRequirements(array $form_mode_infos) {
    return [
      '_permission' => "use {$form_mode_infos['id']} form mode",
      '_custom_access' => static::FORM_MODE_DEFAULT_CONTROLLER . '::checkAccess',
    ];
  }

  /**
   * Add one route for `add_page` entity operation per form_mode.
   *
   * This page concern only bundled entities using a route for listing all,
   * bundles using by this entity. Form mode manager add more granularity ,
   * permit to choose what bundle is compatible with a specific form mode.
   * This method adds one listing route by form mode to provide this
   * granularity. Not all entities declare a `add_page` operation,
   * because this isn't needed for all use cases but Form mode manager
   * needs to add this possibility as a standard.
   * All routes key are named with to follow these standard,
   * `form_mode_manager.ENTITY_TYPE_ID.add_page.FORM_MODE_NAME`
   *
   * @param array $form_mode_infos
   *   A form-mode for specified entity_type_id.
   */
  public function setAddPageCollection(array $form_mode_infos) {
    $form_mode_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);
    if ($route = $this->getFormModeListPageRoute($form_mode_infos)) {
      $form_mode_route_name = "form_mode_manager.{$this->entityDefinition->id()}.add_page.$form_mode_name";
      $this->routeCollection->add($form_mode_route_name, $route);
    }
  }

  /**
   * Generate routes to use `add-list` entity operation per form modes.
   *
   * @param array $form_mode_infos
   *   An associative array represent a DisplayForm entity.
   *
   * @return \Symfony\Component\Routing\Route|null
   *   The generated route, if available.
   */
  public function getFormModeListPageRoute(array $form_mode_infos) {
    $form_mode_machine_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);
    $entity_type_id = $this->entityDefinition->id();
    $route = NULL;

    if ($this->formModeManager->hasActiveFormMode($entity_type_id, $form_mode_machine_name)) {
      $route_path = implode('/', [
        $entity_type_id,
        'add-list',
        $this->formModeManager->getFormModeMachineName($form_mode_infos['id']),
      ]);

      $route_defaults = [
        '_controller' => static::FORM_MODE_DEFAULT_CONTROLLER . '::addPage',
        '_title' => $this->getListRouteTitle($form_mode_infos['label']),
        'form_mode_name' => $this->formModeManager->getFormModeMachineName($form_mode_infos['id']),
      ];

      $route_requirements = [
        '_permission' => "use {$form_mode_infos['id']} form mode",
      ];

      $route_options = [
        '_form_mode_manager_entity_type_id' => $entity_type_id,
        '_form_mode_manager_bundle_entity_type_id' => $this->entityDefinition->getBundleEntityType(),
        '_admin_route' => TRUE,
      ];

      $route = new Route("/$route_path", $route_defaults, $route_requirements, $route_options);
    }

    return $route;
  }

  /**
   * Add one route for `content_translation_add` entity operation.
   */
  public function setContentTranslationCollection(array $form_mode_infos) {
    $form_mode_machine_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);
    $entity_type_id = $this->entityDefinition->id();
    if ($entity_type_id === 'node' && $this->formModeManager->hasActiveFormMode($entity_type_id, $form_mode_machine_name)) {
      $form_mode_name = $this->formModeManager->getFormModeMachineName($form_mode_infos['id']);

      // Unlike other paths added by form mode manager, the content_translation
      // dynamic routes cannot end with the form mode or content_translation
      // handles the route instead of form mode manager.
      $route_path = '/' . $entity_type_id . '/{' . $entity_type_id . '}/translations/add/' . $form_mode_name . '/{source}/{target}';
      $route = new Route(
        $route_path,
        [
          '_controller' => '\Drupal\form_mode_manager\Controller\FormModeManagerEntityController::entityTranslationAdd',
          'entity_type_id' => $entity_type_id,
          'source' => NULL,
          'target' => NULL,
        ],
        [
          '_entity_access' => $entity_type_id . '.view',
          '_access_content_translation_manage' => 'create',
        ],
        [
          'parameters' => [
            'source' => [
              'type' => 'language',
            ],
            'target' => [
              'type' => 'language',
            ],
            'node' => [
              'type' => 'entity:' . $entity_type_id,
              'load_latest_revision' => TRUE,
            ],
            'form_mode' => $form_mode_infos + ['type' => NULL],
          ],
          '_admin_route' => TRUE,
          '_form_mode_manager_entity_type_id' => $entity_type_id,
          'form_mode_theme' => NULL,
        ]
      );
      $this->routeCollection->add("form_mode_manager.$entity_type_id.content_translation_add.$form_mode_name", $route);
    }
  }

  /**
   * Format the title for `add_list` routes.
   *
   * @param string $form_mode_label
   *   The label of current DisplayForm entity.
   *
   * @return string
   *   The translated title string of `add_list` operation routes.
   */
  public function getListRouteTitle($form_mode_label) {
    $translatable_markup = $this->t('Add @entity_type as @form_mode_label', [
      '@entity_type' => $this->entityDefinition->getLabel(),
      '@form_mode_label' => $form_mode_label,
    ]);

    return $translatable_markup->render();
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc