ParamConverterManager.php 5.55 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php

/**
 * @file
 * Contains Drupal\Core\ParamConverter\ParamConverterManager.
 */

namespace Drupal\Core\ParamConverter;

use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
14
use Symfony\Component\Routing\RouteCollection;
15 16 17
use Symfony\Component\HttpFoundation\Request;

/**
18
 * Manages converter services for converting request parameters to full objects.
19
 *
20 21
 * A typical use case for this would be upcasting (converting) a node id to a
 * node entity.
22
 */
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
class ParamConverterManager extends ContainerAware implements RouteEnhancerInterface {

  /**
   * An array of registered converter service ids.
   *
   * @var array
   */
  protected $converterIds = array();

  /**
   * Array of registered converter service ids sorted by their priority.
   *
   * @var array
   */
  protected $sortedConverterIds;
38 39

  /**
40
   * Array of loaded converter services keyed by their ids.
41 42 43
   *
   * @var array
   */
44
  protected $converters = array();
45 46

  /**
47
   * Registers a parameter converter with the manager.
48
   *
49 50 51 52
   * @param string $converter
   *   The parameter converter service id to register.
   * @param int $priority
   *   (optional) The priority of the converter. Defaults to 0.
53
   *
54 55
   * @return \Drupal\Core\ParamConverter\ParamConverterManager
   *   The called object for chaining.
56
   */
57 58 59 60 61 62
  public function addConverter($converter, $priority = 0) {
    if (empty($this->converterIds[$priority])) {
      $this->converterIds[$priority] = array();
    }
    $this->converterIds[$priority][] = $converter;
    unset($this->sortedConverterIds);
63 64 65 66
    return $this;
  }

  /**
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
   * Sorts the converter service ids and flattens them.
   *
   * @return array
   *   The sorted parameter converter service ids.
   */
  public function getConverterIds() {
    if (!isset($this->sortedConverterIds)) {
      krsort($this->converterIds);
      $this->sortedConverterIds = array();
      foreach ($this->converterIds as $resolvers) {
        $this->sortedConverterIds = array_merge($this->sortedConverterIds, $resolvers);
      }
    }
    return $this->sortedConverterIds;
  }

  /**
   * Lazy-loads converter services.
   *
   * @param string $converter
   *   The service id of converter service to load.
   *
   * @return \Drupal\Core\ParamConverter\ParamConverterInterface
   *   The loaded converter service identified by the given service id.
   *
   * @throws \InvalidArgumentException
   *   If the given service id is not a registered converter.
   */
  public function getConverter($converter) {
    if (isset($this->converters[$converter])) {
      return $this->converters[$converter];
    }
    if (!in_array($converter, $this->getConverterIds())) {
      throw new \InvalidArgumentException(sprintf('No converter has been registered for %s', $converter));
    }
    return $this->converters[$converter] = $this->container->get($converter);
  }

  /**
   * Saves a list of applicable converters to each route.
107
   *
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
   * @param \Symfony\Component\Routing\RouteCollection $routes
   *   A collection of routes to apply converters to.
   */
  public function setRouteParameterConverters(RouteCollection $routes) {
    foreach ($routes->all() as $route) {
      if (!$parameters = $route->getOption('parameters')) {
        // Continue with the next route if no parameters have been defined.
        continue;
      }

      // Loop over all defined parameters and look up the right converter.
      foreach ($parameters as $name => &$definition) {
        if (isset($definition['converter'])) {
          // Skip parameters that already have a manually set converter.
          continue;
        }

        foreach ($this->getConverterIds() as $converter) {
          if ($this->getConverter($converter)->applies($definition, $name, $route)) {
            $definition['converter'] = $converter;
            break;
          }
        }
      }

      // Override the parameters array.
      $route->setOption('parameters', $parameters);
    }
  }

  /**
   * Invokes the registered converter for each defined parameter on a route.
140 141
   *
   * @param array $defaults
142
   *   The route defaults array.
143 144 145
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
146 147 148 149
   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
   *   If one of the assigned converters returned NULL because the given
   *   variable could not be converted.
   *
150 151 152 153 154 155
   * @return array
   *   The modified defaults.
   */
  public function enhance(array $defaults, Request $request) {
    $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];

156 157 158
    // Skip this enhancer if there are no parameter definitions.
    if (!$parameters = $route->getOption('parameters')) {
      return $defaults;
159 160
    }

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    // Invoke the registered converter for each parameter.
    foreach ($parameters as $name => $definition) {
      if (!isset($defaults[$name])) {
        // Do not try to convert anything that is already set to NULL.
        continue;
      }

      if (!isset($definition['converter'])) {
        // Continue if no converter has been specified.
        continue;
      }

      // If a converter returns NULL it means that the parameter could not be
      // converted in which case we throw a 404.
      $defaults[$name] = $this->getConverter($definition['converter'])->convert($defaults[$name], $definition, $name, $defaults, $request);
      if (!isset($defaults[$name])) {
177 178 179 180 181 182
        throw new NotFoundHttpException();
      }
    }

    return $defaults;
  }
183

184
}
185