Explaining Panels: An Overview for Drupal Developers
In my time as a Drupal developer I've had plenty of long and— and sometimes heated— conversations about Panels module. Is it good? Is it too complex? Does it make a site faster or slower? Those are all fine questions to debate; after first agreeing what is Panels?
First, when people talk about "Panels" there are numerous submodules and supporting projects they might be referring to as well. To understand where the lines are drawn between the modules I like to look at which existing Drupal concepts each module augments.
Panels module is a User Interface on top of theme()
and hook_theme()
I suspect the common introduction to Panels is something like:
It's very easy to get overwhelmed by the number of options, buttons and links. So let's start simpler: a two column layout.
The code needed to declare that this two column layout exists at all is really small. Browse around the directory declaring it. There is not much there. An info array tells us most importantly that we have left and right regions.
'regions' => array(<br> 'left' => t('Left side'),<br> 'right' => t('Right side')<br> ),
Source: http://cgit.drupalcode.org/panels/tree/plugins/layouts/twocol/twocol.inc?id=7.x-3.4
And there's a template file that prints the left and right regions.
<div class="panel-display panel-2col clearfix" <span style="color: #000000"><span style="color: #0000BB"><?php </span><span style="color: #007700">if (!empty(</span><span style="color: #0000BB">$css_id</span><span style="color: #007700">)) { print </span><span style="color: #DD0000">"id="</span><span style="color: #0000BB">$css_id</span><span style="color: #DD0000">""</span><span style="color: #007700">; } </span><span style="color: #0000BB">?></span></span>><br> <div class="panel-panel panel-col-first"><br> <div class="inside"><span style="color: #000000"><span style="color: #0000BB"><?php </span><span style="color: #007700">print </span><span style="color: #0000BB">$content</span><span style="color: #007700">[</span><span style="color: #DD0000">'left'</span><span style="color: #007700">]; </span><span style="color: #0000BB">?></span></span></div><br> </div><br> <div class="panel-panel panel-col-last"><br> <div class="inside"><span style="color: #000000"><span style="color: #0000BB"><?php </span><span style="color: #007700">print </span><span style="color: #0000BB">$content</span><span style="color: #007700">[</span><span style="color: #DD0000">'right'</span><span style="color: #007700">]; </span><span style="color: #0000BB">?></span></span></div><br> </div><br></div>
Source: http://cgit.drupalcode.org/panels/tree/plugins/layouts/twocol/panels-twocol.tpl.php?id=7.x-3.4
The simplicity of this layout plugin (aside from its love for divs and css classes) makes it easy to forget that it is an abstraction layer on top of the hook_theme()
and theme()
. When most Drupalers think about the theme system, they think about the complexity.
Not many people in the world completely understand this diagram. Source: http://john.albin.net/drupal/arrays-of-doom
At the heart of this complexity, is the simple idea that you use hook_theme()
to tell Drupal that there's a type of thing that you would like to print in the future (like a two column layout). And that thing will require some variables (like ‘left' and ‘right'). Then at any time theme()
can be asked to print a two column layout and all that it needs is to be told are the values of ‘left' and ‘right'.
So how do we figure out what is actually supposed to go in ‘left' and ‘right'? Panels answers that question in a way that is fundamentally different from nearly all Drupal Core usages of hook_theme()
/theme()
. Core's node module uses hook_theme()
to say "Hey, in the future I'm going to ask to you print a ‘node' and I'll give you is a variable called ‘node'". And what it really means is "Oh, yeah, and I'm going to need a ton of preprocessing to derive some variables that can actually be printed in a template." When that Panels layout plugin says "I really only need ‘left' and ‘right'", it can do so because Panels has a separate subsystem for building up those variables.
If you wanted Core to print a node into two columns then you would likely rely on preprocessing and template overrides to figure out what goes where inside theme(‘node')
. Panels can take a node and follow instructions for splitting it into ‘left' and ‘right' before calling theme(‘twocol')
.
This somewhat academic distinction opens up a different mental model for developers. Core encourages you to think "I'm rendering a node and I'll alter and override what that means once I start doing it." Panels encourages you to think "I know I want a two column layout and here is the configuration to split up this node accordingly." In an upcoming blog post I will explain why I prefer the second way of thinking.
Page Manager is a UI on top of hook_menu()
and hook_menu_alter()
The response you get when visiting a URL on a Drupal site is determined largely by hook_menu()
. When you go to the user register page you see a registration form because the user module told hook_menu()
that a registration form belonged there. Other stuff may appear in Blocks in sidebars, footer and headers that are outside the knowledge of hook_menu()
. hook_menu()
primarily controls the main response that shows up in the "content" region of page.tpl.php
If you're on a music website that has a Taxonomy term of "Jazz" you might see a listing of every node on the site tagged in Jazz. That's what taxonomy module tells hook_menu()
to put in the response for a taxonomy term page. On the other hand, if I were running a music website I would probably want my page for Jazz to be a little more customized than a simple listing of everything on the site tagged in Jazz. Let's step up the complexity just a little and imagine that the term pages for music genres should separately group musical artists, albums and songs.
Here the distinction between Panels and Page Manager becomes cloudy. Page Manager tells Drupal "I know taxonomy module told you how term pages look, but I am overriding that. Use this Panels configuration instead." Page Manager changes the way Drupal responds to existing paths as well as telling Drupal about new ones.
Panels is not even necessary to use Page Manager. Out of the box, Page Manager gives you the option to override existing URL paths just to change the HTTP response code (for that rare case where you want node/%node
to respond with a 403 for anonymous users but don't want to use the node access system). There are even other modules like Contextual Adminstration https://www.drupal.org/project/context_admin which use Page Manager to add more administrative options.
To go over that Jazz use case one more time, Page Manager:
- Overrides the normal ‘list all nodes' behavior declared by Taxonomy module.
- Replaces it with a package of Panels configuration which
- Declares the layout it wants
- Contains instructions for populating the regions of that layout based on the given "context" (the taxonomy term "Jazz")
Panels Everywhere is a UI alternative to page.tpl.php and Blocks UI
I just mentioned above that Page Manager is the way to control the the "content" region inside page.tpl.php
. Panels Everywhere is meant to entirely replace your typical page.tpl.php customizations and the Block configurations that feed that file.
The Core block system encourages a "global" mindset.
This block will show up on every page except those the pages whose URL paths match a certain pattern.
Blocks can also be restricted by user role.
This model implicitly asks you to think "all of my Blocks are going to show up all of the time except for when I tell them not to show up." You can simultaneously think in the inverse of "this Block will never show up except for this one page." That model can put too much in the site builder's head at once; especially when there is another way to change what happens at this global, Block level. With Core's tools you can always override page.tpl.php with something like page--front.tpl.php. That template can change the overall markup of the front page as well as change what regions will actually print their Blocks. Now you have another independent paradigm to keep in mind when asking yourself "should I expect this block to print?"
Panels Everywhere allows for those two concepts to be united. Decide on your layout first and then pull in the contents of the layout regions. Core will load Blocks even if an override of page.tpl.php
has nowhere to print them.
With Panels Everywhere, just like Core, you will have to figure out if a given element should be controlled by main page response (hook_menu()
/Page Manager) or if the element is a more global decoration (Blocks/Panels Everywhere). An advantage of the Panels Everywhere / Page Manager combo is that the same workflows and concepts apply in both places.