views:

58

answers:

3

Hi there,

I'm trying to use hook_menu to create a link to a view which takes an argument. However if I use the path (in $items[view-path/%dest]) that I've already set as the path in the view then the link doesn't appear. I'm guessing there's a path conflict somewhere. Is there a way round this? Or can I use another method to return the view?

I'm using the following code:

/**
* implementation of hook_menu().
*/

function sign_custom_menu() {
  $items['view-path/%dest'] = array(
    'title' => 'Link to view',
    'page callback' => 'sign_custom_hello',
    'page arguments' => array(1), //(corrected typo from 'page arguements')
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
    'menu_name' => 'menu-student-links',
  );

  return $items;
}

function dest_to_arg() {
  // would normally be dynamic to get view with correct argument
  $arg = 73;
  return $arg;
}

Thanks in advance.

Addition

function sign_custom_hello() {
    //return t('Hello!');
}
A: 

I don't have much experience with wildcard URLs in my custom modules much, but I researched the issue in the Pro Drupal Development book. From what I read in the "Wildcards and Parameter Replacement" section on page 77, I think you may want to use $items['view-path/%'] instead. Using %dest apparently makes drupal look for a dest_load function.

Matt V.
Thanks for your response. I have a copy of this book. On page 79 they show a to_arg function which is used to pass wildcard arguments to the menu item. This is the %dest argument I've put in. This references the dest_to_arg() which passes the argument to the path. It is not possible to use % by itself as Drupal doesn't know what the value is to build the link.
Ben
A: 

Items that appear in menus can't be created by a wildcard router item: each menu item corresponds to exactly one path. That is, if you have a router item that is foo/%bar and %bar can have 10 different values, Drupal's menu system isn't going to create 10 new menu items off of one router item definition.

So what you're going to need to do is create a router item for each possible argument ahead of time. Otherwise, you're going to have to look outside Drupal's menu system and think about creating a separate Views block that looks like a menu but is really a Views unordered list of the available options.

To do the former, you need to implement hook_menu_alter() to add your custom router item after everything else, including the wildcard router item you're trying to override. Your custom router item will be more or less the same as the wildcard router item, but with some defaults set that would normally be derived from the wildcard.

For example, if I wanted to create a new router item for user/1/edit which overrides the built-in user/%user_category/edit, I'd implement hook_menu_alter() as so:

function mymodule_menu_alter(&$items) {
  // user_edit and user_edit_access expect a user object
  $account = user_load(array('uid' => 1));

  $items['user/1/edit'] = array(
    'type' => MENU_CALLBACK,
    'page arguments' => array($account),
    'access arguments' => array($account),
  ) + $items['user/%user_category/edit'];
}

In this example, user/%user_category/edit calls user_edit() and user_edit_access() for the page and access callbacks, respectively, and they both attempt to use the wildcard. Since there is no wildcard in your router item, you need override the arguments to say "check user 1".

You'll do this for each and every possible value of the wildcard.

But this isn't enough: notice I used MENU_CALLBACK instead of MENU_NORMAL_ITEM. If you use MENU_NORMAL_ITEM, your router item is going to show up in the Navigation menu, not in your custom menu, even if you set menu_name (I don't know why this is: it should work). But you can get around this by using menu_link_save().

Consider this implementation of hook_init():

function mymodule_init() {
  $router_path = 'user/1/edit';

  // Check to see if the custom router item has been added to menu_links.
  // This is to ensure the menu has already been rebuilt.
  $router_item = db_fetch_object(db_query("SELECT * FROM {menu_links} WHERE router_path = '%s'", $router_path));

  // Only create a new menu item if the router item exists and it
  // hasn't already been created (it's hidden until created).
  if ($router_item && $router_item->hidden) {  
    $item = array(
      'link_title' => 'Edit Administrator',
      'link_path' => $router_path,
      'menu_name' => 'primary-links',
      'router_path' => $router_path,
      'mlid' => $router_item->mlid,
    );

    // Save the menu item.
    menu_link_save($item);
  }
}

In this implementation, it checks to see if the custom router has already been created and hasn't been otherwise modified. If this is true, it creates a menu link in the primary links menu that references your custom router item.

Obviously, since this is in hook_init(), it'll perform the check on every page: this is to ensure it fires after the menu is rebuilt. It shouldn't be much of a performance hit, but it's something to keep in mind.

As you can see, it's a long and drawn out process to do this: if you're not going to go the Views fake-menu route, it might be better to just manually create the links yourself.

Mark Trapp
Hi Mark, Thank you very much for taking the time to answer my query. I managed to find an easier way to solve the problem which uses wildcards (see below). It is documented here: http://drupal.org/node/109153#to_arg. There was also a useful example here: http://drupal.org/node/462684.
Ben
A: 

I managed to answer my problem. Basically I used a different path to the one I had set in the view and then used views_page() as my "page callback". I passed it the arguments for the view, the page ID and it's own additional arguments to make the view work. I was able to use a wildcard in the menu item to pass to views_page() by using the to_arg() function that works with hook_menu() to pass in wildcards. The 'page arguments' pass in the three arguments. The last argument, "1" is a reference to which position in the path the argument appears (starting from 0).

The working code is:

<?php
/**
* implementation of hook_menu().
*/

function sign_custom_menu() {
    $items['view-path/%dest'] = array(
        'title' => 'link to view',
        'page callback' => 'views_page',
        'page arguments' => array('view_name', 'page_1', 1),
        'access callback' => TRUE,
        'type' => MENU_NORMAL_ITEM,
        'menu_name' => 'menu-student-links',
    );
    return $items;
}

//this function is needed from the "%dest" argument in hook_menu above 

function dest_to_arg() {
    // would normally be dynamic to get view with correct argument
    $arg = 73;
    return $arg;
}
?>
Ben