diff --git a/examples.module b/examples.module
index 96cb44f..1fbcd6b 100644
--- a/examples.module
+++ b/examples.module
@@ -51,6 +51,7 @@ function examples_toolbar() {
'file_example' => 'file_example.fileapi',
'hooks_example' => 'hooks_example.description',
'js_example' => 'js_example.info',
+ 'menu_example' => 'examples.menu_example',
'node_type_example' => 'config_node_type_example.description',
'page_example' => 'page_example_description',
'pager_example' => 'pager_example.page',
diff --git a/menu_example/menu_example.routing.yml b/menu_example/menu_example.routing.yml
index 300925f..59f30c2 100644
--- a/menu_example/menu_example.routing.yml
+++ b/menu_example/menu_example.routing.yml
@@ -65,7 +65,8 @@ examples.menu_example.custom_access_page:
requirements:
_menu_example_role: 'TRUE'
-# This showcases how you define only a with no menu link entry.
+# This route has a corresponding menu entry, so the user can click on it in the
+# UI. This gives them a link to a route with no menu entry.
examples.menu_example.route_only:
path: '/examples/menu-example/route-only'
defaults:
@@ -75,7 +76,7 @@ examples.menu_example.route_only:
_permission: 'access content'
# These type of routes can be used to create webservices.
-examples.menu_example.callback:
+examples.menu_example.route_only.callback:
path: '/examples/menu-example/route-only/callback'
defaults:
_controller: '\Drupal\menu_example\Controller\MenuExampleController::routeOnlyCallback'
@@ -104,8 +105,8 @@ examples.menu_example.use_url_arguments:
examples.menu_example.title_callbacks:
path: 'examples/menu-example/title-callbacks'
defaults:
- _title_callback: 'Drupal\menu_example\Controller\MenuExampleController::backTitle'
- _controller: '\Drupal\menu_example\Controller\MenuExampleController::titleCallback'
+ _title_callback: 'Drupal\menu_example\Controller\MenuExampleController::titleCallback'
+ _controller: '\Drupal\menu_example\Controller\MenuExampleController::titleCallbackContent'
requirements:
_permission: 'access content'
diff --git a/menu_example/src/Controller/MenuExampleController.php b/menu_example/src/Controller/MenuExampleController.php
index 55e938b..789bb48 100644
--- a/menu_example/src/Controller/MenuExampleController.php
+++ b/menu_example/src/Controller/MenuExampleController.php
@@ -4,6 +4,7 @@ namespace Drupal\menu_example\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Link;
+use Drupal\Core\Url;
use Drupal\examples\Utility\DescriptionTemplateTrait;
/**
@@ -62,10 +63,11 @@ class MenuExampleController extends ControllerBase {
* @throws \InvalidArgumentException
*/
public function permissioned() {
- $link = Link::createFromRoute($this->t('examples/menu-example/permissioned/controlled'),
- 'examples.menu_example.permissioned_controlled')->toString();
+ $url = Url::fromRoute('examples.menu_example.permissioned_controlled');
return [
- '#markup' => $this->t('A menu item that requires the "access protected menu example" permission is at @link', ['@link' => $link]),
+ '#markup' => $this->t('A menu item that requires the "access protected menu example" permission is at @link', [
+ '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
+ ]),
];
}
@@ -88,10 +90,11 @@ class MenuExampleController extends ControllerBase {
* @see \Drupal\menu_example\Controller\MenuExampleController::customAccessPage()
*/
public function customAccess() {
- $link = Link::createFromRoute($this->t('examples/menu-example/custom-access/page'),
- 'examples.menu_example.custom_access_page')->toString();
+ $url = Url::fromRoute('examples.menu_example.custom_access_page');
return [
- '#markup' => $this->t('A menu item that requires the user to posess a role of "authenticated" is at @link', ['@link' => $link]),
+ '#markup' => $this->t('A menu item that requires the user to posess a role of "authenticated" is at @link', [
+ '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
+ ]),
];
}
@@ -109,15 +112,16 @@ class MenuExampleController extends ControllerBase {
}
/**
- * Only callback without a menu link.
+ * Give the user a link to the route-only page.
*
* @throws \InvalidArgumentException
*/
public function routeOnly() {
- $link = Link::createFromRoute($this->t('examples/menu-example/route-only/callback'),
- 'examples.menu_example.callback')->toString();
+ $url = Url::fromRoute('examples.menu_example.route_only.callback');
return [
- '#markup' => $this->t('A menu entry with no menu link is at @link', ['@link' => $link]),
+ '#markup' => $this->t('A menu entry with no menu link is at @link', [
+ '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
+ ]),
];
}
@@ -131,14 +135,20 @@ class MenuExampleController extends ControllerBase {
}
/**
+ * Uses the path and title to determine the page content.
+ *
+ * This controller is mapped dynamically based on the 'route_callbacks:' key
+ * in the routing YAML file.
+ *
* @param string $path
* Path/URL of menu item.
- *
* @param string $title
* Title of menu item.
*
* @return array
* Controller response.
+ *
+ * @see Drupal\menu_example\Routing\MenuExampleDynamicRoutes
*/
public function tabsPage($path, $title) {
$secondary = substr_count($path, '/') > 2 ? 'secondary ' : '';
@@ -148,12 +158,12 @@ class MenuExampleController extends ControllerBase {
}
/**
- * Demonstrates use of url arguments in for menu item.
+ * Demonstrates use of optional URL arguments in for menu item.
*
* @param string $arg1
- * First argument of url.
+ * First argument of URL.
* @param string $arg2
- * Second argument of url.
+ * Second argument of URL.
*
* @return array
* Controller response.
@@ -161,16 +171,19 @@ class MenuExampleController extends ControllerBase {
* @see https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes
*/
public function urlArgument($arg1, $arg2) {
- // Perpare link for single arguments.
- $link = Link::createFromRoute($this->t('examples/menu-example/use-url-arguments/one'),
- 'examples.menu_example.use_url_arguments', ['arg1' => 'one'])->toString();
+ // Perpare URL for single arguments.
+ $url_single = Url::fromRoute('examples.menu_example.use_url_arguments', ['arg1' => 'one']);
- // Prepare link for multiple arguments.
- $linktwo = Link::createFromRoute($this->t('examples/menu-example/use-url-arguments/one/two'),
- 'examples.menu_example.use_url_arguments', ['arg1' => 'one', 'arg2' => 'two'])->toString();
+ // Prepare URL for multiple arguments.
+ $url_double = Url::fromRoute('examples.menu_example.use_url_arguments', ['arg1' => 'one', 'arg2' => 'two']);
- $markup = $this->t('This page demonstrates using arguments in the url. For example, access it with @link for single argument or @linktwo for two arguments in URL', ['@link' => $link, '@linktwo' => $linktwo]);
+ // Add these argument links to the page content.
+ $markup = $this->t('This page demonstrates using arguments in the url. For example, access it with @link_single for single argument or @link_double for two arguments in URL', [
+ '@link_single' => Link::createFromRoute($url_single->getInternalPath(), $url_single->getRouteName(), $url_single->getRouteParameters())->toString(),
+ '@link_double' => Link::createFromRoute($url_double->getInternalPath(), $url_double->getRouteName(), $url_double->getRouteParameters())->toString(),
+ ]);
+ // Process the arguments if they're provided.
if (!empty($arg1)) {
$markup .= '
' . $this->t('Argument 1 = @arg', ['@arg' => $arg1]) . '
';
}
@@ -178,9 +191,9 @@ class MenuExampleController extends ControllerBase {
$markup .= '' . $this->t('Argument 2 = @arg', ['@arg' => $arg2]) . '
';
}
+ // Finally return the markup.
return [
'#markup' => $markup,
-
];
}
@@ -189,7 +202,7 @@ class MenuExampleController extends ControllerBase {
*
* @see \Drupal\menu_example\Controller\MenuExampleController::backTitle()
*/
- public function titleCallback() {
+ public function titleCallbackContent() {
return [
'#markup' => $this->t('The title of this page is dynamically changed by the title callback for this route defined in menu_example.routing.yml.'),
];
@@ -200,10 +213,11 @@ class MenuExampleController extends ControllerBase {
*
* @see \Drupal\menu_example\Controller\MenuExampleController::titleCallback()
*/
- public function backTitle() {
- $name = \Drupal::currentUser()->getDisplayName();
+ public function titleCallback() {
return [
- '#markup' => $this->t('The new title is your username: @name', ['@name' => $name]),
+ '#markup' => $this->t('The new title is your username: @name', [
+ '@name' => $this->currentUser()->getDisplayName(),
+ ]),
];
}
@@ -216,10 +230,11 @@ class MenuExampleController extends ControllerBase {
* @see https://www.drupal.org/docs/8/api/routing-system/using-parameters-in-routes
*/
public function placeholderArgs() {
- $link = Link::createFromRoute($this->t('examples/menu-example/placeholder-argument/3343/display'),
- 'examples.menu_example.placeholder_argument.display', ['arg' => 3343])->toString();
+ $url = Url::fromRoute('examples.menu_example.placeholder_argument.display', ['arg' => 3343]);
return [
- '#markup' => $this->t('Demonstrate placeholders by visiting @link', ['@link' => $link]),
+ '#markup' => $this->t('Demonstrate placeholders by visiting @link', [
+ '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName(), $url->getRouteParameters())->toString(),
+ ]),
];
}
diff --git a/menu_example/src/Routing/MenuExampleDynamicRoutes.php b/menu_example/src/Routing/MenuExampleDynamicRoutes.php
index ca75524..6ee88a3 100644
--- a/menu_example/src/Routing/MenuExampleDynamicRoutes.php
+++ b/menu_example/src/Routing/MenuExampleDynamicRoutes.php
@@ -5,8 +5,11 @@ namespace Drupal\menu_example\Routing;
use Symfony\Component\Routing\Route;
/**
- * Defines dynamic routes for our tabs menu items.
+ * Defines dynamic routes for our tab menu items.
*
+ * These routes support the links created in menu_example.links.task.yml.
+ *
+ * @see menu_example.links.task.yml
* @see https://www.drupal.org/docs/8/api/routing-system/providing-dynamic-routes
*/
class MenuExampleDynamicRoutes {
diff --git a/menu_example/tests/src/Functional/MenuExampleTest.php b/menu_example/tests/src/Functional/MenuExampleTest.php
index acfc49e..b0fccc6 100644
--- a/menu_example/tests/src/Functional/MenuExampleTest.php
+++ b/menu_example/tests/src/Functional/MenuExampleTest.php
@@ -3,7 +3,6 @@
namespace Drupal\Tests\menu_example\Functional;
use Drupal\Tests\BrowserTestBase;
-use Drupal\user\Entity\Role;
use Drupal\Core\Url;
/**
@@ -22,87 +21,90 @@ class MenuExampleTest extends BrowserTestBase {
* @var array
*/
public static $modules = ['menu_example'];
+
/**
* The installation profile to use with this test.
*
+ * We use 'minimal' because we want the tools menu to be available.
+ *
* @var string
*/
protected $profile = 'minimal';
/**
- * Runs different tests for menu example.
+ * {@inheritdoc}
*/
- public function testMenuExample() {
- $this->assertSession()->pageTextContains('Menu Example');
- $this->clickLink(t('Menu Example'));
- $this->assertSession()->pageTextContains(t('This page is displayed by the simplest (and base) menu example'));
-
- // Create a user with permissions to access protected menu entry.
- $web_user = $this->drupalCreateUser();
- // Use custom overridden drupalLogin function to verify the user is logged
- // in.
- $this->drupalLogin($web_user);
- // Check that our title callback changing /user dynamically is working.
- $this->assertSession()->pageTextContains(t("@name", ['@name' => $web_user->getUsername()]));
-
- // Now start testing other menu entries.
- $this->drupalGet(Url::fromRoute('examples.menu_example'));
- $this->clickLink(t('Custom Access Example'));
- $this->assertSession()->pageTextContains(t('Custom Access Example'));
- $this->drupalGet(Url::fromRoute('examples.menu_example.custom_access_page'));
- $this->assertSession()->statusCodeEquals(200);
- // Logout user to check denial of access.
- $this->drupalLogout();
- $this->drupalGet(Url::fromRoute('examples.menu_example.custom_access_page'));
- $this->assertSession()->statusCodeEquals(403);
+ protected function setUp() {
+ // Always call the parent setUp().
+ parent::setUp();
+ // Add the main menu block, as provided by the Block module.
+ $this->placeBlock('system_menu_block:main');
+ }
- // Check 'Permission Example' menu entry.
- $this->drupalLogin($web_user);
- $this->drupalGet(Url::fromRoute('examples.menu_example.permissioned'));
- $this->assertSession()->pageTextContains(t('Permissioned Example'));
- $this->clickLink('examples/menu-example/permissioned/controlled');
- $this->assertSession()->statusCodeEquals(403);
- // Check access with appropriate permissions.
- $this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['access protected menu example']);
+ /**
+ * Test all the routes.
+ */
+ public function testMenuExampleRoutes() {
+ $assert = $this->assertSession();
+ // Key is route, value is page contents.
+ $routes = [
+ 'examples.menu_example' => 'This page is displayed by the simplest (and base) menu example.',
+ 'examples.menu_example.permissioned' => 'A menu item that requires the "access protected menu example" permission',
+ 'examples.menu_example.custom_access' => 'A menu item that requires the user to posess',
+ 'examples.menu_example.custom_access_page' => 'This menu entry will not be visible and access will result in a 403',
+ 'examples.menu_example.route_only' => 'A menu entry with no menu link is',
+ 'examples.menu_example.use_url_arguments' => 'This page demonstrates using arguments in the url',
+ 'examples.menu_example.title_callbacks' => 'The title of this page is dynamically changed by the title callback',
+ 'examples.menu_example.placeholder_argument' => 'Demonstrate placeholders by visiting',
+ 'example.menu_example.path_override' => 'This menu item was created strictly to allow the RouteSubscriber',
+ 'examples.menu_example.alternate_menu' => 'This will be in the Main menu instead of the default Tools menu',
+ ];
+ $this->drupalLogin($this->createUser());
+ $this->drupalGet(Url::fromRoute(''));
+ // Check that all the links appear in the tools menu.
+ foreach (array_keys($routes) as $route) {
+ $assert->linkByHrefExists(Url::fromRoute($route)->getInternalPath());
+ }
+
+ // Add routes that are not in the tools menu.
+ $routes['examples.menu_example.route_only.callback'] = 'The route entry has no corresponding menu links entry';
+ // Check that all the routes are reachable and contain content.
+ foreach ($routes as $route => $content) {
+ $this->drupalGet(Url::fromRoute($route));
+ $assert->statusCodeEquals(200);
+ $assert->pageTextContains($content);
+ }
+
+ // Check some special-case routes. First is the required argument path.
+ $arg = 2377;
+ $this->drupalGet(Url::fromRoute('examples.menu_example.placeholder_argument.display', ['arg' => $arg]));
+ $assert->statusCodeEquals(200);
+ $assert->pageTextContains($arg);
+
+ // Check the generated route_callbacks tabs.
+ $dynamic_routes = [
+ 'examples.menu_example.tabs_second',
+ 'examples.menu_example.tabs_third',
+ 'examples.menu_example.tabs_fourth',
+ 'examples.menu_example.tabs_default_second',
+ 'examples.menu_example.tabs_default_third',
+ ];
+ $this->drupalGet(Url::fromRoute('examples.menu_example.tabs'));
+ foreach ($dynamic_routes as $route) {
+ $assert->linkByHrefExists(Url::fromRoute($route)->getInternalPath());
+ }
+ foreach ($dynamic_routes as $route) {
+ $this->drupalGet(Url::fromRoute($route));
+ $assert->statusCodeEquals(200);
+ }
+
+ // Check the special permission route.
$this->drupalGet(Url::fromRoute('examples.menu_example.permissioned_controlled'));
- $this->assertSession()->statusCodeEquals(200);
-
- // Check Route only example entry.
- $this->clickLink(t('Route only example'));
- $this->clickLink('examples/menu-example/route-only/callback');
- $this->assertSession()->pageTextContains('The route entry has no corresponding menu links entry');
-
- // Check links on Tabs landing page.
- $this->clickLink(t('Tabs'));
- $this->assertSession()->pageTextContains(t('This is the tab "Default primary tab" in the "basic tabs" example'));
- $this->assertSession()->linkByHrefExists('/examples/menu-example/tabs');
- // Check presence of default tabs.
- $this->assertSession()->linkByHrefExists('/examples/menu-example/tabs/default/second');
- $this->assertSession()->linkByHrefExists('/examples/menu-example/tabs/default/third');
-
- // Check second tab.
- $this->drupalGet('examples/menu-example/tabs/second');
- $this->assertSession()->pageTextContains(t('This is the tab "Second" in the "basic tabs" example'));
-
- // Check third tab.
- $this->clickLink(t('Third'));
- $this->assertSession()->pageTextContains(t('This is the tab "Third" in the "basic tabs" example'));
-
- // Now verify 'URL arguments' menu entry to be working with two arguments.
- $this->clickLink(t('URL Arguments'));
- $this->drupalGet(Url::fromRoute('examples.menu_example.use_url_arguments', ['arg1' => 'one', 'arg2' => 'two']));
- $this->assertSession()->pageTextContains(t('Argument 1 = one'));
- $this->assertSession()->pageTextContains(t('Argument 2 = two'));
-
- // Check 'placeholder arguments' menu entry.
- $this->clickLink(t('Placeholder Arguments'));
- $this->clickLink(t('examples/menu-example/placeholder-argument/3343/display'));
- $this->assertSession()->pageTextContains('3343');
-
- // Check altered route example.
- $this->drupalGet('examples/menu-example/menu-altered-path');
- $this->assertSession()->pageTextContains('Menu item altered by RouteSubscriber::alterRoutes');
-
+ $assert->statusCodeEquals(403);
+ $this->drupalLogin($this->createUser(['access protected menu example']));
+ $this->drupalGet(Url::fromRoute('examples.menu_example.permissioned_controlled'));
+ $assert->statusCodeEquals(200);
+ $assert->pageTextContains('This menu entry will not show and the page will not be accessible');
}
}