TempstoreConverter.php

Same filename in other branches
  1. 4.0.x src/ParamConverter/TempstoreConverter.php

Namespace

Drupal\ctools\ParamConverter

File

src/ParamConverter/TempstoreConverter.php

View source
<?php

namespace Drupal\ctools\ParamConverter;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\ParamConverter\ParamConverterInterface;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\Routing\Route;

/**
 * Parameter converter for pulling entities out of the tempstore.
 *
 * This is particularly useful when building non-wizard forms (like dialogs)
 * that operate on data in the wizard and getting the route access correct.
 *
 * There are four different ways to use this!
 *
 * In the most basic way, you specify the 'tempstore_id' in the defaults (so
 * that the form/controller has access to it as well) and in the parameter type
 * we simply give 'tempstore'. This assumes the entity is the full value
 * returned from the tempstore.
 *
 * @code
 * example.route:
 *   path: foo/{example}
 *   defaults:
 *     tempstore_id: example.foo
 *   options:
 *     parameters:
 *       example:
 *         type: tempstore
 * @endcode
 *
 * If the value returned from the tempstore is an array, and the entity is
 * one of the keys, then we specify that after 'tempstore:', for example:
 *
 * @code
 * example.route:
 *   path: foo/{example}
 *   defaults:
 *     tempstore_id: example.foo
 *   options:
 *     parameters:
 *       example:
 *         # Get the 'foo' key from the array returned by the tempstore.
 *         type: tempstore:foo
 * @endcode
 *
 * You can also specify the 'tempstore_id' under the parameter rather than in
 * the defaults, for example:
 *
 * @code
 * example.route:
 *   path: foo/{example}
 *   options:
 *     parameters:
 *       example:
 *         type: tempstore:foo
 *         tempstore_id: example.foo
 * @endcode
 *
 * Or, if you have two parameters which are represented by two keys on the same
 * array from the tempstore, put the slug which represents the id for the
 * tempstore in the 2nd key. For example:
 *
 * @code
 * example.route:
 *   path: foo/{example}/{other}
 *   defaults:
 *     tempstore_id: example.foo
 *   options:
 *     parameters:
 *       example:
 *         type: tempstore:foo
 *       other:
 *         type: tempstore:{example}:other
 * @endcode
 */
class TempstoreConverter implements ParamConverterInterface {
    
    /**
     * The tempstore factory.
     *
     * @var \Drupal\Core\TempStore\SharedTempStoreFactory
     */
    protected $tempstore;
    
    /**
     * The entity type manager.
     *
     * @var \Drupal\Core\Entity\EntityTypeManagerInterface
     */
    protected $entityTypeManager;
    
    /**
     * Constructs a TempstoreConverter.
     *
     * @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
     */
    public function __construct(SharedTempStoreFactory $tempstore, EntityTypeManagerInterface $entity_type_manager) {
        $this->tempstore = $tempstore;
        $this->entityTypeManager = $entity_type_manager;
    }
    
    /**
     * {@inheritdoc}
     */
    public function convert($value, $definition, $name, array $defaults) {
        $tempstore_id = !empty($definition['tempstore_id']) ? $definition['tempstore_id'] : $defaults['tempstore_id'];
        $machine_name = $this->convertVariable($value, $defaults);
        [
            ,
            $parts,
        ] = explode(':', $definition['type'], 2);
        $parts = explode(':', $parts);
        foreach ($parts as $key => $part) {
            $parts[$key] = $this->convertVariable($part, $defaults);
        }
        $cached_values = $this->tempstore
            ->get($tempstore_id)
            ->get($machine_name);
        // Entity type upcasting is most common, so we just assume that here.
        // @todo see if there's a better way to do this.
        if (!$cached_values && $this->entityTypeManager
            ->hasDefinition($name)) {
            $value = $this->entityTypeManager
                ->getStorage($name)
                ->load($machine_name);
            return $value;
        }
        elseif (!$cached_values) {
            return NULL;
        }
        else {
            $value = NestedArray::getValue($cached_values, $parts, $key_exists);
            return $key_exists ? $value : NULL;
        }
    }
    
    /**
     * A helper function for converting string variable names from the defaults.
     *
     * @param mixed $name
     *   If name is a string in the format of {var} it will parse the defaults
     *   for a 'var' default. If $name isn't a string or isn't a slug, it will
     *   return the raw $name value. If no default is found, it will return NULL.
     * @param array $defaults
     *   The route defaults array.
     *
     * @return mixed
     *   The value of a variable in defaults.
     */
    protected function convertVariable($name, array $defaults) {
        if (is_string($name) && strpos($name, '{') === 0) {
            $length = strlen($name);
            $name = substr($name, 1, $length - 2);
            return $defaults[$name] ?? NULL;
        }
        return $name;
    }
    
    /**
     * {@inheritdoc}
     */
    public function applies($definition, $name, Route $route) {
        if (!empty($definition['type']) && ($definition['type'] == 'tempstore' || strpos($definition['type'], 'tempstore:') === 0)) {
            if (!empty($definition['tempstore_id']) || $route->hasDefault('tempstore_id')) {
                return TRUE;
            }
        }
        return FALSE;
    }

}

Classes

Title Deprecated Summary
TempstoreConverter Parameter converter for pulling entities out of the tempstore.