Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
May 10 2017
May 10

The D8 project I'm currently working on used the file-system based workflow for configuration management. As our deployment workflow improved and got slightly more complex, we started to run into issues with file permissions during deployments and wanted to revert back to the database based workflow that is the default for a new D8 installation.

Unfortunately, though there are a lot of resources on the web that explain how to go from the database to the file system approach, I couldn't find anything explaining how to revert back to the database based configuration workflow. All there is, is this kind of information:

You should do this before installing Drupal, as it is complex to revert back to database-based configuration management setup once you switch to file-based.

So I did some digging and came up with this:

Step 1: Backup

Make a backup of your database and your codebase including all configuration files and your settings and services yaml files. The following actions have the potential to break your site. Do everything on a non-production site, preferabbly hosted on your local machine so that recovery is easy.

Step 2: Export your current configuration

Run this drush command to export your configuration:

drush cex sync

Step 3: Revert your settings and services

Put the following in your services.yml:

services:
  config.storage:
    class: Drupal\Core\Config\CachedStorage
    arguments: ['@config.storage.active', '@cache.config']
  config.storage.active:
    class: Drupal\Core\Config\DatabaseStorage
    arguments: ['@database', 'config']
    public: false
    tags:
      - { name: backend_overridable }

And comment out the config storage setting in your settings.php:

// $settings['bootstrap_config_storage'] = array('Drupal\Core\Config\BootstrapConfigStorageFactory', 'getFileStorage');

Step 4: Clear caches

Clear your caches using these drush commands:

drush ev "drupal_flush_all_caches();"
drush cr

Step 5: Re-import your configuration

Run this drush commands to import your configuration:

drush cim sync

This step failed for me at first, due to interdependency between different configuration sets. In my case there had been changes to fields that where attached to some custom ECK entities, and the configuration import wanted to import these before the definition of the actual entity. I was able to work around that by moving the conflicting field configuration out of my configuration sync directory, run the import, moved the files back into the sync directory and run the import again. But you might need to tinker a bit more with that depending on your setup.

Conclusion

Running these 5 steps allowed me to move the site back to use the database based configuration workflow. Let's see if that also helps us with our deployment problems.

Jul 26 2014
Jul 26

The last months I was busy with a friends art project. Today I'm very happy to announce that it went public on july 15th and is doing good so far.

Jule, the founder of Port of Art, approached me last summer, asking if I could help her building an online market place for artworks. Working primarily as a freelance Drupal developer, knowing that her budget is tight and that she is certainly not the first one with this idea, I hesitated. But I gave it a thought and after several meetings I agreed. I liked the idea and I liked Jules approach, that is very trusting and positive without being naive. I like good people ;) She also gave me the impression of being able to value constructive input, even if it means to change previous ideas. That is a good feature in clients!

Basic ideas with a special flavor

The basic requirements were pretty simple:

  • Content management for static content pages as well as for special content like the artworks that are sold on the site
  • Search artworks by different filters
  • Legal compliant checkout process
  • Integration of external payment providers (limited to paypal for the moment being)
  • Contact forms
  • Multilingual content and communication
  • Integration of social media
  • Some map views for geo visualization
  • SEO, customizability, ...

So far that was relatively straight forward and we all love Drupal for that.
But there were some special requirements too, that had a huge impact on my choice of modules to realize this with.

  • Artworks don't integrate with a basic warehouse approach. Each one should be unique and can be bought only once. Therefor there was no need for a shopping cart either.
  • Artworks can be bought for a fixed price or as an auction.
  • Artworks under a certain price are not sold via the site, but instead the customer and the artist are put in touch directly and have to figure out the details independently of the platform.
  • Artists should be able to upload their artworks, pay a fee to get them published and than manage the selling and delivery on their own.
  • Artworks expire after a certain time that depends on the publishing fee that the artist is willing to pay.
  • Once an artwork has been sold on the site, an additional fee has to be paid.
  • Fully customizable e-mails

The main content is obviously the artwork. This is a node type with additional fields to represent attributes of an artwork. Then there are static pages, artschools, faqs and webforms. On the user side we have two frontend user roles for customers and artists that get enhanced using the Profile 2 module.

Additional considerations

The situation that our development team was faced with: Small budget, tiny team (only 2 people), the project's concept still a little in the flux. The founder had no technical background or previous experience using Drupal but needed a customized shop system that she could actually manage after we finished the project and went on to other things. So one of the goals during development has always been to make things configurable. Special text at a certain page? Build a setting for that. A special criterion that controls logic during checkout? Don't hardcode it somewhere! Build a setting for that as it might change later and you don't want to change code for simple things. I love drupal for it's easy variable management and quick form building capabilities. Building an admin form to control certain behaviors takes rarely more then 10 minutes. Obviously there are things that you can't build that way, but when you can, do it. I feel much butter with it and the client loves it too because it gives him control.

Conception and development process

One of the things I knew before, but that got confirmed again: Communication is the key. The client has never did a web project before. That meant that certain good practices and workflow, concerning the development process as well as the final product, were not clear to her. So we (the designer and me) spend a good amount of time helping her figure out what was realistic and which compromises needed to be done in order to deliver the product without cost explosion or an exagerated time frame. Being honest and communicating potential problems early on, as well as the clients openness towards constructive input, was something that attributed a lot to the perceived quality of the development process. Including the client in the development and design decisions also allowed us to educate her on the technical aspects of the product and raise awarness about technical implications, making her see advantages and restrictions in different areas that she didn't consider in the beginning.
We didn't formalize the process, but we ended up with some kind of agile development with three distinct roles: Conception and design by the client, frontend by the designer and backend logic and architecural design by me. That worked very good for us.

Obvious modules that we still didn't use

First, there is Rules. A crazy wonderland for workflow configuration that amazes me every time I look at it. But I've almost never used it. Call me old fashioned, but when business logic or complex relations must be build, I prefer to build them on my own. I want as much logic as possible in the code, not in the database. So for all the power Rules provide, I still prefer not to use it.

Then there is Commerce. We have never build a real-world website with it, so our experience was very limited. We thought about it. Very seriously. Then we decided against it. From todays perspective that was probably an error. But given the special requirements we were afraid of having to spent too much time customizing and altering the workflow that commerce proposes. This was more of a gut feeling. And at the end I'm not sure it was the right decision. We ended up with conceiving and building a full fledged product management incuding the purchase logic and payment. The obvious advantage when you write something like this on your own, is that you have a lot of fine grained control about flow and design. But the price is pretty high considering the amount of time necessary. At the end we have a considerable code base that needs to be maintained. So next time, I hope I'll remember this an give commerce a more in depth examination regarding the potential for the problem at hand.

Crucial contrib modules / add ons

It's hardly necessary to mention, but we couldn't have build the site so easily without the usual candidates: Views, Webform, Better Exposed Filters, Address Field, CTools, i18n, References, Profile 2, Geofield, Global Redirect, Libraries, ...

The fantastic wookmark jquery plugin is responsible for the display of the central search component of the site. Our designer loves it!

Some modules that got born or advanced

I build MEFIBS for this site. I had a need for that functionality before, but never quite as strong as this time, so I decided to solve it as a self contained module instead of hacking things together. Though there are some problems currently with a few new features that I added recently, it is already in production and doing pretty well. Have a look at the filter and sorting blocks on the artwork search page: . Two independant blocks without duplicating a views display or intensive custom form altering. That's pretty neat.

Hopefully the jQuery Update module will also profit. During development I ran into issues with the admin version feature introduced here: https://www.drupal.org/node/1524944. I wrote about it in jQuery version per theme. This resulted in a feature patch that is currently on a good way to get committed soon.

I also found a bug in the PayPal for Payment module: https://www.drupal.org/node/2052361 that will hopefully get fixed soon.

Another module I find myself using often is my sandbox module Mailer API. It's a bit cumbersome to use as a developer, but for the client it's perfect. She can customize practically every mail that will be send by the system. It's all on a single configuration page and supports multilingual setups. A test mail feature is also included to see what mails will look like. And a batch mailer that the client often uses to address a bunch of people. It's like very easy home made promotional mails in a consistent look and feel. Made the client happy.

For frontend eye candy we have build a jQuery plugin that is responsible for the collapsible checkbox filter elements in the left side bar.

Some module discoveries

During the work on www.port-of-art.com I found some modules that I didn't know before.

The Form API Validation module allows you to simplify validation rules in custom forms, using predefined validation rules. And you can also add your own rules which we used for the price entry validation needed when artists publish their artworks.

The Physical Fields module provides fields for physical dimensions and for weight attributes. That was exactly what we needed for physical goods. It saved us the time to configure fields in field collections.

Conclusion

At the end of the project I can say, that everyone involved has had a good and productive time and enjoyed the process and the result. The client is happy for all the things she can do with the site. Now she can concentrate on managing business and extending marketing. The designer was happy. Even if some of the design decisions might not have been the best ones looking at the requirements profile from today. I feel positive though that the system fully matches the clients expectations and that it'll be a valuable tool for developing her business. If the site manages to establish itself, it's more than probable that we would rebuild the system, at least some substantial pieces like the shop component.

We as the site builders are happy too. We feel that we have done a good job and that we managed to keep resources and expectations in balance. I would do it again, which always feels like a good measure.

Jan 18 2014
Jan 18

During the Drupal 8 port of my relatively new Mefibs module I realized that block creation in code got somewhat more complex than it used to be in Drupal 7 and earlier. It took me quite a while to figure out how this works, especially because there is not a lot of documentation for Drupal 8 yet. So I had to dig my way through core to see how it's done there and finally I got around it. The following is a comparison between how we used to do this in Drupal 7 and how it's done in Drupal 8.

Defining a single block

Let start with the ground work. In Drupal 8 blocks are now plugins. Defining a block in code is straight forward. You need to create a class located in lib/Drupal/mymodule/Plugin/Block/MyModuleBlock.php inside your module directory, containing:

 'My awesome content',
    );
  }
}

This creates a single block that outputs only the string "My awesome content". As you can see, there is little code. The annotation in the comment, which is a new concept in Drupal 8, informs the plugin system about our custom block:

  • id: This defines the ID of the block which is used to identify the block. This is also used in HTML id attributes and class names.
  • admin_label: This designates the label used on the block configuration page.
  • category: This allows to control in which category on the block configuration page the block appears. You can use one of the existing categories or define a custom new one.

Your block plugin class will usually extend BlockBase. Inspecting this class will give you an idea of other methods that can be used to achieve specific goals, like access control or custom setting forms for the block. In order to fully define and display a block it is sufficient though to implement the build() method from BlockPluginInterface.

VoilĂ , there is the block. So this is the equivalent of implementing hook_block_info() and hook_block_view() in Drupal 7.

Defining multiple blocks

As you may notice though, this only defines a single block. There is only one class with a single build() method and no obvious way of extending this to support multiple instances of the same block type. In Drupal 7 blocks are defined in hook_block_info(), which returns an array of block description arrays. So it would have been sufficient to return multiple blocks in an array, keyed by the block id, like this:

function mymodule_block_info() {
  $blocks = array();
  $myblocks = array(
    'mymodule_block_first' => t('MyModule Block: First'),
    'mymodule_block_second' => t('MyModule Block: Second'),
  );
  foreach ($myblocks as $block_id => $block_label) {
    $blocks[$block_id] = array(
      'info' => $block_label,
      'cache' => DRUPAL_NO_CACHE,
    );
  }
  return $blocks;
}

In this example the available blocks are defined directly in the hook implementation, but they could also come from a database query or from another source, you name it. The important aspect is, that the logic to define multiple blocks was the same as for defining a single block. You just returned more of them. As they all had their proper block ids, which are passed to hook_block_view() or hook_block_configure() they could all be handled independently, but still be defined using the same logic.

Now in Drupal 8 this got more complicated. It took me a while, but finally I found a simple example in the core system module that defines menu blocks. A menu block is structurally exactly what I was after: A block definition that adheres to the same logic, though it can be created by a user in the UI so that you never know in advance how many blocks there will be or what their block ids might be.

That is where derivatives appear on the scene. Might be that this is obvious for someone coming from a strong OO background, but I had never heard of them before, so they didn't catch my attention on the first screening of the core block implementations. For those of you who, just like me, do not know what derivatives are, I will cite a phrase from the documentation:
"Derivatives provide a simple way to expand a single plugin so that it can represent itself as multiple plugins in the user interface."

Gotcha! Cool, so how are they used? It boils down to this:

  • Create a new class called, let's say, MyModuleBlock and save that in lib/Drupal/mymodule/Plugin/Derivative/MyModuleBlock.php. This class must implement DerivativeInterface and can extend DerivativeBase for convenience.
  • Implement the interface method getDerivativeDefinitions(). You can also implement getDerivativeDefinition() but it's usually not necessary when the derivative class extends DerivativeBase.
  • Reference the derivative class in the annotation of your block plugin class.
  • In the block plugin class, use $this->getDerivativeId() wherever you need the actual id of the block instance that is processed.

To extend the first example, the block plugin class would look something like this:

getDerivativeId();
    return array(
      '#markup' => mymodule_build_block_content($block_id),
    );
  }
}

It didn't change much. We added the derivative class to the annotation and we retrieve the block id to pass it on to a function that retrieves content based on the given argument. That's it.

Now for the derivative class, we will simply use the logic from the earlier Drupal 7 example:

 t('MyModule Block: First'),
      'mymodule_block_second' => t('MyModule Block: Second'),
    );
    foreach ($myblocks as $block_id => $block_label) {
      $this->derivatives[$block_id] = $base_plugin_definition;
      $this->derivatives[$block_id]['admin_label'] = $block_label;
      $this->derivatives[$block_id]['cache'] = DRUPAL_NO_CACHE;
    }
    return $this->derivatives;
  }
}

As you can see, this is very similar to the way it had been done in Drupal 7 in hook_block_info(). The form changes, but essentially we return an array with all our block definitions, keyed by an internal block ID that allows us to differentiate which block we are processing.

The given example is very simplistic and you wouldn't use derivatives for this, because obviously you already know what blocks will be needed when you write the code. Instead you would create two block plugins in separate classes. But it should make the basic idea of derivatives clear. The getDerivativeDefinitions() method from the system module for example looks like this:

/**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions(array $base_plugin_definition) {
    foreach ($this->menuStorage->loadMultiple() as $menu => $entity) {
      $this->derivatives[$menu] = $base_plugin_definition;
      $this->derivatives[$menu]['admin_label'] = $entity->label();
      $this->derivatives[$menu]['cache'] = DRUPAL_NO_CACHE;
    }
    return $this->derivatives;
  }

In this example it is obvious why the blocks can't simply be defined in different block plugin classes, because they are not known at the time the code is written. So instead it needs to load all available menu entities from the storage controller and create one block for each menu.

That's it. I hope it helps someone. If you find errors or want to share something, please leave a comment.

Dec 07 2013
Dec 07

Finally there are stable releases for my Webform Protected Downloads module.

Honestly, it's been a while that both versions (6.x, 7.x) are pretty stable. Currently in use on over 1500 sites (according to the statistics on drupal.org) there have been very few bug reports during the last year. The 6.x-1.x-rc4 had been around for 18 months without any bug report. And the few minor issues in the 7.x-1.x branch didn't do much of a release blocker either.

This was long overdue, I just never came to it. And so I'm glad today that it's finally done. Enjoy and give feedback!

Nov 27 2013
Nov 27

The project I'm currently working on requires different registration paths and logic for users with different roles. The heavy lifting of having different paths for the registration page, different form elements to collect profile data, and also the final role assignment is done with the Profile2 Registration Path module. Very nice work of grasmash (he has a nice blog too btw).

But one piece was missing. I needed to allow users who where finally getting only the authenticated role to register without admin approval, but other users with a specific role shouldn't have that right. There is no permission for that, no setting, and a standard drupal system allows for only one type of registration. No module out there that promised a quick solution. So I started looking into user_register_submit() to see how I could hook into there. While digging through that function I realized that the solution is a lot easier than I had expected.

My setup is the following: I have normal customers (authenticated role) and artists (authenticated role and artist role). The former should register without approval, the later with approval.
To achieve this it was sufficient to register my own submit handler to run before the user_register_submit() and then change one single form value.

First the submit handler:

function mymodule_form_user_register_form_alter(&$form, $form_state) {
  array_unshift($form['#submit'], 'mymodule_user_register_form_submit');
}

Second the manipulation of the status value:

/**
 * Custom submit handler for the user registration form.
 *
 * This is used to differentiate account register settings for normal
 * authenticated users vs. artist:
 *
 * authenticated users: Do not need admin approval.
 * artists: Need admin approval.
 */
function mymodule_user_register_form_submit($form, &$form_state) {
  $roles = array_filter($form_state['values']['roles']);
  if (count($roles) == 1) {
    // This is a normal user account.
    $form_state['values']['status'] = 1;
  }
  else {
    // This is an artist account.
    $form_state['values']['status'] = 0;
  }
}

$form_state['values']['status'] controls whether the registering user will be activated immediately or only later after admin approval. This effectively differentiates the both cases I needed to implement. Luckily this also controls which welcome emails will be send out, so that there is no confusion for the user.

Nov 23 2013
Nov 23

Recently I came across something that feels like a bug in the jQuery Update module. The current version allows to specify a modern jQuery version to use for a site, which is awesome when you want to include modern user interface functionality using jQuery UI. Additionally the module allows to override the jQuery version that should be used, but only for admin pages (introduced after a rather long issue discussion).

At a first glance this seems like a nice solution. Lots of websites that I'm working on use one or more frontend themes with heavy usage of modern versions of jQuery UI, and a backend theme (often simply Seven) for administrative usage. The admin override allows to revert the jQuery version to one that is compatible with the backend theme.

The problems comes when you look how that is defined. For a page to be considered an admin page it is sufficient for any custom or contrib module to implement hook_admin_paths(). Whether pages that are defined admin pages are rendered using the backend theme is a totally different question. A very good example from core is the node module:

/**
 * Implements hook_admin_paths().
 */
function node_admin_paths() {
  if (variable_get('node_admin_theme')) {
    $paths = array(
      'node/*/edit' => TRUE,
      'node/*/delete' => TRUE,
      'node/*/revisions' => TRUE,
      'node/*/revisions/*/revert' => TRUE,
      'node/*/revisions/*/delete' => TRUE,
      'node/add' => TRUE,
      'node/add/*' => TRUE,
    );
    return $paths;
  }
}

It provides support for a setting that is configurable under /admin/appearance and that controls whether certain node edit pages should be rendered using the admin theme. While this should in general work, it just doesn't. I didn't really dig too much into this, because the general idea of this solution seems like a misconception to me.

The version of jQuery that is used on a given page, is completely theme related. The theme doesn't care to know if it displays an admin page, neither does jquery_update. All that jquery_update needs to know, is which version of jQuery is compatible with the theme that displays the current page. So effectively, the jQuery version becomes a theme setting. If the road is chosen to allow different versions for different themes, then this separation should be clear.

Things get a lot easier from then on. And it opens up a lot more possibilities. Even 3 or 4 different themes, each one using a distinctive jQuery version, become possible. No need to content yourself with only one backend and one frontend theme. Obviously I haven't been the only one who ran into this and I was glad to provide help in this issue. There is a patch that applies to the latest jquery_update-7.x-2.x-dev version (as of the time of this writing) and that enables theme specific settings for jQuery.

If you want this functionality too, got to the issue and leave a comment: https://drupal.org/node/1969244

Nov 08 2013
Nov 08

Sometimes I just want to display specific pieces of information in, say, a node view. For styling reasons it is often preferable to have the same kind of basic wrapper markup as for fields. This would be particularly useful for pseudo fields that can be declared using hook_extra_fields. Those pseudo fields act almost like real fields in the Fields UI, meaning I can position them just like real fields, put them into groups, etc. But to display them I need to write all the markup myself or write custom theming functions to do this for me. But all I want is to have the same markup that is used for real fields.
Now instead of hardcoding the markup somewhere I came up with a little utility function that provides me with a drop in solution for those case:

/**
 * Helper function to render a pseudo field with the same markup as a normal
 * field.
 *
 * @param array or mixed $value 
 *  A render array or a string. It no array is given it will be turned into a
 *  render array of #type markup.
 * @param string $title 
 *  Set to NULL to hide the title.
 * @param string $field_name 
 *  The name of the pseudo field.
 * @param string $field_type 
 *  The field type, this can be something like text, number_float, see
 *  https://groups.drupal.org/node/124289 for more examples.
 * @param string $label_display 
 *  Can be 'above', 'inline' or 'hidden'.
 * @param string $entity_type 
 *  The type of the entity. This is used for class names.
 * @param string $bundle 
 *  The name of the bundle. This is used for class names.
 * @return void
 */
function pseudo_field_render($value, $title, $field_name, $field_type = 'text',
    $label_display = 'inline', $entity_type = 'node', $bundle = 'page') {

  $item = !is_array($value) ? array('#markup' => $value) : $value;
  return theme('field', array(
    'element' => array(
      '#label_display' => $title === NULL ? 'hidden' : $label_display,
      '#title' => $title,
      '#items' => array(
        0 => $item,
      ),
      '#field_name' => $field_name,
      '#field_type' => $field_type,
      '#entity_type' => 'node',
      '#bundle' => 'artwork',
      '#pseudo_field' => TRUE,
      0 => $item,
    ),
  ));
}

Having this code somewhere in my custom module space, I can simply call

function MY_MODULE_node_view($node, $view_mode, $langcode) {
  ...
  $label = t('My label for the pseudo field');
  $field_name = 'field_pseudo_field';
  $node->content['pseudo_field'] = pseudo_field_render($value, $label, $field_name);
  ...
}

Naturally this seems like an error prone approach as it pretends to have a field on a bundle where the field does not exists. It is even probable that this could brake some modules from contrib space that I'm not aware of and that need to do some altering based on specific bundle related conditions. But I did a little digging through the whole field theming in core and I didn't see any obvious error source. I will soon have this in production on one of my new projects and during development I haven't encountered any problems with this.

Oct 21 2013
Oct 21

Views are great for searches if you configure them with exposed filters. Especially if the filters are displayed in a separate block, for example in the sidebar, so that they are accessible on different pages and, once submitted, point to a results page. Modules like Better Exposed Filters can help you create great user experiences for accessing all the content you want to be found on your website. Something that I have often missed though, is the ability to have multiple exposed filter blocks, e.g. one in the sidebar, with a subset of filters, and then another one directly above the search results that provides additional (secondary or less important) filters and / or options that control the display of the search results (things like the number of items per page or the sort options). All that good stuff that is already built into views can only be displayed in one block. I know I'm not the only one missing this.
When I came across this need again for a current project of mine (unfortunately not yet online) I decided to give it a try and to solve this problem in way of a module.

While researching I have found several possible solutions to this and you can certainly find more on the web. Some examples:

The different approaches boil down to this:

  • Clone a view with exposed filters in a block so that you have essentially two identical filter blocks that you can display independently, then hide the undesired filters using CSS.
  • form_alter your way through the exposed block and add markup, so that you can style them in a way that makes them appear to be displayed independently.

Both approaches require a certain amount of maintenance overhead and are in their essence hacky solutions for a use case that the Views module does not address. Now I can't really claim that my solution is less hacky. But at least it is configurable using the Views UI and it spares you of having several identical views just for the sake of different filter blocks (resulting in less maintenance overhead) and the work of manual altering your views forms and coping with possible side effects (resulting in more flexibility and removing the need for programming skills).

You can find the solution I came up with as a sandbox module hosted on drupal.org: MEFIBS. It approaches the problem in a different way than described above. It comes as a views plugin that provides a UI that let's you enable an additional block for the exposable elements of a view (filters, sort options, items per page). Then, for each exposed element you can decide on the options form for that element whether it should be displayed in the default block or in the additional one.

The logic is pretty simple: Behind the scenes, the original exposed form is first altered to hide all elements (using CSS's display: none) that should appear in the additional form. In a second step, the original form is entirely cloned to provide the additional form block and all elements that already appear in the original form are hidden, leaving only the elements visible that have previously been hidden in the original form. All form ids in the additional block are altered to make sure that ids are unique. On form submit the submitted values are altered before the view receives its arguments, meaning the form values are rewritten to the original form ids so that the view receives exactly the form elements it expects. As often, the basic approach is pretty simple, but problems arise when trying to make this approach working in different use cases (ajax vs. non-ajax for example).

Originally I wanted to be able to provide multiple additional blocks, but I soon realized that a stable version with a single additional block is already enough work. And having two different form blocks is solving my normal use cases and should cover a great deal of the use cases of people who have already found other work arounds for this topic. So for the moment I'm focusing on getting the current two-block solution stable. Once that is done, I might turn it into a full module and maybe add support for more blocks, but that is still a long road ...

No code samples in this post, but all the code is available on drupalcode.org.

Update: The module is now available as a full project on drupal.org: https://drupal.org/project/mefibs. It's currently in dev and I started working on multiple block support and a better UI.

Oct 06 2013
Oct 06

During development I often need to know what a fully build views query looks like. There are several ways to accomplish this:

Unfortunately none of those worked for me, so I build my own:

function mymodule_views_pre_render(&$view) {
  $query = $view->build_info['query'];
  
  $sql = (string) $query;
  $args = array_reverse($query->getArguments());
  $sql = str_replace('{', '', $sql);
  $sql = str_replace('}', '', $sql);
  
  if ($args !== NULL && is_array($args) && count($args)) {
    $sql = vsprintf($sql, $args);
  }
  
  if (count($args)) {
    foreach ($args as $key => $arg) {
      if (is_string($key) && $key[0] == ':') {
        switch (gettype($arg)) {
          case 'string':
            $sql = str_replace($key, '"' . $arg . '"', $sql);
            break;
          case 'integer':
            $sql = str_replace($key, $arg, $sql);
            break;
          case 'double':
            $sql = str_replace($key, $arg, $sql);
            break;
          case 'boolean':
            $sql = str_replace($key, $arg ? '1' : 0, $sql);
            break;
        
        }
      
      }
    }
  }
  d($sql);
}

The d($sql) is a custom function of my sandbox debug module. All it does is print the given string into a log file. (I know, there is the devel module, but sometimes I just prefer my own solutions ...)

The final output is a complete SQL query string with placeholders replaced by the actual filter values and the curly braces removed. Note that this only works in setups where no database prefix is used.

The output can be directly copy-pasted into my database gui or used as-is on the command line.

This should work with all SelectQuery objects, so I have included that into my debug module so that now I can simply use the following to see what views is doing:

function mymodule_views_pre_render(&$view) {
  dquery($view->build_info['query']);
}

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web