Commit 71bdc7bb authored by catch's avatar catch

Issue #2596095 by tstoeckler, bojanz, dawehner: Provide a route provider for add-form of entities

parent ffa6d37e
......@@ -7,7 +7,9 @@
namespace Drupal\Core\Entity\Controller;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
......@@ -19,6 +21,8 @@
* Provides generic entity title callbacks for use in routing.
*
* It provides:
* - An add title callback for entities without bundles.
* - An add title callback for entities with bundles.
* - A view title callback.
* - An edit title callback.
* - A delete title callback.
......@@ -30,20 +34,40 @@ class EntityController implements ContainerInjectionInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The entity type bundle info.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* Constructs a new EntityController.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info.
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation.
*/
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
$this->entityManager = $entity_manager;
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityRepositoryInterface $entity_repository, TranslationInterface $string_translation) {
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->entityRepository = $entity_repository;
$this->stringTranslation = $string_translation;
}
......@@ -52,11 +76,56 @@ public function __construct(EntityManagerInterface $entity_manager, TranslationI
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$container->get('entity.repository'),
$container->get('string_translation')
);
}
/**
* Provides a generic add title callback for entities without bundles.
*
* @param string $entity_type_id
* The entity type ID.
*
* @return string
* The title for the entity add page.
*/
public function addTitle($entity_type_id) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getLowercaseLabel()]);
}
/**
* Provides a generic add title callback for entities with bundles.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle_parameter
* The name of the route parameter that holds the bundle.
*
* @return string
* The title for the entity add page, if the bundle was found.
*/
public function addBundleTitle(RouteMatchInterface $route_match, $entity_type_id, $bundle_parameter) {
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
// If the entity has bundle entities, the parameter might have been upcasted
// so fetch the raw parameter.
$bundle = $route_match->getRawParameter($bundle_parameter);
if ((count($bundles) > 1) && isset($bundles[$bundle])) {
return $this->t('Add @bundle', ['@bundle' => $bundles[$bundle]['label']]);
}
// If the entity supports bundles generally, but only has a single bundle,
// the bundle is probably something like 'Default' so that it preferable to
// use the entity type label.
else {
return $this->addTitle($entity_type_id);
}
}
/**
* Provides a generic title callback for a single entity.
*
......@@ -65,8 +134,8 @@ public static function create(ContainerInterface $container) {
* @param \Drupal\Core\Entity\EntityInterface $_entity
* (optional) An entity, passed in directly from the request attributes.
*
* @return string
* The title for the entity view page.
* @return string|null
* The title for the entity view page, if an entity was found.
*/
public function title(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
if ($entity = $this->doGetEntity($route_match, $_entity)) {
......@@ -82,8 +151,8 @@ public function title(RouteMatchInterface $route_match, EntityInterface $_entity
* @param \Drupal\Core\Entity\EntityInterface $_entity
* (optional) An entity, passed in directly from the request attributes.
*
* @return string
* The title for the entity edit page.
* @return string|null
* The title for the entity edit page, if an entity was found.
*/
public function editTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
if ($entity = $this->doGetEntity($route_match, $_entity)) {
......@@ -101,7 +170,7 @@ public function editTitle(RouteMatchInterface $route_match, EntityInterface $_en
* set in \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer.
*
* @return string
* The title for the delete entity page.
* The title for the entity delete page.
*/
public function deleteTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
if ($entity = $this->doGetEntity($route_match, $_entity)) {
......@@ -135,8 +204,8 @@ protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface
}
}
}
if ($entity) {
return $this->entityManager->getTranslationFromContext($entity);
if (isset($entity)) {
return $this->entityRepository->getTranslationFromContext($entity);
}
}
......
......@@ -21,6 +21,16 @@
*/
class AdminHtmlRouteProvider extends DefaultHtmlRouteProvider {
/**
* {@inheritdoc}
*/
protected function getAddFormRoute(EntityTypeInterface $entity_type) {
if ($route = parent::getAddFormRoute($entity_type)) {
$route->setOption('_admin_route', TRUE);
return $route;
}
}
/**
* {@inheritdoc}
*/
......
......@@ -7,9 +7,12 @@
namespace Drupal\Core\Entity\Routing;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Entity\Controller\EntityController;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
......@@ -31,20 +34,30 @@
class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHandlerInterface {
/**
* The entity manager.
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* Constructs a new DefaultHtmlRouteProvider.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
*/
public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
}
/**
......@@ -52,7 +65,8 @@ public function __construct(EntityManagerInterface $entity_manager) {
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$container->get('entity.manager')
$container->get('entity_type.manager'),
$container->get('entity_field.manager')
);
}
......@@ -64,14 +78,18 @@ public function getRoutes(EntityTypeInterface $entity_type) {
$entity_type_id = $entity_type->id();
if ($edit_route = $this->getEditFormRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.edit_form", $edit_route);
if ($add_route = $this->getAddFormRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.add_form", $add_route);
}
if ($canonical_route = $this->getCanonicalRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.canonical", $canonical_route);
}
if ($edit_route = $this->getEditFormRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.edit_form", $edit_route);
}
if ($delete_route = $this->getDeleteFormRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.delete_form", $delete_route);
}
......@@ -79,6 +97,78 @@ public function getRoutes(EntityTypeInterface $entity_type) {
return $collection;
}
/**
* Gets the add-form route.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return \Symfony\Component\Routing\Route|null
* The generated route, if available.
*/
protected function getAddFormRoute(EntityTypeInterface $entity_type) {
if ($entity_type->hasLinkTemplate('add-form')) {
$entity_type_id = $entity_type->id();
$route = new Route($entity_type->getLinkTemplate('add-form'));
// Use the add form handler, if available, otherwise default.
$operation = 'default';
if ($entity_type->getFormClass('add')) {
$operation = 'add';
}
$route->setDefaults([
'_entity_form' => "{$entity_type_id}.{$operation}",
'entity_type_id' => $entity_type_id,
]);
// If the entity has bundles, we can provide a bundle-specific title
// and access requirements.
if ($bundle_key = $entity_type->getKey('bundle')) {
$route->setDefault('_title_callback', EntityController::class . '::addBundleTitle');
// If the bundles are entities themselves, we can add parameter
// information to the route options.
if ($bundle_entity_type_id = $entity_type->getBundleEntityType()) {
$bundle_entity_type = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
$route
// The title callback uses the value of the bundle parameter to
// fetch the respective bundle at runtime.
->setDefault('bundle_parameter', $bundle_entity_type_id)
->setRequirement('_entity_create_access', "{$entity_type_id}:{$bundle_entity_type_id}");
// Entity types with serial IDs can specify this in their route
// requirements, improving the matching process.
if ($this->getEntityTypeIdKeyType($bundle_entity_type) === 'integer') {
$route->setRequirement($entity_type_id, '\d+');
}
$bundle_entity_parameter = ['type' => 'entity:' . $bundle_entity_type_id];
if ($bundle_entity_type instanceof ConfigEntityTypeInterface) {
// The add page might be displayed on an admin path. Even then, we
// need to load configuration overrides so that, for example, the
// bundle label gets translated correctly.
// @see \Drupal\Core\ParamConverter\AdminPathConfigEntityConverter
$bundle_entity_parameter['with_config_overrides'] = TRUE;
}
$route->setOption('parameters', [$bundle_entity_type_id => $bundle_entity_parameter]);
}
else {
// If the bundles are not entities, the bundle key is used as the
// route parameter name directly.
$route
->setDefault('bundle_parameter', $bundle_key)
->setRequirement('_entity_create_access', "{$entity_type_id}:{$bundle_key}");
}
}
else {
$route
->setDefault('_title_callback', EntityController::class . '::addTitle')
->setRequirement('_entity_create_access', $entity_type_id);
}
return $route;
}
}
/**
* Gets the canonical route.
*
......@@ -195,7 +285,7 @@ protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) {
return NULL;
}
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type->id());
$field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
return $field_storage_definitions[$entity_type->getKey('id')]->getType();
}
......
......@@ -38,6 +38,7 @@
* "langcode" = "langcode",
* },
* links = {
* "add-form" = "/entity_test_mul/add",
* "canonical" = "/entity_test_mul/manage/{entity_test_mul}",
* "edit-form" = "/entity_test_mul/manage/{entity_test_mul}/edit",
* "delete-form" = "/entity_test/delete/entity_test_mul/{entity_test_mul}",
......
......@@ -42,6 +42,7 @@
* "langcode" = "langcode"
* },
* links = {
* "add-form" = "/entity_test_mul_changed/add",
* "canonical" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}",
* "edit-form" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}/edit",
* "delete-form" = "/entity_test/delete/entity_test_mul_changed/{entity_test_mul_changed}",
......
......@@ -41,6 +41,7 @@
* "default_langcode" = "custom_default_langcode_key",
* },
* links = {
* "add-form" = "/entity_test_mul_langcode_key/add",
* "canonical" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",
* "edit-form" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}/edit",
* "delete-form" = "/entity_test/delete/entity_test_mul_langcode_key/{entity_test_mul_langcode_key}",
......
......@@ -41,6 +41,7 @@
* "langcode" = "langcode",
* },
* links = {
* "add-form" = "/entity_test_mulrev/add",
* "canonical" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
* "delete-form" = "/entity_test/delete/entity_test_mulrev/{entity_test_mulrev}",
* "edit-form" = "/entity_test_mulrev/manage/{entity_test_mulrev}/edit",
......
......@@ -43,6 +43,7 @@
* "langcode" = "langcode",
* },
* links = {
* "add-form" = "/entity_test_mulrev_changed/add",
* "canonical" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}",
* "delete-form" = "/entity_test/delete/entity_test_mulrev_changed/{entity_test_mulrev_changed}",
* "edit-form" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}/edit",
......
......@@ -42,6 +42,7 @@
* "langcode" = "langcode",
* },
* links = {
* "add-form" = "/entity_test_rev/add",
* "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
* "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}",
* "edit-form" = "/entity_test_rev/manage/{entity_test_rev}/edit",
......
......@@ -30,7 +30,6 @@ public function routes() {
array('_entity_form' => "$entity_type_id.default"),
array('_permission' => 'administer entity_test content')
);
$routes["entity.$entity_type_id.admin_form"] = new Route(
"$entity_type_id/structure/{bundle}",
array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment