1

a Drupal newbie here trying to create his first module for Drupal 7.

In my site I've created a new content type (machine name: abstract) from the site's admin's front-end. A user can add a new instance of this content type by using a form I've defined using the FAPI. In this form's submit function I do the following:

function form_add_abstract_submit($form, &$form_state) {

  global $user;

  $node = new stdClass();
  $node->type = 'abstract';
  node_object_prepare($node);

  $node->title = $form_state['values']['abstract_title'];
  $node->language = LANGUAGE_NONE;
  $node->uid = $user->uid;

  // ... fill the rest of the node's fields here [...]

  if($node = node_submit($node)) {
    node_save($node);
    // Not sure about this way of defining the path to the created node
    $path = 'abstract/' . $node->nid;
    $node->path = array('alias' => $path);
    node_save($node);
    drupal_set_message(t('Abstract submitted');
  } else {
    form_set_error('description', t('ERROR'));
  }

  $form_state['redirect'] = 'index.php';
}

As shown above, I define the path to these nodes to be 'abstract/[its-node-id]'. Then, when a user visits this URL I'd like to show two tabs, one allowing the user to see the node's fields and another linking to a form to edit it. I've tried to implement it by adding the following three menu items in my hook_menu function:

$items['abstract/%'] = array(
      'title callback' => 'abstract_page_title',
      'title arguments' => array(1),
      'page callback' => 'abstract_page_view',
      'page arguments' => array(1),
      'access callback' => TRUE,
      'type' => MENU_CALLBACK,
  );

  $items['abstract/%/view'] = array(
      'title' => 'View',
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10,
  );

  $items['abstract/%/edit'] = array(
      'title' => 'Edit',
      'page callback' => 'drupal_get_form',
      'page arguments' => array('my_module_form_abstract_edit', 1),
      'access callback' => TRUE,
      'file' => 'my_module_forms.inc',
      'type' => MENU_LOCAL_TASK,
  );

However, if I visit abstract/[a-node's-id], my abstract_page_view function is not being called and the default view for rendering my content type is displayed. The 'Edit' tab on this view also links to abstract/[the-node's-id]#overlay=node/[the-node's-id]/edit, not to abstract/[the-node's-id]/edit, as I'd like to.

What am I doing wrong? Should I programmatically define my content type instead of doing it from the admin's backend or is my problem somewhere else?

2

If I understand what you are trying to do, you want to customise the node add/edit page and the node view page for your 'abstract' content type.

It seems like you are trying to duplicate what drupal already does for you for no apparent reason. Your code doesn't seem to really be doing anything that drupal does not do for you already (from the code sample you provided). (As a side note, what are you actually trying to achieve?)

If so, I wouldn't be creating fully custom pages. Drupal gives you hooks so you can modify what it gives you by default.

For example, the node add/edit page (node/add/abstract & node/[nid]/edit) can be modified by using hook_form_BASE_FORM_ID_alter() or hook_form_FORM_ID_alter(). While form altering these forms you can add your own custom validate and submit functions if you need to do additional submission.

For path aliases, use the pathauto module, which will allow you to set the path alias for your page to abstract/[nid] - although you would generally be better off doing something like abstract/[title] for the sake of SEO and more user-friendliness.

For the node add page you can add a path alias by going to admin/config/search/path. Then you can make it something like abstract/add instead of node/add/abstract.

Drupal fields should be able to handle pretty much any of the data you want to handle, especially with the help of modules like date, link, email, and many many more.

Now that I know you want to store some node data in an external database then this next part is of interest to you. These node hooks allow you to hook into the node CRUD system:

If you really really do need to have extra data (not so common, you can generally do what you need with fields), for example, data you custom store in a separate database table, you can use the hooks hook_node_view() for showing your custom data on the node view page, hook_node_load() to load your custom data into the node object each time it is loaded, hook_node_delete() to delete your custom data when a node is deleted, hook_node_insert() for inserting your custom data when a node is created, hook_node_update() for updating your custom data when a node is saved.

There are other hooks too, like revision related ones if you are using revisions and search index related ones, for adding data to search index & search results. See them here - http://api.drupal.org/api/search/7/hook_node_

Also, if you are adding data to nodes from your external database that aren't in drupal fields, you might also be interested in the APIs that the Entity module provides. In particular, you can define your data as properties of the entity, so they can be easily handles by modules such as rules and search api (and other modules that utilise the entity api). See http://drupal.org/node/1021466

If you really must do it that way, your hook_menu() also has some issues: The % characters in the menu paths should really be replaced with %node - which will then call node_load and properly validate the nid in the path. See http://drupal.org/node/224170 for more information on menu wildcard loaders.

abstract/% also should not be MENU_CALLBACK, it should be MENU_NORMAL_ITEM.

Basically, you are trying to replicate the node/% node/%/view node/%/edit pages so you should try copying what the node module has in node_menu() for those items, but just change the path. Then you can have your new paths doing what the node module would do at node/% node/%/view and node/%/edit. However you will then have issues with other modules that add node/%/something paths (if you are using any such modules). In this case those extra tabs on the node page would not be present on your pages because their paths would not match.

So unless you really really have to have abstract/%/edit, I would leave the menu alone and just use path auto to have abstract/[nid] or abstract/[title] and leave the edit pages as node/[nid]/edit.

An alternative solution, depending on whether or not it suits your use case better, is to not use the node system at all and create your own custom entity type. So you would have an abstract entity type, like the user, node and taxonomy term ones.

If you aren't really using any of the features that the node system provides this might be a better solution. It is relatively straight forward to do but the downside is a lot of existing contrib modules work only with nodes, and not other entity types. So those additional functionalities you might require you would also have to code yourself. So it depends on whether you have the time to do the additional custom work required or if you are better off utilising nodes, so you get compatibility out of the box.

  • Thank you for your thoughtful answer. What I'm actually trying to achieve is to CRUD the submitted data into another database, independent from Drupal. Maybe using the hook_form_BASE_FORM_ID_alter() hook is what I need here. Also, I'd like to change the default "lightbox" behavior that appears when editing I node (I'd rather have the edit form integrated with the rest of my site and not "pop-up"). Is there any way to achieve that? – tomurillo Feb 4 '13 at 7:50
  • Using a drupal node type and an external database you can let drupal do its thing and save its data to the drupal database. You can make any alterations to the node form with the form alter hooks and you can load and save data from your database to the nodes with the hook_node_***() hooks from the node CRUD system. I have modified the CRUD hooks part of the answer a little, along with a note about the entity property API. Alternatively you could make a fully custom entity type, that only saves to your external database, which may or may not be better for you depending on your use case. – rooby Feb 4 '13 at 10:39
  • As for the pop-up. That is the drupal overlay module, which you can disable just by uninstalling the overlay module. (I'm not a huge fan of the overlay either.) Also, you can restrict access to the overlay via permissions, if you want only some users to get them. The overlay applies to pages considered admin pages. You can also set which pages are considered admin pages using the hooks: hook_admin_paths() and hook_admin_paths_alter() – rooby Feb 4 '13 at 10:39
  • Added a part about the alternative of creating your own entity type instead of using nodes. – rooby Feb 4 '13 at 10:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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