As suggested by Berdir you can look at Devel module and at how it's implementing that. The following code was "extracted" from Devel
1) Create the routes
Create the file mymodule.routing.yml in and inside define a route callback (which is used for create the dynamic routes)
route_callbacks:
- '\Drupal\mymodule\Routing\MyModuleRoutes::routes'
Create the class MyModuleRoutes for generate yours dynamic routes in src/Routing
<?php
namespace Drupal\mymodule\Routing;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class MyModuleRoutes implements ContainerInjectionInterface {
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager')
);
}
public function routes() {
$collection = new RouteCollection();
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->hasLinkTemplate('canonical')) {
$route = new Route("/mymodule/$entity_type_id/{{$entity_type_id}}");
$route
->addDefaults([
'_controller' => '\Drupal\mymodule\Controller\MyModuleController::doStuff',
'_title' => 'My module route title',
])
->addRequirements([
'_permission' => 'access mymodule permission',
])
->setOption('_mymodule_entity_type_id', $entity_type_id)
->setOption('parameters', [
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
]);
$collection->add("entity.$entity_type_id.mymodule", $route);
}
}
return $collection;
}
}
2) Create the dynamic local tasks
Create the file mymodule.links.task.yml and inside define a deriver
mymodule.tasks:
class: \Drupal\Core\Menu\LocalTaskDefault
deriver: \Drupal\mymodule\Plugin\Derivative\MyModuleLocalTasks
Create the class MyModuleLocalTasks for generate yours dynamic routes in src/Plugin/Derivative
<?php
namespace Drupal\mymodule\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyModuleLocalTasks extends DeriverBase implements ContainerDeriverInterface {
protected $entityTypeManager;
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity_type.manager')
);
}
public function getDerivativeDefinitions($base_plugin_definition) {
$this->derivatives = array();
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->hasLinkTemplate('canonical')) {
$this->derivatives["$entity_type_id.mymodule_tab"] = [
'route_name' => "entity.$entity_type_id.mymodule",
'title' => t('Mymodule title'),
'base_route' => "entity.$entity_type_id.canonical",
'weight' => 100,
] + $base_plugin_definition;
}
}
return $this->derivatives;
}
}
3) Create the controller
Create the class MyModuleController in src/Controller
namespace Drupal\mymodule\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
class MyModuleController extends ControllerBase {
public function doStuff(RouteMatchInterface $route_match) {
$output = [];
$parameter_name = $route_match->getRouteObject()->getOption('_mymodule_entity_type_id');
$entity = $route_match->getParameter($parameter_name);
if ($entity && $entity instanceof EntityInterface) {
$output = ['#markup' => $entity->label()];
}
return $output;
}
}