block_editor-1.0.x-dev/src/EventSubscriber/BlockEditorRequestSubscriber.php

src/EventSubscriber/BlockEditorRequestSubscriber.php
<?php

namespace Drupal\block_editor\EventSubscriber;

use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Url;
use Drupal\block_editor\Controller\BlockEditorController;
use Drupal\block_editor\Service\EntityManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Exception\RouteNotFoundException;

/**
 * Redirects canonical entity routes to dedicated Block Editor routes.
 */
class BlockEditorRequestSubscriber implements EventSubscriberInterface {

  public function __construct(
    protected EntityManager $entityManager,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected RouteProviderInterface $routeProvider,
  ) {
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      // Priority 31 runs after Drupal's RouterListener (priority 32)
      // but before most other listeners, ensuring routing is resolved.
      KernelEvents::REQUEST => ['onRequest', 31],
    ];
  }

  /**
   * Redirects canonical edit routes to the isolated Block Editor routes.
   */
  public function onRequest(RequestEvent $event): void {
    if (!$event->isMainRequest()) {
      return;
    }

    $request = $event->getRequest();
    $route_name = (string) $request->attributes->get('_route');

    if ($route_name === '') {
      return;
    }

    if ($this->maybeRedirectToBlockEditor($event, $route_name)) {
      return;
    }

    if ($this->maybeRedirectToBlockEditorAddForm($event, $route_name)) {
      return;
    }

    if ($this->isBlockEditorRoute($route_name, $request)) {
      $request->attributes->set('_admin_route', FALSE);
      $request->attributes->set('_block_editor_route', TRUE);
    }
  }

  /**
   * Determines whether the matched route should be treated as Block Editor.
   */
  protected function isBlockEditorRoute(string $route_name, Request $request): bool {
    if ($request->attributes->get('_block_editor_route') === TRUE) {
      return TRUE;
    }

    if (str_starts_with($route_name, 'block_editor.')) {
      return TRUE;
    }

    $controller = (string) $request->attributes->get('_controller');
    if ($controller === BlockEditorController::class . '::addForm') {
      return TRUE;
    }

    if (!str_starts_with($route_name, 'entity.')) {
      return FALSE;
    }

    return $request->attributes->get('_entity_form') === 'block_editor';
  }

  /**
   * Redirects entity edit forms to the Block Editor edit form routes.
   */
  protected function maybeRedirectToBlockEditor(RequestEvent $event, string $route_name): bool {
    if (!str_starts_with($route_name, 'entity.') || !str_ends_with($route_name, '.edit_form')) {
      return FALSE;
    }

    $entity_info = $this->extractContentEntity($event->getRequest());

    if (!$entity_info) {
      return FALSE;
    }

    [$parameter_name, $entity] = $entity_info;

    if (!$this->shouldUseBlockEditor($entity)) {
      return FALSE;
    }

    $entity_type_id = $entity->getEntityTypeId();
    $target_route = 'block_editor.entity.' . $entity_type_id . '.edit_form';

    if ($route_name === $target_route) {
      return FALSE;
    }

    try {
      $this->routeProvider->getRouteByName($target_route);
    }
    catch (RouteNotFoundException $exception) {
      return FALSE;
    }

    $url = Url::fromRoute($target_route, [$parameter_name => $entity->id()]);
    $event->setResponse(new RedirectResponse($url->toString()));

    return TRUE;
  }

  /**
   * Redirects canonical add routes to the Block Editor add routes.
   */
  protected function maybeRedirectToBlockEditorAddForm(RequestEvent $event, string $route_name): bool {
    // Skip if already on a Block Editor route.
    if (str_starts_with($route_name, 'block_editor.')) {
      return FALSE;
    }

    // Check for various add route patterns.
    // Common patterns: entity.{type}.add_form, {type}.add, node.add.
    $is_add_route = str_contains($route_name, '.add_form')
                    || str_contains($route_name, '.add')
                    || $route_name === 'node.add';

    if (!$is_add_route) {
      return FALSE;
    }

    $request = $event->getRequest();
    $bundle_info = $this->extractBundleEntity($request);

    // If bundle entity is not in the request, try to extract and load it
    // from route parameters.
    if (!$bundle_info) {
      $bundle_info = $this->extractAndLoadBundleFromRouteParameters($request, $route_name);
    }

    if (!$bundle_info) {
      return FALSE;
    }

    [$parameter_name, $bundle] = $bundle_info;

    if (!$this->entityManager->isBlockEditorEnabledForEntity($bundle)) {
      return FALSE;
    }

    $content_entity_type_id = $bundle->getEntityType()->getBundleOf();
    if (!$content_entity_type_id) {
      return FALSE;
    }

    $target_route = 'block_editor.entity.' . $content_entity_type_id . '.add_form';
    if ($route_name === $target_route) {
      return FALSE;
    }

    try {
      $this->routeProvider->getRouteByName($target_route);
    }
    catch (RouteNotFoundException $exception) {
      return FALSE;
    }

    $url = Url::fromRoute($target_route, [$parameter_name => $bundle->id()]);
    $event->setResponse(new RedirectResponse($url->toString()));

    return TRUE;
  }

  /**
   * Extracts the content entity (if any) from the route request attributes.
   */
  protected function extractContentEntity(Request $request): ?array {
    foreach ($request->attributes->all() as $key => $value) {
      if ($value instanceof ContentEntityInterface) {
        return [$key, $value];
      }
    }

    return NULL;
  }

  /**
   * Extracts the bundle config entity from the current request.
   */
  protected function extractBundleEntity(Request $request): ?array {
    foreach ($request->attributes->all() as $key => $value) {
      if ($value instanceof ConfigEntityInterface) {
        return [$key, $value];
      }
    }

    return NULL;
  }

  /**
   * Extracts and loads bundle entity from route parameters.
   *
   * This handles cases where the bundle is passed as a string parameter
   * rather than an upcasted entity object.
   */
  protected function extractAndLoadBundleFromRouteParameters(Request $request, string $route_name): ?array {
    // Common bundle parameter names by entity type.
    $bundle_parameter_mapping = [
      'node_type' => 'node',
      'taxonomy_vocabulary' => 'taxonomy_term',
      'block_content_type' => 'block_content',
      'media_type' => 'media',
    ];

    // First, try the standard bundle parameter names.
    foreach ($bundle_parameter_mapping as $bundle_entity_type_id => $content_entity_type_id) {
      $bundle_id = $request->attributes->get($bundle_entity_type_id);

      if ($bundle_id && is_string($bundle_id)) {
        try {
          $bundle_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id);
          $bundle = $bundle_storage->load($bundle_id);

          if ($bundle instanceof ConfigEntityInterface) {
            return [$bundle_entity_type_id, $bundle];
          }
        }
        catch (\Exception $e) {
          // Continue to next mapping.
          continue;
        }
      }
    }

    // Fallback: Try to find any string parameter in the request that
    // might be a bundle ID and check against known bundle entity types.
    foreach ($request->attributes->all() as $param_name => $param_value) {
      // Skip special parameters.
      if (str_starts_with($param_name, '_') || !is_string($param_value)) {
        continue;
      }

      // Try each bundle entity type to see if this parameter is a valid bundle.
      foreach (array_keys($bundle_parameter_mapping) as $bundle_entity_type_id) {
        try {
          $bundle_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id);
          $bundle = $bundle_storage->load($param_value);

          if ($bundle instanceof ConfigEntityInterface) {
            return [$bundle_entity_type_id, $bundle];
          }
        }
        catch (\Exception $e) {
          // This entity type doesn't exist or failed to load, continue.
          continue;
        }
      }
    }

    return NULL;
  }

  /**
   * Determines whether a content entity's bundle has Block Editor enabled.
   */
  protected function shouldUseBlockEditor(ContentEntityInterface $entity): bool {
    return $this->entityManager->isBlockEditorEnabledForContentEntity($entity);
  }

}

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

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