Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Jun 11 2014
Jun 11

This is a quick post regarding the sort order of option elements in the Drupal Commerce Add to Cart form as part of Product Reference fields.

I was confused as to how this was sorting. It does not sort by the Product title. It does not sort by the Product entity identifier. On my development site, the options seemed to be sorting by SKU.

There were a couple options to look into:

1. hook_form_alter().

I initially decided to give up and go the custom code route disparagingly. Although this option did in fact allow me to change the sort order to an arbitrary one, the Add to Cart form had already loaded the Product price of the default value meaning that the wrong price was displayed for the Product on initial page load.

In order to change that I’d have to write even more custom code.

Not good…

2. Change the SKU.

I found that the list was sorted by SKU. However this was not an option because changing SKUs will mess with history. And though I tried this option, it did not change the sort order. This might have worked given the actual issue below.

Not good…

The actual issue

The product list is stored as part of a Product Reference field, which can be a multi-value field. The product ids are ordered by field “delta” or in other words the ordinal in which they were stored.

The Select List field widget (and Checklist field widget) stores multi-value field items in the order of the options. So if initially the Select List grabbed the order by SKU, then that’s the storage order.

Commerce also provides an Autocomplete field widget. Field items are assigned their delta left-to-right. The Autocomplete field widget would allow sort ordering of Commerce Products.

If Product Reference field were an Entity Reference field, then select lists could pull an ordered list from a view or sort by a particular entity property or field.

Matthew Radcliffe is a Drupal developer at Kosada, Inc. and contributor to the Drupal project including Drupal core, contributed projects, development environments and the Contribution Mentoring program among other things.

Feb 19 2013
Feb 19

At times I am confused by behavior in the powerful Rules module. Sometimes Rules data selectors for entities have their fields listed and sometimes they do not. I did not find documentation about this behavior.

So I dug through code… I thought that perhaps the following would provide a variable of an entity_type:

  'provides' => array(
    'my_variable' => array(
      'type' => 'my_entity_type',
      'label' => t('My entity type'),
    ),
  ),

However if the entity type provides bundle support, then the bundle must be explicitly defined beforehand. It does not look like it is possible for Rules to dynamically load field properties based on the returned data. This makes sense because without a known bundle, then Rules would need to load all possible fields as possible data selectors. This would be confusing to users when they would try one, and it just wouldn’t work.

I found an example of the peculiar way in which this works in rules/modules/entity.eval.inc:rules_action_entity_create_info_alter(). A rules action info_alter hook is defined in the above documentation page about providing variables. This info_alter hook extends the configuration of the action as defined in rules/modules/entity.rules.inc:rules_entity_action_info().

The alter_info implementation adds an additional parameter for the bundle dynamically based on the entity type, and then tells what bundle to use based on that configuration. The important idea to take away is that you should define a bundle parameter in your action info:

  'bundle' => array(
    'type' => 'my_bundle_entity_type', // see Entity API
    'label' => t('Bundle'),
    'description' => t('Set the bundle'),
  ),

…and then implement an info_alter function for that action that sets the bundle parameter to the provided variable:

function my_module_action_info_alter(&$element_info, RulesAbstractPlugin $element) {
  $element_info['provides']['bundle'] = $element->settings['bundle'];
}

The action just needs to return the entity metadata wrapper of the loaded entity.

Matthew Radcliffe is a Drupal developer at Kosada, Inc. and contributor to the Drupal project including Drupal core, contributed projects, development environments and the Contribution Mentoring program among other things.

Dec 22 2011
Dec 22

The Chaos tool suite (ctools) is collection of useful methods intended to help Drupal developers create complex interfaces without duplicating too much effort.

Drupal 7 introduced a new Form API element: Vertical Tabs. When you set a parent element as this Form API type, then any child elements that are of type fieldset are displayed as vertical tabs instead of as in the traditional fieldset. Unfortunately for us developers, the documentation does not provide an example of how to use Vertical Tabs properly, but with some trial and error you can do it.

However, this new Vertical Tab Form API element doesn’t work in modal multistep wizard forms created with ctools. If you look at the theme_vertical_tabs() method, you will notice it loads a Javascript library to do its magic.

This library won’t be loaded properly in a modal dialog. Instead you need to call that same drupal_add_library() method in your ctools form.

function blah_form($js, $step) {
 
  drupal_add_library('system', 'drupal.vertical-tabs');  
 
  // Other stuff...  The ctools_ajax_example module is a decent example of this part. 
}

and then you can use vertical tabs in your other form callbacks!

function blah_step1($form, &$form_state) {
  $form['blah'] = array(
    '#type' => 'vertical_tabs',
    '#tree' => TRUE,
  );
 
  for ($i = 0; $i < 10; $i++) {
    $form['blah'][$i] = array(
      '#type' => 'fieldset',
      '#title' => t('Blah !n', array('!n' => $i),
      '#group' => 'blah',
      '#tree' => TRUE,
    );
 
    $form['blah'][$i]['blahblah'] = array(
      '#type' => 'radios',
      '#title' => t('Blah Blah?'),
      '#options' => array(0 => t('No'), 1 => t('Yes'), 2 => t('Maybe')),
      '#default_value' => 0, 
    );
  }
 
  return $form;
}

Update 1/4/2012: This method is incomplete for Internet Explorer. Internet Explorer does not allow you to inject CSS into the DOM directly. This can be averted by using the addImport method, which only Internet Explorer supports. This is addressed in Lazy-loading CSS fails in IE issue on drupal.org so hopefully you will not need to do the following for much longer in the future.

  drupal_add_js('
    if (document.styleSheets[0].addImport) {
      document.styleSheets[0].addImport(' . DRUPAL_ROOT . '/misc/vertical_tabs.css);
    }
  ', 'inline');

Matthew Radcliffe is a Drupal developer at Kosada, Inc. and contributor to the Drupal project including Drupal core, contributed projects, development environments and the Contribution Mentoring program among other things.

Jun 18 2011
Jun 18

In not-so-recent news, Drupal migrated the drupal.org project repository from CVS to Git in late February, 2011. My experience with Git previous to this has been as a user fetching source code of x.org, mesa, dri, and other various Linux and Unix projects.

As a user, not a developer, I found the Git work flow confusing. I did not need to make local commits and it was hard to wrap my head around bringing in changes from multiple remote repositories.

As a PHP developer, I first grasped the work flow at Columbus GiveCamp 2010 when I needed to make a github account for the branch of the Audio module we hacked on. As well, some Ruby guy had a nice Git work flow diagram, which I promptly forgot all about until the not-so-recent event above. In recent news, I have been hacking on Drupal 7 versions of several modules.

In this particular case, the module had a Drupal 7 branch already, but the code was so out of date that I branched from the latest Drupal 6 changes (think 8kb v 32kb diffs). Eventually I ended up with a nice local branch, 7.x-3.x, and the module maintainer requested that I branch from origin/7.x-3.x instead of origin/6.x-3.x.

In either case, I thought, after reading man git and other manual pages, that I could simply do the following command to generate community friendly Git patches.

git format-patch origin/7.x-3.x..7.x-3.x
git checkout -b 7.x-fresh origin/7.x-3.x
git apply (17 patches)

Fail. It also failed when I made patches from origin/6.x-3.x.

Unfortunately, it seems that Git can create patches that it can’t actually apply

Wait. What. Did anyone else think this was a dumb idea before it was implemented?

If you’re not part of the main development team and just want to contribute some code these become headaches. You still want credit for your work (i.e. commit history).

I thought that I would have to make a community-unfriendly sentiment such as this:

git diff origin/7.x-3.x..7.x-3.x > blah.patch
git checkout -b 7.x-3.x-origin origin/7.x-3.x
patch -p1 < blah.patch
git add (new files to add)
git commit -a (comment git sucks)
git format-patch (commit)
git checkout -b 7.x-3.x-fresh origin/7.x-3.x
git apply (formatted-patch)

I have been very timid about the rebase feature because the documentation on what rebase actually does is confusing. That is, it’s more confusing than the rest of Git documentation. Do you keep HEAD changes or do you revert to the patch’s change?

I decided to give up. However, I knew it was not not possible because every day I get spams from mailing lists spamming patches via git format-patch. So a few minutes after giving up, I tried again. I thought I went through rebasing correctly, but I did not commit with each merge conflict.

Not not possible

The work flow ended up a bit more like this:

git checkout 7.x-3.x
git rebase origin/7.x-3.x      (because 7.x-3.x is a branch off of 6.x-3.x)
(fix merge conflicts and choose HEAD)
git add (file)
git commit
git rebase --continue           (or --skip when told to)
(go to fix merge conflicts until done)
git format-patch (commit in origin/7.x-3.x to diff from)
git checkout -b 7.x-3.x-fresh origin/7.x-3.x
git apply (patches)

Success! And the git log history looks cleaner.

Hindsight

In hindsight I probably should not have originally branched from 6.x-3.x. Instead I should have rebased 7.x-3.x to 6.x-3.x and then re-ported to Drupal 7.

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