How To Work Field Collections Into A Legacy Drupal Architecture

James Rutherford
Director of Client Services
Jan
08
2013

How To Work Field Collections Into A Legacy Drupal Architecture

Field Collections

A common data architecture in pre Drupal 7 websites is the use of a separate content type to represent groups of fields that can have multiple values in an original main content type. For example, you might have a content type called Portfolio Gallery. Galleries can have multiple images, and each image field needs a couple of text fields to provide additional image data(photographer name, location etc).

This example would be created with a Portfolio Gallery content type and a Portfolio Image content type, where Portfolio Galleries can reference several Portfolio Images. This kind of approach works but can result in a convoluted content creation process and can require a lot of extra contrib modules to streamline UI, as well as advanced views and theming techniques to get the displays working properly.

As Drupal 6 reaches the end of its life cycle, more and more sites with a data architecture similar to our example are being migrated to Drupal 7. This represents an excellent opportunity to eliminate this architecture, and take advantage of Drupal 7’s capabilities to improve on it.

Enter Field Collections. The Field Collection module provides us with a new entity that solves a lot of the issues that we faced in our example. It works pretty much as you would expect by the name, allowing us to define collections of fields and make them available in content types or even other field collections. Field Collections solve the problem of representing groups of fields that have multiple values in a single piece of content, and is the successor to the CCK3 Multigroup module. Implementing this in our previous example, we would create a Gallery field collection comprised of a title and gallery description and containing another field collection comprised of our actual images and their extra fields. Then we would add the Gallery field collection as a field in our Gallery content type. There are a number of advantages to this approach when compared to just keeping the old node reference.

  • Users are able to easily create multiple ‘Portfolio Images’ inside of the actual Gallery they are working with.
  • Field collections tie in nicely with Display Suite, Views and have a number of excellent supporting contrib modules like Field Slideshow.

If you decide that moving to Field Collections is the correct architecture for your project, then you have a couple of ways to achieve your goal. Keep in mind that Field Collections can be easily implemented into new or existing architectures and the following tutorial is just for helping to improve legacy architectures that match the example above.

The methodology for changing your architecture will be determined by the type of upgrade you are doing. If you are using the migration method (exporting all of your content data and then importing it into a rebuilt Drupal 7 version of your site) then the Migrate Module and Migrate Extras will allow you to create field collections when you import the data back into the system.

If you are doing a normal system upgrade from Drupal 6 to Drupal 7, then you can utilize the Entity API module and some custom code to move the data from the old content types to the new content types. This is executed after your content types have been upgraded to Drupal 7, and Field Collections and Entity API have been installed.

First you will want to setup a Field Collection to mimic the content type you are replacing. Once this has been completed you can add the Field Collection to the content type containing the node reference you are replacing and we will be ready to run our script.

I like to use Drush in order to run these kinds of scripts and I would suggest you set this up as a custom Drush command.

For our example I am going to highlight the Entity API functionality that makes this possible. In this example we load in all of the example Portfolio Image nodes, determine there referenced Portfolio Gallery Node, and then add them as Field Collection entity to the Portfolio Gallery Node. Our example below is setup as a Drush command. Here's more information on creating your own Drush commands.

function convert_to_field_collection() {
// Get all previous portfolios and process into field collection entities

$query  = "Select nid from node where type = 'portfolio_image'";

 $result = db_query($query);
 

foreach ($result as $record) {

// load in portfolio image node to be converted into Field Collection

$image_node = node_load($record->nid);


/* load in original Portfolio Gallery Node that was referenced

we do this so we can associate our new field collection
 with the correct Gallery node */
$article_portfolio_nid =
field_get_items('node', $image_node, 'field_gallery_portfolio_article');

$image_body = field_get_items('node', $image_node, 'body');


$gallery_node = node_load($article_portfolio_nid[0]['nid']);

// Create array to hold all the values for a new Field Collection entity.

 $values = array();


 // field name is the machine name of the Field Collection created
// earlier to hold images

$values['field_name'] = 'field_portfolio';

$values['field_portfolio_title'][LANGUAGE_NONE][0]['value'] =
$image_node->title;

$values['field_portfolio_description'][LANGUAGE_NONE][0]['value'] =
$image_body[0]['value'];

   

// Create and save field collection to node
   
$fc_gallery = entity_create('field_collection_item', $values);
   
$fc_gallery->setHostEntity('node', $host_node); 
   
$fc_gallery->save();



/* Now that we have a parent field collection
we can create 
individual collections
for each image and its sub fields and assign them */


foreach($node->field_gallery_images[LANGUAGE_NONE] as $image) {
   
// create gallery images, assign to gallery and save
   
$file_drupal_path = $image['uri'];

if(file_exists($file_drupal_path)) {
     
$file = array(
       
'fid' => $image['fid'],
        
'alt' => $image['alt'],
       
'title' => $image['title'],
       
'filename' => basename($file_drupal_path),
       
'uri' => $file_drupal_path,
        
'filemime' => file_get_mimetype($file_drupal_path),
       
'filesize' => filesize($file_drupal_path),
       
'uid' => 1,
        
'status' => 1,
        
'origname' => basename($file_drupal_path),
        
'type' => 'image',
        
'timestamp'=> time()

);



$values = array();
   
$values['field_name'] = 'field_gal_images_collection';
// field collection name
   
$values['field_gallery_image'][LANGUAGE_NONE][0] = $file;


// Create image field collection entity,
// assign to parent collection and save

$fc_gallery_image = entity_create('field_collection_item', $values);

$fc_gallery_image->setHostEntity('field_collection_item', $fc_gallery);
$fc_gallery_image->save();
} // end if
} // end foreach image
} // end foreach result
} // end function

 

Now we have successfully migrated all of our legacy content type data to its new field collection home.  As illustrated in our example, making the switch to field collections is fairly painless and I would recommend this method if you determine that field collections would be a better architecture for your newly upgraded Drupal 7 site.

Additional Resources

Upgrading to Drupal 7

A Business Case for Features

How To Quickly Change Your Drupal Homepage

 

 

comments powered by Disqus