Drupal Migrate: Field_Collection Example

Overview

Lately I have been given a small 2-week project for an on campus department. Currently on paper they accept lower-education applications, students in high school, and request references from lower-ed administrators such as Teachers, Counselors and Principals to be delivered to our Campus and the students are vetted; and if their application is gleaming they may attend summer classes on campus in an exciting program!

This program is placing this application program and the Reference submittals online. My job is to make this application — and I turned to Drupal to meet my needs. It provides many tools out of the box to complete this task. Notabely the Webform module and the Migrate module. This project is using Migrate 2.6 and Webform 4.x.

Task

Take a CSV file of applicants (student) info with nested Reference information and present a Webform to be filled out. Track submissions, allow mass emailing and save Submissions to PDFs. Allow new submissions or updates to existing application records over time (via an upload somehow and a fancy import basically).

To meet these needs I wrote Migrations which:

  1. Migrate students data (create or update existing student records)
  2. Migrate N number of references (create or update existing reference records — references relate to a Student)
  3. Be able to track webform Submissions per student and reference.
  4. Be able to take any uploaded changes in the import CSV in steps #1-3 (essentially each migrations) and apply the changes to submitted Webform Submissions.
Steps found in Import/Update Migrations

Steps found in Import/Update Migrations

Having outlined these Migrations. This blog post will focus on step #2 — a (series) of Migration(s) which creates a FieldCollection of References to a Student Application Record (Node).

Implementation Details

In step #1 of this migration we make a simple Drupal Node of a student record. I’ve covered migrating data into Nodes in the past. Now I will cover how to migrate a FieldCollection, or in this case collection items into Collection.

The FieldCollection module (like many community modules) is responsible for maintaining compatibility with the Migrate module. It provides a MigrateDestinationFieldCollection Migrate Field Handler. This allows us to make a Field Collection which in the node edit screen will look something like the following for our student applicant references:

Node with Drupal Field of type field_collection called "references"

Node with Drupal Field of type field_collection called “references”

We define a FieldCollection migration to the student record using the following code:

class StudentReference1Migration extends Migration {
  const DATA_INFILE = 'public://data.csv';
  public function __construct($arguments) {
    ini_set('auto_detect_line_endings', TRUE);
    parent::__construct($arguments);

    $this->description = t('Reference fields migration from CSV source per Student Record for Reference #1.');
    // This must run after student records have been inserted or updated.
    $this->dependencies = array('StudentApplicants');

    $columns = array(
      0 => array('app_status', 'Application Status'),
      1 => array('app_student_first_name', 'First Name'),
      2 => array('app_student_last_name', 'Last Name'),
      3 => array('app_masternumber', 'Master Number'),
      4 => array('app_student_email', 'Email'),
      5 => array('app_student_location' , '"City, State or Country (if international)"'),
      6 => array('app_student_school_name', 'Name of High School you currently attend:'),
      7 => array('app_student_school_url', "Please provide your High School's website address:"),
      8 => array('app_student_grade', 'Current Grade in School'),
      9 => array('app_ref_1_type', 'Type of Reference #1'),
      10 => array('app_ref_1_name', 'Name of Reference'),
      11 => array('app_ref_1_email', 'Email Address of Reference'),
      12 => array('app_ref_2_type', 'Type of Reference # 2'),
      13 => array('app_ref_2_name', 'Name of Reference'),
      14 => array('app_ref_2_email', 'Email Address of Reference'),
      15 => array('app_ref_3_type', 'Type of Reference #3'),
      16 => array('app_ref_3_name', 'Name of Reference'),
      17 => array('app_ref_3_email', 'Email Address of Reference'),
    );

    $fields = array();
    $fp = self::DATA_INFILE;
      // For hashing apart of $options, 3rd argument for MigrateCSV.
      // Track changes in content, so changed data will affect an UPDATE and
      // not an INSERT.
    $options = array(
      'header_rows' => TRUE,
      'track_changes' => 1,
      );

    $this->source = new MigrateSourceCSV($fp, $columns, $options, $fields);
    // Our destination is a FieldCollection the internal machine
    // name of the Field is field_ed_references and its attached
    // to a Drupal entity of type Node.
    $this->destination = new MigrateDestinationFieldCollection(
      'field_ed_references',
      array('host_entity_type' => 'node')
    );

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'app_masternumber' => array(
        'type' => 'varchar',
        'length' => 25,
        'not null' => TRUE,
        'description' => 'Master Number from CSV. Unique ID in Remote system.'
        ),
      ),
      MigrateDestinationFieldCollection::getKeySchema()
    );

    // This says the host_entity_id is whatever "app_masternumber" resolved to in
    // the StudentApplicants migration, eg a Drupal Node.
    $this->addFieldMapping('host_entity_id', 'app_masternumber')->sourceMigration('StudentApplicants');

    $system_user = user_load_by_name('MyProjectSystemUser');
    // Dest -> Source.
    $this->addFieldMapping('uid')->defaultValue($system_user->uid);
    $this->addFieldMapping('field_name', 'app_ref_1_name');
    $this->addFieldMapping('field_type', 'app_ref_1_type');
    $this->addFieldMapping('field_email', 'app_ref_1_email');
  }

Noteworthy Code Details

This Field takes a host_entity_type in its Constructor. This tells the FieldCollection what type of Drupal data it is attached to. Later, we define the mapping to the Drupal content ID:

$this->addFieldMapping('host_entity_id', 'app_masternumber')->sourceMigration('StudentApplicants');

This line of code says the Drupal ID is whatever the ID was from the previous migration for this row of the CSV file. Every student record has a masternumber — we reverse lookup the value from the previous migration which is conveniently saved off by Migrate across migrations! (we don’t have to write SQL to say “what ID in Drupal is the 3rd student in this CSV file?”)

Also, the MigrateSQLMap configuration for this FieldCollection basically says “these 3 CSV columns are related to the 1st column “app_masternumber” found in the CSV. This is used by Migrate to track inserts or updates.

I hope this small example of a FieldCollection migration was useful! This is 1 item in a field collection. This project uses 3 migrations outlined above to migrate 3 sets of nested row content from the source row in a CSV file. All 3 globs of reference data build 1 FieldCollection object attached to our node.

Looking for quality Drupal Web Hosting? Look no further than Arvixe Web Hosting!

Tags: , , , , , , , , , , | Posted under Drupal | RSS 2.0

Author Spotlight

David Gurba

David Gurba

I am a web programmer currently employed at UCSB. I have been developing web applications professionally for 8+ years now. For the last 5 years I’ve been actively developing websites primarily in PHP using Drupal. I have experience using LAMP and developing data driven websites for clients in aviation, higher education and e-commerce. If you’d like to contact me I can be reached at david.gurba@arvixe.com

Leave a Reply

Your email address will not be published. Required fields are marked *


× 6 = 42

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>