9

Some pages (paths) represent one entity. For example:

  • The pages at these paths represent one node entity, entity_id (nid) is XX:
    • /node/xx
    • /node/xx/delete
    • /node/xx/edit
  • The page at this path represents one search server entity (defined by search api module), entity_id (machine_name) is 'apache_solr':
    • /admin/config/search/search_api/server/apache_solr

How do you load in the basic data about this entity (id and type) when the hook or function you are in doesn't hand it to you?

The standard way to get the current entity is apparently to use menu_get_object($entity_type,$position_of_id_in_url). This, however, assumes that you are coding for just one entity type: you need to state the entity type programmatically, and, even worse, you the human being need to research and hard code in the url position of entity ids that this entity uses.

The closest I can find so far is getting the underlying system uri for the page (e.g. substr(request_uri(),strlen($base_path))), then writing a monsterous switch list based on something like this suggestion, guessing and then hard coding cases for every possible entity you think a site might ever have. But there must be something better than that, right?

While a general solution from core would be best, I'll also accept an answer that depends on common modules like ctools (page manager, contexts, etc), Entity API, Devel, etc.

My immediate need is for edit forms, within hook_field_widget_form(), for a widget that can (with appropriate config and modules) be attached to any fieldable entity type. So, while a general solution would be best, I would also accept a narrow answer that only works within this function or in forms (The id and data that is specific to entity type appears to be buried in $form, but keyed in an entity type specific way, so getting it out of $form seems like a chicken and egg problem).

------------ EDIT ------------------

Seems some people have difficulty understanding the simple general problem. Here's an alternate use case (another seperate but related problem I'm facing). Suppose you have a view that lists some entities. This view sits as a block/pane on pages that can represent one entity, such as a node page or a user profile (or both). You've got a hook set up that reads the views data and looks through the results (foreach $view->result as...). Let's say it's hook_views_pre_render, which means you just have the $view object. You want to do something (say, remove the row or change some field data) if the view results row has the same entity id and entity type as the page does. You've got the logic down to extract this data from the view results, so all you need is to look up the entity_type and entity_id of the page being generated, to make a comparison. That data is in Drupal somewhere. If it wasn't, Drupal couldn't have figured out that it needed to load this view. So how do you get it when it isn't passed to the hook or function you are in?

2
  • As pointed out by paul-m, what is an "entity page"?
    – Letharion
    Commented Apr 6, 2012 at 18:48
  • I mean a http delivered html doc rendered in a browser (or "web page", perhaps "path" is the more Drupal term?) on a Drupal site which [insert correct Drupal terminology here] represents and is on the path of one entity. E.g. /node/xx 'represents' one node entity with id xx, as do /node/xx/delete /node/xx/edit, /admin/config/search/search_api/server/apache_solr represents a search server entity defined by search_api module with machine name 'apache_solr' Commented Apr 6, 2012 at 19:31

6 Answers 6

4

In Drupal Core, you cannot do this. The relevant issue to add such functionality is here.

What core itself does is always passing around two arguments:

function($entity, $entity_type)
2
  • Oh God... one of those Drupal core debates that has been going on for well over a year... Quick question though, for that issue to be relevant, I must already have the $entity obejct. How are you suggesting I get the $entity object? Is there a generic way? Commented Apr 6, 2012 at 17:34
  • 1
    If there's a way to get the $entity object I can use this ugly but workable hack from that debate you linked to drupal.org/node/1042822#comment-5134730 and solve the problem. But first I need the $entity object... Commented Apr 6, 2012 at 17:35
4

The most straightforward way I have found to solve this issue is to implement hook_entity_load().

function MODULE_entity_load($entities, $entity_type) {
  foreach ($entities as $entity) {
    $entity->entity_type = $entity_type;
  }
}

Then the object returned by menu_get_object() will have an entity_type bound to it.

4

I spotted something that works as this in the module Token Filter (D7 version):

  // Attempt to fetch the entity that is being viewed via a backtrace to the
  // field_attach_view($entity_type, $entity) function and parameters §if found.
    $backtrace = debug_backtrace();
    foreach ($backtrace as $caller) {
      if ($caller['function'] == 'field_attach_view') {
        $entity_type = $caller['args'][0];
        $entity = $caller['args'][2];
        // do stuff with entity
        break;
      }  
    } 

Pretty smart - it figures out a function that must have happened before its function is called which will contain $entity_type and $entity ($entity_id would also work instead of $entity), and looks back through the backtrace object until it finds it.

Token Filter is a field processing module, so it will know that its function will always have been called after a call to field_attach_view(). I'm not if there is a good generic alternative to field_attach_view - but the general approach seems to work and a case-specific candidate can always be found for any specific case by dropping some backtrace debug code (formerly broken link fixed) into the point in the code where you need to get the entity, and seeing what's available (then checking logic and testing to make sure it is always present).

3
  • 1
    Nice hack, really useful
    – Clive
    Commented Sep 15, 2012 at 14:35
  • 1
    Upvote just because thats amazing. Hacky as hell, but amazing.
    – tobynew
    Commented Jul 27, 2017 at 16:14
  • 1
    Downvote for the same reason: hacky as hell. Commented Jun 17, 2019 at 7:19
1

The following works for me to get entity and entity type by path:

/**
 * Get the entity and entity-type by path.
 *
 * @param string $path
 *   The path to the entity page.
 * @return array|boolean
 *   Contains the entity and the entity-type or false if entity or type could
 *   not be determinded.
 */
function custom_module_get_entity_and_entity_type_by_path($path) {
  $item = menu_get_item($_GET['internal_path']);
  if (empty($item['access']) || empty($item['page_arguments'][0]) || empty($item['load_functions'])) {
    return FALSE;
  }

  $entity = $item['page_arguments'][0];

  // Get entity-type.
  $load_function = reset($item['load_functions']);
  $entity_types = entity_get_info();
  dpm($entity_types);
  $entity_type = '';
  foreach ($entity_types as $type => $type_definition) {
    if ($load_function == $type_definition['load hook']) {
      // When the load_function from menu_get_item matches "load hook" of the
      // entity type definition, then the entity_type was found.
      $entity_type = $type;
      break;
    }
  }
  if (!$entity_type) {
    return FALSE;
  }

  return array('entity' => $entity, 'entity_type' => $entity_type);
}
0

may this help?

function entity_by_path($path) {
    $router_item = menu_get_item($path);

    $ent_sug_array = array();
    foreach ($router_item['map'] as $key => $value) {
        if(is_object($value) && isset($value->type)) {
           $ent_sug_array[$key] =  $value;
        }
    }

    $ent_sug = false;
    if(count($ent_sug_array) > 1) {
        $lf_sug = false;
        if(isset($router_item['load_functions'])) {
            if(count($router_item['load_functions']) == 1) {
                reset($router_item['load_functions']);
                $lf_sug = key($router_item['load_functions']);
            }
        }
        if($lf_sug !== false) {
            $ent_sug = is_object($router_item['map'][$lg_sug] && isset($router_item['map'][$lg_sug]->type)) ? $router_item['map'][$lg_sug] : $ent_sug;
        }
    } elseif(count($ent_sug_array) == 1) {
        $ent_sug = reset($ent_sug_array);
    }

    if(!empty($ent_sug)) {
        $bundle = $ent_sug->type;
        $ent_type = method_exists($ent_sug, 'entityType') ? $ent_sug->entityType() : false; //eck case
        if(empty($ent_type)) {
            $aclass = get_class($ent_sug);
            if($aclass == 'stdClass') {
                //node, user, core entity case
                if(isset($router_item['load_functions'])) {
                    $lf = reset($router_item['load_functions']);
                    $ei = entity_get_info();
                    foreach ($ei as $key => $value) {
                        if(empty('load hook'))
                            continue;
                        if($lf == $value['load hook']) {
                            $ent_type = $key;
                            break;
                        }
                    }
                }
            }else{
                //custom entity case
                $ent_type = $aclass;
            }
        }

        return array('entity'=>$ent_sug, 'type'=>$ent_type, 'bundle'=>$bundle);
    }

    return false;
}
4
  • 1
    Your function seemed to be complicate and missing documentation and not self-explaining variables don't help much to understand it better. Anyway, after I found a solution using menu_get_item() too, I saw, that it is somehow also contained in your code – see answer with function custom_module_get_entity_and_entity_type_by_path(). I wonder in which cases my solution won't work, as your's do much more stuff to find the entity_type.
    – LarS
    Commented Nov 20, 2017 at 21:39
  • actually i don`t remember already what was wrong with your function, but definetly i tried it out before writing mine one. In my case i needed it to have a bundle of entity, a entity type and a entity key value to deal with print module theming, using custom templates for custom entities like this $variables['theme_hook_suggestions'][] = "print__{$format}__{$ent['type']}__{$ent['bundle']}__{$nid}"; Commented Nov 27, 2017 at 17:04
  • 1
    Nothing was wrong with my function as it was not there, I posted it a week ago. Now I see, you also need the bundle name, maybe that's the reason why it is more complicate.
    – LarS
    Commented Nov 27, 2017 at 17:43
  • :-D Indeed week ago. Anyway i tried other solutions before writing my own. Yes, bundle, entity id, entity type is required in my case to find proper enity page custom template via $suggestion[] Commented Nov 29, 2017 at 18:08
-1

The real answer to this question depends on why you need the data, and at what layer of the API layer cake you're operating.

If you want the entity for a page, you can't do that. A given page could have many entities on it. (Every node, comment, taxonomy... are all entities.)

If you're using hook_field_widget_form(), then you're implementing a field in the Field API, and your answer is in the documentation: Entity type is $element['#entity_type'], and you can't grab the entity ID, since fields implementing the API shouldn't concern themselves with that.

So: Why do you need the entity id?

7
  • Thanks for the tip about $element, and it's a nice suprise to see a docs page that lists the keys of one of the parameters. I'd assumed if the entity type the form is for wasn't in $form or $instance it wouldn't be in an element of an instance on a form. Of course I mean page in the general sense of a http delivered html doc in a browser, not a Drupal node bundle, which would obviously be a node. I need the entity id for a widget that deals with relations from Relation module and needs to figure out which endpoint in a relation is 'this' entity. Or more generally... Commented Apr 6, 2012 at 19:14
  • ...a function that can be called during rendering of a'page' that represents just one entity, which outputs data that references entities, and which needs to compare the data it is processing against data in the entity of the 'page' it is to be printed on. Commented Apr 6, 2012 at 19:18
  • Ah, if by "page" you meant page manager page, which could have many entities in different panels, I had in mind only those pages on the path of one specific entity. These could be loading any number of entities, but would still 'represent' the one entity they are on the path of. Commented Apr 6, 2012 at 19:40
  • I'd wager the Relation module has already done the heavy lifting for you: drupal.org/project/relation
    – paul-m
    Commented Apr 7, 2012 at 17:45
  • I'm using the Relation module, as stated in my first comment, and it gives me all the data I need about the relation. That part is fine, it's not my problem. I just want to look up the entity that this path represents and that this form object, field and widget are for, so I can compare data about the entity with the data I have about the relation. Drupal has already parsed the uri query path and from that identified an entity type and identified an entity id, so I would expect it to be a trivial task to just look this up. It seems however that there is no generic way to do this. Commented Apr 7, 2012 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.