Download & Extend

Add complete() handler to MigrateFieldsEntityHandler

Project:Migrate
Version:7.x-2.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:mikeryan
Status:closed (fixed)

Issue Summary

Hi!

Is there a method where I can do something after a row has been created. Like post_row_creation(), similar to prepare_row()?

I am still working at a field_colletion handler and would like to call the field collection create methods. But this is only working if I can provide a parentEntityID which I don't have in prepare_row.

Regards
marcus

Comments

#1

Status:active» postponed (maintainer needs more info)

Do you mean after an entity is created? When Migrate speaks of "row", it's talking about a row of source data retrieved from the source - prepareRow() is called after the data is retrieved but before any other handlers are called. Just before an entity is saved (e.g., node_save() is called) various prepare() handlers are called - first those registered to work with 'Entity', then those registered for the particular entity type ('node', 'user', etc.), then finally the migration's prepare() method. After the entity is saved, complete handlers are called.

I don't know how field_collection is constructed, but I would expect it to be similar to other field modules - look at plugins/destinations/fields.inc, or in migrate_extras look at date.inc, to see how they work.

#2

Status:postponed (maintainer needs more info)» active

If I speak from row I mean a row in the nodes table (because I am migrating nodes at the moment).

But let me clear things up.

Instead of "after row is created" I should say after a new destination node is created and therefore a node id to this node is existing.

Why is this important in this case.
A field collection is an entity itself. The field_collection_field in a node keeps only the reference to this entity_id.
You can think of it a little bit like a node reference field.

But with one important difference. The field_collection module takes care of keeping the integrity of the node reference. If I try to create an field_collection_item in code

<?php
  
// Manually create a field_collection.
   
$entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
   
$entity->setHostEntity('node', $node);
   
$entity->save();
?>

I need to set theHostEntity. But I can't do it without the node id I don't have during migrate is preparing the row.

Maybe it is somehow possible to create the nodes first. And than running the migratione again as an update.
Is it possible to get the nodeid from the mapping tables via the API?

But all this wouldn't be neccessary if I could run an entity_create() after migrate has finished one "source row" of migration and the destination object has been created.

As field_collection module takes care of the integrity of the reference even a rollback wouldn't be a problem. If a node with a field collection in it is deleted, the referenced field_collection data is deleted automatically as well.

#3

So you should be able to implement a complete handler. Take one of the existing field handlers as a model, replacing the prepare() function with a complete() function. Ahhh, but I see MigrateFieldsEntityHandler has a prepare() but not a complete(), it looks like we need to fill that hole for you...

#4

That would be awesome.

So a complete() function would be called after the entiy is created, would than look like this...

<?php
// Define a way to migrate the date field.
class MigrateSomeFieldHandler extends MigrateFieldHandler {
 
// Define the constructor of our Handler - it lets Migrate know what *types* of fields this handler is compatible against.
 
public function __construct() {
   
// ...
 
}

 
// Define our arguments on where this data will be found during the preparation.
 
static function arguments($start_date = NULL, $end_date = NULL, $rrule = NULL, $separator = NULL) {
   
// ...
 
}

 
// Based on the entity and return an array of field values to get processed.
 
public function prepare(stdClass $entity, array $field_info, array $instance, array $values) {
   
// ...
 
}

// Based on the entity
 
public function complete(stdClass $entity, array $field_info, array $instance, array $values) {
   
// THINGS HERE WOULD HAPPEN AFTER ENTITY THIS FIELD BELONGS TO HAS BEEN CREATED
 
}
}
?>

#5

Title:Is there a function where I can do something after a row has been created?» Add complete() handler to MigrateFieldsEntityHandler
Category:support request» feature request
Assigned to:Anonymous» mikeryan

#6

Title:Add complete() handler to MigrateFieldsEntityHandler» Is there a function where I can do something after a row has been created?
Category:feature request» support request
Assigned to:mikeryan» Anonymous

Can I copy the complete handler from the prepare handler in MigrateFieldsEntityHandler?

Like this:

<?php
public function complete($entity, stdClass $row) {
   
migrate_instrument_start('MigrateDestinationEntity->completeFields');
   
// Look for Field API fields attached to this destination and handle appropriately
   
$migration = Migration::currentMigration();
   
$destination = $migration->getDestination();
   
$entity_type = $destination->getEntityType();
   
$bundle = $destination->getBundle();
   
$instances = field_info_instances($entity_type, $bundle);
    foreach (
$instances as $machine_name => $instance) {
      if (
property_exists($entity, $machine_name)) {
       
// Normalize to an array
       
if (!is_array($entity->$machine_name)) {
         
$entity->$machine_name = array($entity->$machine_name);
        }
       
$field_info = field_info_field($machine_name);
       
$entity->$machine_name = migrate_field_handler_invoke_all($entity, $field_info,
         
$instance, $entity->$machine_name);
      }
    }
   
migrate_instrument_stop('MigrateDestinationEntity->completeFields');
  }
?>

Do I get it right that the prepare handler are called in migrate.module at this place:

<?php
/**
* Invoke any available handlers attached to a given field type.
* If any handlers have dependencies defined, they will be invoked after
* the specified handlers.
*
* @param $entity
*  The object we are building up before calling example_save().
* @param $field_info
*  Array of info on the field, from field_info_field().
* @param $instance
*  Array of info in the field instance, from field_info_instances().
* @param $values
*  Array of incoming values, to be transformed into the appropriate structure
*  for the field type.
*/
function migrate_field_handler_invoke_all($entity, array $field_info, array $instance,
    array
$values) {
 
$return = array();
 
$type = $field_info['type'];
 
$class_list = _migrate_class_list('MigrateFieldHandler');
  foreach (
$class_list as $class_name => $handler) {
    if (
$handler->handlesType($type) && method_exists($handler, 'prepare')) {
     
migrate_instrument_start($class_name . '->prepare');
     
$result = call_user_func_array(array($handler, 'prepare'),
        array(
$entity, $field_info, $instance, $values));
     
migrate_instrument_stop($class_name . '->prepare');
      if (isset(
$result) && is_array($result)) {
       
$return = array_merge_recursive($return, $result);
      }
      elseif (isset(
$result)) {
       
$return[] = $result;
      }
    }
  }
  return
$return;
}
?>

But where could a complete handler be called? Or will this happen through some magic I can't find? As in destination.inc you already mention that it may be done.

<?php
/**
* All destination handlers should be derived from MigrateDestinationHandler
*/
abstract class MigrateDestinationHandler extends MigrateHandler {
 
// abstract function arguments(...)
  /**
   * Any one or more of these methods may be implemented
   */
  //abstract public function fields();
  //abstract public function prepare($entity, stdClass $row);
  //abstract public function complete($entity, stdClass $row);
}
?>

Than maybe invoking the complete stuff is already done but I can't find it.

regards
marcus

#7

Uuups! As I was writing this you grabbed the ticket.

Than I will make diff to figure out what has to be done after you commit something and try to learn it from there.

Thanks a lot!

#8

Yes, it's true, migrate_field_handler_invoke_all needs to be extended to support complete as well.

It'll probably be a couple of days before I get to this.

#9

Title:Is there a function where I can do something after a row has been created?» Add complete() handler to MigrateFieldsEntityHandler
Category:support request» feature request
Assigned to:Anonymous» mikeryan

#10

Status:active» fixed

Committed for D6 and D7.

#11

Hi Mike!

Thanks! Works pretty well, but I have a problem with the $values in the complete handler. They are unset somewhere? Is this normal?

I return the values in the prepare function to have them available in the complete function. Is it correct to do it like that or will this call field API functions during prepare which will not work because of the invalid return value?

<?php
   
public function prepare(stdClass $entity, array $field_info, array $instance, array $values) {
   
           return
$values;
    }
   
    public function
complete(stdClass $entity, array $field_info, array $instance, array $values) {
   
       print
"\n\nValues: \n";
      
print_r ($values);  //This returns only something if the values in prepare are returned
   
}
?>

#12

Do not define prepare() unless you're using it to populate the field. The return value of prepare sets the value in the entity (e.g., the return value of the date field handler prepare() will populate $node->field_my_date) - what you're doing will just set that to the incoming value as is without putting things into the proper field form.

I can't explain why you wouldn't see $values set in the complete() handler - did you map anything to that field?

#13

Status:fixed» closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

nobody click here