From 3f56402e79ca0d775cad97c6cb2fc5777c03f50f Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Mon, 25 Jul 2016 13:43:54 +0100
Subject: [PATCH] Issue #2767853 by tstoeckler: Provide a route for collections
 of entities

---
 .../Routing/DefaultHtmlRouteProvider.php      | 32 ++++++++++++
 .../Routing/DefaultHtmlRouteProviderTest.php  | 49 +++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
index b1f6abd234c2..1ac54bb277aa 100644
--- a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
+++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
@@ -23,6 +23,7 @@
  * - add-form
  * - edit-form
  * - delete-form
+ * - collection
  *
  * @see \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider.
  *
@@ -95,6 +96,10 @@ public function getRoutes(EntityTypeInterface $entity_type) {
       $collection->add("entity.{$entity_type_id}.delete_form", $delete_route);
     }
 
+    if ($collection_route = $this->getCollectionRoute($entity_type)) {
+      $collection->add("entity.{$entity_type_id}.collection", $collection_route);
+    }
+
     return $collection;
   }
 
@@ -298,6 +303,33 @@ protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
     }
   }
 
+  /**
+   * Gets the collection route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function getCollectionRoute(EntityTypeInterface $entity_type) {
+    // If the entity type does not provide an admin permission, there is no way
+    // to control access, so we cannot provide a route in a sensible way.
+    if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass() && ($admin_permission = $entity_type->getAdminPermission())) {
+      $route = new Route($entity_type->getLinkTemplate('collection'));
+      $route
+        ->addDefaults([
+          '_entity_list' => $entity_type->id(),
+          // @todo Improve this in https://www.drupal.org/node/2767025
+          '_title' => '@label entities',
+          '_title_arguments' => ['@label' => $entity_type->getLabel()],
+        ])
+        ->setRequirement('_permission', $admin_permission);
+
+      return $route;
+    }
+  }
+
   /**
    * Gets the type of the ID key for a given entity type.
    *
diff --git a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
index 71966527602c..a97f29308f4b 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
@@ -252,6 +252,52 @@ public function providerTestGetCanonicalRoute() {
     return $data;
   }
 
+  /**
+   * @covers ::getCanonicalRoute
+   * @dataProvider providerTestGetCollectionRoute
+   */
+  public function testGetCollectionRoute(Route $expected = NULL, EntityTypeInterface $entity_type) {
+    $route = $this->routeProvider->getCollectionRoute($entity_type);
+    $this->assertEquals($expected, $route);
+  }
+
+  public function providerTestGetCollectionRoute() {
+    $data = [];
+
+    $entity_type1 = $this->getEntityType();
+    $entity_type1->hasLinkTemplate('collection')->willReturn(FALSE);
+    $data['no_collection_link_template'] = [NULL, $entity_type1->reveal()];
+
+    $entity_type2 = $this->getEntityType();
+    $entity_type2->hasLinkTemplate('collection')->willReturn(TRUE);
+    $entity_type2->hasListBuilderClass()->willReturn(FALSE);
+    $data['no_list_builder'] = [NULL, $entity_type2->reveal()];
+
+    $entity_type3 = $this->getEntityType($entity_type2);
+    $entity_type3->hasListBuilderClass()->willReturn(TRUE);
+    $entity_type3->getAdminPermission()->willReturn(FALSE);
+    $data['no_admin_permission'] = [NULL, $entity_type3->reveal()];
+
+    $entity_type4 = $this->getEntityType($entity_type3);
+    $entity_type4->getAdminPermission()->willReturn('administer the entity type');
+    $entity_type4->id()->willReturn('the_entity_type_id');
+    $entity_type4->getLabel()->willReturn('The entity type');
+    $entity_type4->getLinkTemplate('collection')->willReturn('/the/collection/link/template');
+    $entity_type4->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE);
+    $route = (new Route('/the/collection/link/template'))
+      ->setDefaults([
+        '_entity_list' => 'the_entity_type_id',
+        '_title' => '@label entities',
+        '_title_arguments' => ['@label' => 'The entity type'],
+      ])
+      ->setRequirements([
+        '_permission' => 'administer the entity type',
+      ]);
+    $data['collection_route'] = [clone $route, $entity_type4->reveal()];
+
+    return $data;
+  }
+
   /**
    * @covers ::getEntityTypeIdKeyType
    */
@@ -313,5 +359,8 @@ public function getAddFormRoute(EntityTypeInterface $entity_type) {
   public function getCanonicalRoute(EntityTypeInterface $entity_type) {
     return parent::getCanonicalRoute($entity_type);
   }
+  public function getCollectionRoute(EntityTypeInterface $entity_type) {
+    return parent::getCollectionRoute($entity_type);
+  }
 
 }
-- 
GitLab