Today I'd like to discuss a little Drupal 8 snippet to dynamically load an entity from the controller by the current path.
This acts a bit like a Drupal 8 replacement for Drupal 7's menu_get_object().
Use case / example:
We had to provide information on template level about the currently loaded (so to say "main") entity. What we mainly wanted to know was the entity type and bundle to add them as body class.
As we found out Drupal 8 offers the node object in
hook_preprocess_page($variables){...} ?>
as
$variables['node']
?>
but not for other entity types like user oder commerce_product which is a bit inconsistent and broke our before simple node logic for other entity types. But we also needed information for other entity types.
Example implementation in HOOK_preprocess_page
So we decided to extend our hook_preprocess_page by a more generic logic which returns the currently loaded entity information (by controller) or null if the current page is no entity page:
function MYTHEME_preprocess_page(&$variables)
{
// Provide information about the currently loaded page / Entity
// Default (and for non-entity pages):
$variables['entity_type'] = null; // Null on system pages
$variables['bundle'] = null; // Null on system pages
$variables['entity'] = null; // TODO - load from controller!
// Get information about the currently loaded entitiy from the controller (if it is an entity):
$routeParameters = \Drupal::routeMatch()->getParameters();
if ($routeParameters->count() > 0) {
// We use the first parameter as indicator for the entity type to load (node,user,commerce_product, ...)
$entityTypeIndicator = \Drupal::routeMatch()->getParameters()->keys()[0];
$entity = \Drupal::routeMatch()->getParameter($entityTypeIndicator);
if (is_object($entity) && $entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
$variables['entity_type'] = $entity->getEntityTypeId();
$variables['bundle'] = $entity->bundle();
$variables['entity'] = $entity;
}
}
?>
Of course this logic also works in other places / modules etc. outside of HOOK_preprocess_page():
// Get information about the currently loaded entitiy from the controller (if it is an entity):
$routeParameters = \Drupal::routeMatch()->getParameters();
if ($routeParameters->count() > 0) {
// We use the first parameter as dynamic indicator for the entity type to load (node,user,commerce_product, ...)
$entityTypeIndicator = \Drupal::routeMatch()->getParameters()->keys()[0];
$entity = \Drupal::routeMatch()->getParameter($entityTypeIndicator);
if (is_object($entity) && $entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
// $entity is the currently loaded entity here.
}
}
?>
**Updated**
I decided to write a different helper function based on comment #1 which requires less function calls and might have better performance:
/**
* Helper function to return the currently loaded entity from the URL (controller).
* Returns NULL if the currently loaded page is no entity.
*
* @return \Drupal\Core\Entity\EntityInterface
*/
function _get_current_controller_entity()
{
$currentRouteParameters = \Drupal::routeMatch()->getParameters();
foreach ($currentRouteParameters as $param) {
if ($param instanceof \Drupal\Core\Entity\EntityInterface) {
$entity = $param;
return $entity;
}
}
return NULL;
}
?>
Special thanks to @mxh for his idea!
Disclaimer: This is a proof-of-concept implementation and may fail in cases where for example the first parameter of the controller isn't the right parameter for our function. Eventually the allowed entity types / parameters should be whitelisted.
So what do you think? Was this helpful for you? Do you have improvements for my implementation?