Hi
I found the in the attaching a new theme to the Drupal we have to replace the same code at the node.tpl.php and so on , those are the anti patterns in the Drupal cause code in node.tpl.php can be consider as a dead code so the "Old Baggage" will occur. So what are the other types of anti patterns we can found i n Drupal
Thanks
Hesh

Comments

dinukauom’s picture

yes yes your correct

heshanlk’s picture

Drupal Design Patterns / Anti Patterns

Drupal is a free and open source modular framework and content management system (CMS) written in the programming language PHP. There are many constructs in Drupal that fit the description of an "object". Some of the more prominent Drupal components that could be considered objects are modules, themes, nodes, and users. Nodes, Modules and themes are object-like as well, filling the "controller" role in many ways. Each module is a source file, but also bundles together related functions and follows a pattern of defining Drupal hooks.

Design Patterns in Drupal
Singleton
As I mentioned above we can consider modules and themes as an Objects. These objects do not encapsulate data, while we can identify one module from another by contained functions, so it should be thought as a class with a singleton instance.
hook functions :

hook_actions_delete($aid)// Execute code after an action is deleted.
hook_block($op = 'list', $delta = 0, $edit = array())//Declare a block or set of blocks.
hook_comment($a1, $op)// This hook allows modules to extend the comments system.
hook_cron()//Perform periodic actions.
hook_hook_info()//Expose a list of triggers (events) that your module is allowing users to assign actions to.
hook_menu_alter(&$callbacks)// Alter the data being saved to the {menu_router} table after hook_menu is invoked.
hook_menu_link_alter(&$item, $menu)// Alter the data being saved to the {menu_links} table by menu_link_save().
hook_db_rewrite_sql($query, $primary_table, $primary_field, $args)// Rewrite database queries, usually for access control.
hook_elements() // Allows modules to declare their own Forms API element types and specify their default values.
hook_exit($destination = NULL)// Perform cleanup tasks.
hook_footer($main = 0)// Insert closing HTML.


Decorator
The core of the view node done by node_view () which is in node.module. If we want to display a content from a module we have to call node_view() from that module with validated parameters. The actual rendering, though, will depend on which type of node is passed to the function; this is directly analogous to having the class of an object determine its behavior when a message is sent to it.
More interesting is the use of hook_nodeapi(), which allows arbitrary modules to extend the behavior of all nodes. This feature allows for a wide variety of behaviors to be added to nodes without the need for subclassing. For instance, a basic story node has only a few pieces of associated data like: title, author, body, teaser etc.
Let say, its need files to be uploaded and attached to a node, so one could design a new node type that had the story node's features plus the ability to attach files. Drupal's upload module satisfies this need in a much more modular fashion by using nodeAPI to grant every node that requests it the ability to have attached files.

function hook_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'presave':
      if ($node->nid && $node->moderate) {
        // Reset votes when node is updated:
        $node->score = 0;
        $node->users = '';
        $node->votes = 0;
      }
      break;
    case 'insert':
    case 'update':
      if ($node->moderate && user_access('access submission queue')) {
        drupal_set_message(t('The post is queued for approval'));
      }
      elseif ($node->moderate) {
        drupal_set_message(t('The post is queued for approval. The editors will decide whether it should be published.'));
      }
      break;
    case 'view':
      $node->content['my_additional_field'] = array(
        '#value' => theme('mymodule_my_additional_field', $additional_field),
        '#weight' => 10,
      );
      break;
  }
}

Observer
When a modification is made to a vocabulary in Drupal's taxonomy system, the taxonomy hook is called in all modules that implement it. By implementing the hook, they have registered as observers of the vocabulary object; any changes to it can then be acted on as is appropriate.

function taxonomy_node_save($node, $terms) {

  taxonomy_node_delete_revision($node);

  // Free tagging vocabularies do not send their tids in the form,
  // so we'll detect them here and process them independently.
  if (isset($terms['tags'])) {
    $typed_input = $terms['tags'];
    unset($terms['tags']);

    foreach ($typed_input as $vid => $vid_value) {
      $typed_terms = drupal_explode_tags($vid_value);

      $inserted = array();
      foreach ($typed_terms as $typed_term) {
        // See if the term exists in the chosen vocabulary
        // and return the tid; otherwise, add a new record.
        $possibilities = taxonomy_get_term_by_name($typed_term);
        $typed_term_tid = NULL; // tid match, if any.
        foreach ($possibilities as $possibility) {
          if ($possibility->vid == $vid) {
            $typed_term_tid = $possibility->tid;
          }
        }

        if (!$typed_term_tid) {
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
        }

        // Defend against duplicate, differently cased tags
        if (!isset($inserted[$typed_term_tid])) {
          db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $typed_term_tid);
          $inserted[$typed_term_tid] = TRUE;
        }
      }
    }
  }

  if (is_array($terms)) {
    foreach ($terms as $term) {
      if (is_array($term)) {
        foreach ($term as $tid) {
          if ($tid) {
            db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $tid);
          }
        }
      }
      else if (is_object($term)) {
        db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term->tid);
      }
      else if ($term) {
        db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term);
      }
    }
  }
}

Bridge
The Drupal database abstraction layer is implemented in a fashion similar to the Bridge design pattern. Modules need to be written in a way that is independent of the database system being used, and the abstraction layer provides for this. New database layers can be written that conform to the API defined by the bridge, adding support for additional database systems without the need to modify module code.

function db_create_table(&$ret, $name, $table) {
  $statements = db_create_table_sql($name, $table);
  foreach ($statements as $statement) {
    $ret[] = update_sql($statement);
  }
}

Chain of Responsibility
Drupal's menu system follows the Chain of Responsibility pattern. On each page request, the menu system determines whether there is a module to handle the request, whether the user has access to the resource requested, and which function will be called to do the work. To do this, a message is passed to the menu item corresponding to the path of the request. If the menu item cannot handle the request, it is passed up the chain. This continues until a module handles the request, a module denies access to the user, or the chain is exhausted.

Command
Many of Drupal's hooks use the Command pattern to reduce the number of functions that are necessary to implement, passing the operation as a parameter along with the arguments. In fact, the hook system itself uses this pattern, so that modules do not have to define every hook, but rather just the ones they care to implement.

Front controller pattern
Index.php will respond on requests for a web based application, deciding what happens next.

require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$return = menu_execute_active_handler();

// Menu status constants are integers; page content is a string.
if (is_int($return)) {
  switch ($return) {
    case MENU_NOT_FOUND:
      drupal_not_found();
      break;
    case MENU_ACCESS_DENIED:
      drupal_access_denied();
      break;
    case MENU_SITE_OFFLINE:
      drupal_site_offline();
      break;
  }
}
elseif (isset($return)) {
  // Print any value (including an empty string) except NULL or undefined:
  print theme('page', $return);
}

Anti Patterns
Old Baggage
In the theme implementation in Drupal we have to replace the node.tpl.php files with the theme’s node.tpl.php file so we can consider node.tpl.php file in the modules/nodes can be consider as dead-code so the old baggage occurs.

function _phptemplate_variables($hook, $variables = array()) {
  switch ($hook) {
    case 'page':
   // node type isn't available in template.php so we have to go get it.
    if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(arg(1));
      $type = $node->type;
    }
    if (($node->type) == 'forum') {
       // if the node type is forum load the page-forum.tpl.php layout
        $variables['template_file'] = 'page-forum';
        }
      break;
  }

  return $variables;
}

No Name
In some Drupal functions which separate parameters into odd orders that must be memorized, when one variable as an array when one is numeric.

function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) {
  $output = '<div class="item-list">';
  if (isset($title)) {
    $output .= '<h3>'. $title .'</h3>';
  }

  if (!empty($items)) {
    $output .= "<$type". drupal_attributes($attributes) .'>';
    $num_items = count($items);
    foreach ($items as $i => $item) {
      $attributes = array();
…

Control Freak
In Drupal thousand of rows of code will loaded in per request. But when end user add some more modules the Drupal the overhead will be high.

/**
 * Load all the modules that have been enabled in the system table.
 */
function module_load_all() {
  foreach (module_list(TRUE, FALSE) as $module) {
    drupal_load('module', $module);
  }
}

Spaghetti
When we use global for certain function the reusing parts will be impossible and also we can’t use that function in another script and also global the him itself so the what it does and from where it coming is difficult to understand.

function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') {
  global $theme_info, $base_theme_info, $theme_engine, $theme_path;
  $theme_info = $theme;
  $base_theme_info = $base_theme;
...

Hard coding
Another one is you cannot query multiple databases by default there is db_set_active() which makes a call to the hardcoded db settings. But Drupal is not set up to handle multiple database natively.



/* Database URL format:
 *   $db_url = 'mysql://username:password@localhost/databasename';
 *   $db_url = 'mysqli://username:password@localhost/databasename';
 *   $db_url = 'pgsql://username:password@localhost/databasename';
 */
$db_url = 'mysqli://root@localhost/drupal_6';
$db_prefix = '';

// Below BY CHAMIN
Duplicate code
Duplication code segment can be found in the Drupal core (theme.inc) and the function grupal_get_html_head() in the common.inc .

//Duplicate below segment in both common,inc and theme.inc
  $output .= " <style type=\"text/css\" media=\"all\">";
  $output .= " @import url(misc/drupal.css);";
  $output .= " </style>";

Heshan Wanigasooriya
Github