Drupal In a Box - Creating really custom functionality within Drupal 7
I often get tasks on Drupal client sites for completely custom functionality. Those are the most fun tasks for me. I have the amount of freedom and creativity to build up the required functionality and at the same time I can utilize Drupal's framework and infrastructure to keep things modular and reusable.
Some developers are great with HTML/CSS and Javascript yet they struggle with getting their functionality done on Drupal given that it has a bit complex architecture. This blog post might be very helpful for the beginner and intermediate Drupal Developers and Site Builders.
In this post, I'm going to walk you through the steps of building custom work on Drupal, and I'm going to provide you with a module skeleton for reference.
First of all, let me show you some examples of very custom components that I had to rebuild on Drupal:
As you can see, these are very customized components (Javascript mainly) that have not so much to do with Drupal itself. However, they can be wrapped nicely within Drupal's framework and module system. Of course, I'm not going to discuss the micro technicalities, but rather I want to show you how such component can be plugged into Drupal, and I'll leave the rest to your imagination. Think of your component as a canvas and Drupal is the frame of it.
I'm going to start a new Custom.module and walk through the major helpful hooks. I'm going to display the component within Drupal either as a standalone page or as a block that can be placed where ever.
Component function
/** * The main function where the component is being generated */ function custom_component() { // Simple arguments $arguments = array('argument1' => 'Hello', 'argument2' => 'World!'); // Adding required CSS styling and Javascript functionality drupal_add_css(drupal_get_path('module', 'custom') . '/custom.css'); drupal_add_js(drupal_get_path('module', 'custom') . '/custom.js'); // Passing arguments to a theme function $output = theme('custom_component', array('arguments' => $arguments)); // Return clean HTML output return $output; }
Theme Function
/** * Implements hook_theme(). */ function custom_theme($existing, $type, $theme, $path) { // Simple theme definition that points to the file // custom-component.tpl.php where you can place your HTML return array( 'custom_component' => array( 'path' => drupal_get_path('module', 'custom') . '/templates', 'template' => 'custom-component', 'variables' => array('argument1' => '', 'argument2' => ''), ), ); }
Template File
<?php /*** * This is a template file * This is where most of your creative custom work goes * You can write HTML code as well as Javascript / CSS and PHP, obviously! * For simplicity I'm just receiving arguments and printing them here. * But this can be the HTML of any sophisticated component. */ ?> <div class="custom-component"> <p>I'm just going to display the arguments passed to form a Hello World! sentence.</p> <h1><?php print $argument1 ?></h1> <h2><?php print $argument2 ?></h2> </div>
Define A Drupal Page (Path)
/** * Implementation of hook_menu() */ function custom_menu() { $items = array(); // This item will point to the path /custom_component // and is going to use custom_component_page function as a callback $items['custom_component'] = array( 'title' => t('Custom Component'), 'page callback' => 'custom_component_page', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK ); return $items; }
Define the PAGE Callback
/** * Custom Component Page Callback, returns the output of * the custom_component function within an extra HTML wrapper */ function custom_component_page() { $output = "<div class=\"component-page-wrapper\">"; $output .= custom_component(); $output .= "</div> <!-- // end of page wrapper -->"; return $output; }
So far this is a fully functional module. Once enabled, users can visit the /custom_component page and be able to view its content. And since we have the component built why not add another Block display that would be more portable.
Add a block Display
/** * Implements hook_block_info(). */ function custom_block_info() { $blocks['custom_component'] = array( 'info' => t('Custom Component'), 'cache' => DRUPAL_CACHE_PER_PAGE, ); return $blocks; } /** * Implements hook_block_view(). */ function custom_block_view($delta = '') { switch ($delta) { case 'custom_component': $block['subject'] = t('Custom Component'); $block['content'] = custom_component(); break; } return $block; }
Dealing With Settings and Configs
For real life components when things get a bit more complex, it's essential to deal with dynamic arguments, parameters, and configurations. Let's say you want to pass dynamic arguments to theme files, or pass settings to Javascript Drupal Behaviors. You can easily accomplish this using Drupal helper functions. Below is an example for that
/** * Implements hook_init(). */ function custom_init() { // Save variables in Drupal variable_set('config_name' , 'config_value'); // Get variables from Drupal $config = variable_get('config_name', 'default value'); // Make settings visible to Javascript Drupal Behaviors // Used within Drupal Behaviors like this: // Drupal.settings.customComponent.myJsConfig drupal_add_js( array( 'customComponent' => array( 'myJsConfig' => $config, ) ), 'setting'); }
Javascript Behavior (CUSTOM.JS)
(function($) { /** * Drupal Behaviors. */ Drupal.behaviors.customComponent = { attach: function (context, settings) { var config = Drupal.settings.customComponent.myJsConfig; // Do whatever with the config/param/setting value console.log('Drupal custom module config value: ' + config); } }; })(jQuery);
Have any questions on any of this? Write them in the comments section below and we'll answer them as soon as possible.