by David BurnsJuly 17, 2010

Assembling Pages with Drupal

As with many facets of Drupal, and coding in general, there are multiple ways to accomplish the same task. A good exmple of this was with the recent additions to the Lullabot team. The expanded team brought together three skilled developers and an amazing designer each with their own methods of site building. On one side we have Jerad Bitner and myself, who for the past few years have been building sites exclusively with Panels module. On the other side we have James Sansbury and Jared Ponchot who also build beautiful sites using the more recent Context module.

Our first collaboration was the redesign of Lullabot.com, since this project was initially designed and scoped by James and Jared, the decision to use Context module was already in place. I was in no rush to learn Context, when I knew the same result could be achieved with Panels. Lucky for me James and Jared are both excellent resources for answering questions and giving great examples. Now that the project is complete I have a better understanding of Context module. This article is intended to identify the similarities, differences, pros and cons of using each module to build a Drupal website. There is some overlap of terminology which will occur. A block is a block which can be used in both Context and Panels modules. Context is a stand alone module, but not the same as "context" that is used in Panels module. This will be clarified as you read the rest of this article. But before we travel too far down the rabbit hole, let's answer two basic questions:

What is a Page? A page is HTML/JS/CSS rendered to the browser through a series of theme functions, hooks, preprocessors and template files.

How does Drupal build a page? By making calls to menus, blocks, nodes, and modules, which then provide markup that will be placed into regions within the template system.

Blocks

"Blocks are the boxes visible in the sidebar(s) regions of your Drupal website. Most of the blocks that you will see (e.g., recent forum topics) are generated on-the-fly by various Drupal modules, but you can also create your own blocks." ~ http://drupal.org/node/17170

The Block system that comes with Drupal core has been around since Dec 2000, back when Drupal was just a baby. Since that time it has undergone many revisions and improvements. Today, modules can provide their own blocks by invoking hook_block(). These blocks can then be configured on the block administration page. A block can then be placed into a single region per theme and have it's visibility set by path, the current logged-in user's role, or custom which involves PHP logic that will return TRUE (enabled) or FALSE (disabled). For a number of websites this is enough options to accomplish most layouts, but as you gain expertise and work on more complex sites you begin to see the limitations of only having a single region to place a block within a theme. It's also worth mentioning that using custom and executing PHP from the database is very bad practice and could potentially be a security risk.

Context

"Context allows you to manage contextual conditions and reactions for different portions of your site... Think of conditions as a set of rules that are checked during page load to see what context is active. Any reactions that are associated with active contexts are then fired." ~ http://drupal.org/project/context

I like to refer to Context as "The Advanced Page Builder". This is the block system on steroids, but even that is an understatement. Context module, created by the awesome team at Development Seed, lets you manipulate and activate items from the menu, block, module and theme systems using a robust set of conditions. Multiple conditions (or rules) can be set per context and multiple contexts can be set per page. Sounds confusing but hopefully a brief use case and diagram will help clarify.

Context 1: Condition - Current node is of type "Blog" and user viewing blog has a role of "Admin". Reaction - Display Admin Block in Left Column. Display a view list block of all nodes by current node author in Left Column.

Context 2: Condition - Current node has taxonomy "Tech" Reaction - Display a view list block of all Blogs that have a "Tech" taxonomy in the Left Column. Set Active menu item to "Tech".

Before a page is rendered, all of the context for a page will be called. If the current user logged in viewing this blog node has the "Admin" role, Context 1 and Context 2 will both exist, the resulting output of this page will have Admin Block, view list block of all nodes by current node author, and a view list of all Blogs that have a "Tech" taxonomy displayed in the region "Left Column" and the Active menu item will be "Tech". If the current user does not have the "Admin" role, only Context 2 will be true resulting in a view list of all Blogs that have a "Tech" taxonomy displayed in "Left Column" region and Active menu item will be "Tech".

context example

Due to this ability to mix, match, and stack context per page you have the ability to place not just blocks, but also views (provided by Views module) and menus into various regions within a page. In comparison, the core block system limits a block to only appear in a single region per theme with constrained visibility rules. This is great for site builders who can conceptualize sections of a site into specific context that will be needed to put together an entire site. However, this is not really great for handing a site over to a client who is expecting to modify page layouts without first explaining how Context module works. Another limitation of Context is that it's limited to the regions provided by the template system. The default regions available will be those provided by the theme. Context module allows you to provide your own template files which can add more regions and unique page layout.

Panels

"The Panels module allows a site administrator to create customized layouts for multiple uses. At its core it is a drag and drop content manager that lets you visually design a layout and place content within that layout." ~ http://drupal.org/project/panels

I like to refer to Panels as "The visual Page Builder". If you are familiar with the tabbed navigation of Views 2, then you will be familiar with the navigation of Panels 3. The reason for this similarity is that both modules are the brain child of Earl Miles (merlinofchaos). Views and Panels work very well together because of this. At it's core, Panels is a drag and drop interface for placing content into sections on a page. This is attractive to most site builders because it sounds easy, and if you're just dropping in blocks or views then for the most part it is simple to use.

Panels is path driven. When creating a panel page you must declare a path. This path is what will invoke panels module to take over the rendering of the content area the page. This also means that Panels module can "hijack" the rendering of paths provided by Drupal core and other modules. As an example, let's say you enable Panels to render the output for 'node/%'. Once enabled you won't notice much of a difference when viewing the node, but if you look at the page source you'll see that there are divs with IDs and Classes which identify Panel panes.

Panes are regions within panels in which content can be placed. Panels provide a number of layouts (templates) that come shipped by default, they include 1, 2, 3 column, stacked or bricked. Another option for layout is Flexible, which allows you to build very complex layouts using a JavaScript UI without the need of creating custom template files. However, if you're more comfortable using custom template files that is also an option. Panels also provides you with the option to disable all sidebars provided by the theme, thus giving you further control of the rendered page.

So back to our example of using Panels to render 'node/%', and we've decided upon a layout (example: 2 column). How do we change the layout for a different content type? This is actually pretty simple. Each Panel you create allows you to have "Variants". Variants use selection rules that will trigger a specific panel display. Our default for all 'node/%' will be 2 column, but we can create a variant for node type Blog which can have an entirely different layout with entirely different content displayed in panes.

Aside from being a great way to change layout and position content on a page, Panels is a powerful tool that makes all content aware of each other. Since Panels is a wrapper that pulls in objects before rendering html to the screen, it has the ability to pass information about a block, node, or view to other objects within the panel. This is what Panels calls "context" (not to be confused with Context module). To build upon our original example of using 'node/%' as our Panel display, we then have the ability to pass anything (cck fields, author info, relationships, taxonomy, flag counts) from the node, into a view being rendered in the same panel!

Performance

Performance is important for any site (just stating the obvious). Whether it's a high traffic site spread across multiple servers or a small site on shared hosting, making the best use of resources available to deliver pages to your user faster gives a better user experience. With that said Drupal has a great caching mechanism built into core that, when enabled, provides significant improvements. However, the core block system is not as efficient at pulling in content compared to Context or Panels.

The core block system loops through block_list() for each template region and selects all active blocks in that block for current user. It then checks that block to see if it's enabled for current path or validates custom PHP rule. To use an analogy, think of an audience and a stage (which works well at DrupalCamp), and think of each member of the audience as a block. All members of the audience raise their hand if their first name starts with "A" (select region). The audience members with their hands raised keep their hands up if they consider themselves a themer (select active). Those remaining come up to the stage but put yourself in alphabetical order based on last name (sort delta). Repeat this process for each letter of the alphabet (each region). You can see this isn't exactly efficient.

Context is a vast improvement over the block system. After creating a set of conditions (rules) and reactions (display) via the Context UI (a replacement to the Block Admin page) this information is stored. When the page object is being built before being passed into theme functions it identifies which context currently exist and adds the context reaction (block, menu, views, etc.) to the page. Continuing the audience analogy, select 2 separate groups (context 1 and context 2) of people (reactions) at random and tell them where they would stand on stage. Clarify that not everyone on stage is a block, some can be a view or active menu item. I tell them context 2 no longer exist, the people in that group would leave the stage. This is much more efficient than looping through each region and checking for active blocks.

Panels has a bad wrap for being inefficient. I've know developers brought into projects to strip out Panels to improve performance. Let me publicly state that Panels is NOT the cause of slow loading pages. Inefficient and uncached queries used within views and blocks (both custom and provided by other modules) pulled into a Panel cause long page loads. Creating complex pages by stacking multiple inefficient blocks, views, and tabbed displays into a page will increase page load times drastically. However, the maintainers of Panels noticed this and have built in an incredible caching system that let's you cache individual panes inside a panel display. As stated before Panels is simply a wrapper that calls content into a page. Using the audience example one last time, I pick and choose the members I want from the audience and tell them exactly where to stand on stage. That's it, it's done. Panels is fast and direct.

With all that being said, there's other more efficient ways to address server performance, like apache/mysql tweaking, reverse cache, memcache and/or CDN for static files. Some of these advanced performance techiques are impossible to achieve on shared hosting environments. You get what you pay for.

Use Case

For many new users and simple site builders, using blocks is enough to get the job done. If the job gets done quickly and correctly, isn't that all that should matter? Stick with what works.

For more advanced projects that have complex layouts or require all changes to exist in code (exportable) then you'll need to use Context or Panels. Exportables are extremely important because it keeps all changes in code. This makes it very easy to develop in a local->dev->stg/qa->live environment. It also saves some time if someone makes a change that breaks the site or layout. Exportables make it very easy to revert, even more so if using a version control system.

Context is great for developers, not so great for end users. The abstract model of context requires a strong understanding of the project and which context will need to be created to achieve the desired output. It is much easier to understand Context UI than Panels UI. Context UI is more dynamic at putting together page variants by the way it allows multiple contexts to be declared based upon what's rendered on the page. However, it's also limited at passing information within a piece of rendered content to other content that exists on the page. By adding jQuery UI and Admin module Context module can provide a drag and drop UI for adding content onto an existing page. This is an excellent feature that brings it's usability closer to the drag-n-drop ability of Panels.

Context UI

Panels is great for site builders, and if there were a simple mode (less options) for the UI it would be great for end users as well. As it stands the nice drag and drop interface works well, but once you get into the configuration options of Panels, things get confusing for those new to it. These issues are being addressed with upcoming Panels 3. It's also good for non-developer site builders that are familiar with argument handling (from views module) to create a robust page with content that is all related. To me this is the biggest benefit and underused part of Panels. I could go on for two days about all of the awesome features of Panels, but that'll need to be in a separate article.

panels UI

Future

The ultimate goal here and with Drupal in general is to allow site builders to build robust sites with related content without the need to get into code. As you can see Context and Panels modules do very similar task with very different approaches. But what is important to all of us is that they provide a solution. There was a minor disturbance in the force (aka our community) seeing two vital groups of developers addressing a similar issues without collaborating. Well good news, this year at DrupalCon San Francisco, these bright individuals were forced into a small room to settle their differences. By the end of this meeting there were hugs and tears of joy (okay, maybe just hugs).

What does this mean for Drupal? It means that the power of context, the ability for content on a page to be more dynamic and share information between each other will be an initiative that hopefully we will see built into a future release of Drupal core. Our modules can tap into a context api which will be full of valuable information about the user, node, views, blocks, and menus that exist within the page. This is huge. This is important. This is what keeps Drupal ahead of all other Open Source Content Management Systems.

Sign up for the Lullabot Newsletter and get our latest articles and more direct to your inbox

We can partner with you!

We've accomplished some amazing things for some really great clients. We'd love to talk about how we can help your next project!

Tell us about your project

You can be a 'bot!

We have an amazing team of friendly experts in a number of disciplines. We work hard, have lots of fun together, and take great care of our team.

See our open positions