Module Development => Best Practices (or: Help - Drupal has its own mind!)

finke77 - March 26, 2009 - 21:48

Hello together!

I've got a problem: I don't know what Drupal is doing at the moment ...
But maybe I start at the beginnig.

I would like to develop my first module. this module should import old data and create nodes and users with the help of the API.
After the success with the first two content types I started to create users.

Users were also created, but it seems that the function was called several times (see also: http://drupal.org/node/413248 and http://drupal.org/node/414408).

Because I couldn't find any information about this problem in the documentation/websites/Google, I created a new (second) simplified function for testing and locating the error and I added it again to the menu. The same problem!
I called the function with the help of the DEVEL Module => execute PHP Code. Also the same problem, the function was called several times.
Then I changed the function once again and I changed also some debug outputs. With this change I noticed that the first function was called!
I read somewhere that there are some problems with caching. Therefore I cleared the cache: with the DEVEL Module, with the Administration Module, also with truncating the database cache tables.
I also read somewhere, that deactivating and activating modules should help. I tried this also.
Afterwards the behavior changed! .. but not as hoped! Now both functions are called, despite of the first function was not in the menu hook!
Also two users are created. Overmore the function were called several times!
I also commented the first function out, uninstall/install of the module - the first function was still called!
Now I am really lost ... it seems Drupal has its own mind ... I guess somewhere in the the depths of Drupal some old code is stored or cached ...

Therefore I have some questions to you:

  1. How can I solve the problem?
  2. What are the best practices for developing modules? Sometimes I changed some code directly in the included files and tested it afterwards. It seems not to work everytime ...
  3. Do I always have to uninstall and install the module after changes?
  4. Are there any other weird caching options in Drupal?
  5. Do you have other tipps, which I should use or what I did wrong?

Thanks in advance for your help! (I realy need it ;-)

Christian

You've not really described

dman - March 26, 2009 - 22:37

You've not really described your problem in a way that lets us guess what you've done wrong. I looked at your other posts, and they also don't have much explanation on just what happened in the middle.

Since you ask about best practice -
it in NOT recommended that you do "actions" (like database insertions) as a direct result of a menu item.
In big theory terms, HTTP GET requests should be 'idempotent'. GET requests are read-only. Calling that page twice should have the same effect as calling it once.
In Drupal (and in most web scripts), ALL real actions should be on the other side of a form submission.
This is not a rule, and is probably not exactly what your problem is, but it is best-practice, and would have avoided your current problem.

Second.
hook_menu() is called any number of times as Drupal figures out what links are available. DO NOT PERFORM ANY ACTIONS within hook_menu. Do not call functions in the hook_menu. It should return information about the module only. Just name the functions to call.
The menu is heavily cached. If you make changes within your hook_menu, you MUST flush. If you don't, hook_menu may not be called again until the cache decides it needs to rebuild. The old actions will continue to be associated with the menu item (which sounds like your issue). devel.module has a button for that - enable the devel block and click 'rebuild menus'.

Third.
Stop doing it the way you are doing it.
Try a Form API example and put your actions into the form submit phase.

.dan.

.dan.

sounds interesting ...

finke77 - March 26, 2009 - 23:29

Hello dman,

Thanks for your respond!

The link sounds very interesting. As I said, I am very new to Drupal Development, maybe I didn't understand all the concepts behind it. If I have the time tomorrow (it is in the middle of the night here and I am quite tired), I will try it with a form like described in the tutorial.

Just to resume:
I have to create a form function which defines and registers my form in Drupal, a function which validates the form and a function which is called with the submit. The form will be hooked into the menu.

Would it be possible to have something like a dropdown box or radio buttons for selecting the import function?
Would I have something like a switch/case in the function "my_module_my_form_submit($form, &$form_state)" which would call my existing function "mytest()"?

Thanks for your help again, it gives me some new ideas! Nevertheless I attached also some existing code, maybe this gives some more information ...

Best regards,

Christian

The module file:

<?php
/**
* Valid permissions for this module
* @return array An array of valid permissions for the onthisdate module
*/
function cfimport_perm() {
  return array(
'access cfimport');
}
// function cfimport_perm()


function cfimport_menu() {

 
$items = array();

   
 
//Menu for Guestbook
 
$items['cfimport/import_gaestebuch'] = array(
   
'title' => 'Import Gästebuch',
   
'page callback' => 'cfimport_gaestebuch',
   
'access arguments' => array('access cfimport'),
   
'type' => MENU_NORMAL_ITEM,
       
'file' => 'import_gaestebuch.inc',
  );

   
//Menu for New
 
$items['cfimport/import_news'] = array(
   
'title' => 'Import News',
   
'page callback' => 'cfimport_news',
   
'access arguments' => array('access cfimport'),
   
'type' => MENU_NORMAL_ITEM,
       
'file' => 'import_news.inc',
  );

   
//cfimport_user
   
$items['cfimport/import_user'] = array(
   
'title' => 'Import User',
   
'page callback' => 'mytest',
   
'access arguments' => array('access cfimport'),
   
'type' => MENU_NORMAL_ITEM,
       
'file' => 'import_user.inc',
  );
   
  return
$items;

}


function
cfimport_access($op, $node) {
  global
$user;

  switch (
$op) {
    case
'access':
     
// only users with view permission may view.
     
return user_access('access cfimport');
      break;
  }
}
?>

The function in 'import_user.inc' which should be called with "cfimport/import_user":

<?php
function mytest() {
 
$user_data = array(
           
'is_new' => TRUE,
           
'status' => 1,
           
'mail' => 'mail@domain.dom',
           
'init' => 'mail@domain.dom',
           
'name' => 'testuser3',
           
'pass' => 'test',
           
'roles' => array( 2=>TRUE, 4=>TRUE),
           
'language' => 'de',
           
'contact' => 1,
           
'messaging_default' => 'mail',
           
'notifications_send_interval' => 0,
           
'notifications_auto' => 1,
           
'created' => time(),
    );
       
           
// create user
   
$local_user = user_save( '', $user_data );

   
$output ='';
    global
$output;
   
output("<br>================== User ======================<br>");
   
output(nl2br('<pre>'.print_r($local_user,true).'</pre>'));
   
output("========================================<br><br>");
   
output("Created user ".$local_user->name." with UID: ".$local_user->uid." and Email: ".$local_user->mail."<br>");
    return
$output;

}
?>

--
Christian

The code you posted LOOKS OK,

dman - March 26, 2009 - 23:48

The code you posted LOOKS OK, unless there is something left out, like a call directly to mytest in the root scope of import_user.inc.
Not great design, You shouldn't use global in the middle there, but I can't see anything that would cause multiple invocations. May be something internally that is building the page twice. Normally that's harmless, but it's now a problem for you.

> I have to create a form function which defines and registers my form in Drupal
Exactly. A form definition is just an array of elements. See any module for examples.
> a function which validates the form
Optional
> and a function which is called with the submit.
Yes
> The form will be hooked into the menu.
Normally, yes. That's the easiest for one-step operations.

> Would it be possible to have something like a dropdown box or radio buttons for selecting the import function?
Of course. There are many built-in widgets, and easy ways to build them.

> Would I have something like a switch/case in the function
> "my_module_my_form_submit($form, &$form_state)" which would call my existing function "mytest()"?
Generally, exactly that. Values submitted are available inside $form_state['values'] (don't use $_POST). Look at them and do the appropriate thing.

.dan.

.dan.

 
 

Drupal is a registered trademark of Dries Buytaert.