Hello,
I found an issue, where the loader function I defined in hook_menu() is called multiple times with no reason.
My loader function is called 4 times.
3 times before the callback, then the form is called, and then the loader function is called once again.
Here's my hook_menu :
function mymodule_menu() {
$items['mymodule/cycle/%mymodule_cycle/edit'] = array(
'title' => 'Edit cycle',
'description' => 'Edit cycle.',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_cycle_form', 2),
'access arguments' => array('administer cycles'),
'file' => 'mymodule.admin.inc',
);
return $items;
}
Is it a bug or a misuse?
Thank you!
Comments
Comment #1
damien tournoud commentedThat looks slightly weird, and should probably be looked at in details.
But anyway, you are responsible to implement static in your menu loader function. The menu system itself doesn't do any caching of the loaded objects.
Comment #2
ludo.rOk I can implement static, but I can't figure out why this loader is called multiple times.
There's someone else having the same issue there : http://drupal.org/node/1425908
Comment #3
bmarcotte commentedHi all,
just a note to say I'm seeing the same thing as dolo and gregfrith. 4 calls to the loader, 1 call to the calback. The callback occurs only after the first loader call. I only noticed it because I placed an echo to trace my code.
In my case, I was able to move my "loader code" into the callback to bypass this quirk. My "loader code" simply returns the argument: i.e. return $arg1; and then the system passes $arg1 onto the callback.
-Bob
Comment #4
Alexander Matveev commentedSubscribing.
Comment #5
andyf commentedA question was asked about this on Drupal Answers. I was curious and tried it out on admin/structure/types/manage/%node_type. I grepped for node_type_load() and nothing seemed to be calling it directly, so I put a ddebug_backtrace() in the load function. I got 13 traces when visiting that path! 10 of these came from menu_local_tasks() which loops through the local tasks and calls _menu_translate() which calls _menu_load_objects() for each router item. The other two extra calls came via menu_set_active_trail() which also called _menu_translate(). So I guess it depends on your menu structure how many times a loader might be called. As far as I can tell the object's loaded only to check whether or not it can be (access is denied if it can't). I guess this just reinforces Damien's comment about implementing static caching in your loader.
If someone who knows can say this is by design (and confirm what I've said) I'd be happy to file a patch against hook_menu()'s documentation to mention that a loader function might be called multiple times and it's important to cache.
Comment #6
twardnw commentedI was seeing similar effects from menu_get_object. I had an access callback on a hook_menu, inside that access callback I needed to check a node for referenced user IDs, but due to this issue with menu_get_object my access callback was never returning TRUE or FALSE. I stuck a watchdog on either side of it, and only the 'before' one was triggered.
Comment #7
cameron tod commentedI just had a quick end-of-day debug on this, as it was effecting some stuff I am writing here.
Menu loader functions are called from
_menu_load_objects, which is called (in my case) by _menu_translate 3 times, and by _menu_link_translate once. I think an easy band-aid patch is to cache the $return value of the loader function in _menu_load_objects - here's a link to the line in the 8.x branch.I'll try to have a deeper look a bit later.
Comment #8
bmarcotte commentedYes this issue is still out here....2 yrs later I re-found this problem
To help others:
I've been creating a custom entity and my hook_load looks like this:
the static variable $r saves the entity between all the excess calls from menu and/or anywhere else.
Fortunately for the general case, node_load loads through the entity.inc file. The controller there checks for a cached value to avoid a full db reload. It seems, the more menu items you include on a page, the more code spinning that goes on.