Showing posts with label query. Show all posts
Showing posts with label query. Show all posts

Thursday, 11 July 2013

Export UI, Features and Taxonomy

Here's a quicky. Let's say you've created some exportable content (using CTools) which references a term ID and you have to go from your dev site to the production site. And your taxonomy is also being exported using UUID.

Somehow you have to tie them together because sure as eggs is eggs the term IDs created on the production site are not going to be the same as the local ones. That's why you used UUID in the first place. Right?

Here's what you do: in your local exportable you will have a column for the term ID so include a column for the term UUID as well.

In your add/edit form for the exportable you'll have to include some code to automatically add the selected term's UUID - I did it in the form validation. Basically you read the selected term ID, load the selected term which will have the UUID in it (because it's added to the base table). Set the term UUID value in $form_state['values']. Assuming you're using CTools Export UI the UUID will be saved automatically.

Also, in the module install, add "no export" => TRUE to the TID field, so that Export UI does not include it in the feature. (This code works for Features, it doesn't work for single imports, I'll leave that as an exercise for the reader - hint: you can specify an "import callback".)

That's the easy bit, when you export your content it will be saved with the term's UUID and not the term's ID. The difficult bit is how to link the UUID of exported content when Features loads it into the new site.

Except it's not hard at all. In your export specification, in the schema, you have the "default hook", well CTools Export UI very kindly calls an drupal_alter() on the default items after it's loaded them. So we can do this:

/**
 * Implements hook_DEFAULT_HOOK_alter().
 *
 * This intercepts any defaults picked up from code and converts
 * their UUID category into the local TID (which might be different
 * on every site).
 *
 */
function mymodule_my_default_hook_alter(&$items) {
  $uuids = db_select('taxonomy_term_data', 't')
      ->fields('t', array('uuid', 'tid'))
      ->execute()->fetchAllKeyed();

  foreach ($items as $item) {
    if (empty($item->tid) && !empty($uuids[$item->uuid])) {
      $item->tid= $uuids[$item->uuid];
    }
  }
}

The database call creates an array which maps all UUIDs to TIDs in one go. If your site uses a lot of taxonomy terms - perhaps you have user tagging - you might want to restrict this call to a specific vocabulary.

The exact item property names will depend on what you set up in your schema.

Sorted.

Wednesday, 4 May 2011

Search Query bug

I've just issued a bug report and patch for a problem with the Search Query class. It's not a huge bug but it prevented me from doing something I needed to do: add some restrictions to a Search Query.

There are two ways of modifying queries in Drupal 7, you can either use a generic "hook_query_alter()" or use "hook_query_TAG_alter()". Tags are a way of telling other modules what a query is about. For example you can have a tag "node_access" which allows modules to add additional restrictions on the query, in this case the Node module connects the query to node_grants.

The Search Query adds a "search_node" or "search_user" tag, unfortunately it adds the tag after the hooks have been called - which is as useful as a chocolate teapot. So I added the patch here: http://drupal.org/node/1146564 which simply moves the place where the tags are added.

Another thing to watch out for: the generic hook_query_alter() is not called on queries that have no tags. This is an efficiency thing as it was discovered early on that this hook was called a lot for no good reason during bootstrap.

Tuesday, 19 April 2011

Conditions for using where

Just to help you avoid spending as many hours as I have trying to force a nasty little query with a subquery to do what I wanted...

If you want to have a WHERE clause that contains something like "fss.entity_id = n.nid", you can't use the condition function. In other words, you can't do this:

$query->condition('fss.entity_id', 'n.nid');

You have to use this:

$query->where('fss.entity_id = n.nid');

Because the first version gets translated into fss.entity_id = 'n.nid' in the SQL, which will never be true.

In debugging this it slowly became obvious that something fundamental was wrong - because when I output the $query string and then replaced the placeholders with the right arguments, and shoved it straight into the MySQL server - it worked.

Eventually it dawned on me to use the Devel query output to see what was actually being called and found the error - though by that time I was looking for it, having exhausted every other option.

Here's a quick tip for outputting the entire contents of an object which contains public and private properties:

dpm(print_r($object, TRUE));

I could have done without that today.