I have implemented

MyMigration(XMLMigration)::prepare() which is setting a couple of things in the node object depending on the data that is being imported, however

plugins/node.inc line 195 calls plugins/destinations/entity.inc, entity.inc executes $migration->prepare() but the results of that are never visible/available to that initial call on plugins/node.inc line 195

  /**
   * Give handlers a shot at modifying the object before saving it.
   *
   * @param $entity
   *  Entity object to build. Prefilled with any fields mapped in the Migration.
   * @param $source_row
   *  Raw source data object - passed through to prepare handlers.
   */
  public function prepare($entity, stdClass $source_row) {
  ...
    // Then call any prepare handler for this specific Migration.
    if (method_exists($migration, 'prepare')) {
      $migration->prepare($entity, $source_row);
    }
   ...

which means anything i modify in my prepare() statement is ignored, NOTE: I am running this as an 'update'

ideas?

Comments

dgtlmoon’s picture

Forcing a reference seems to fix my issues, might be a clue in here somewhere..

(partial patch here just to show the issue)

+++ b/docroot/profiles/my_profile/modules/contrib/migrate/plugins/destinations/entity.inc
@@ -121,7 +121,7 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
    * @param $source_row
    *  Raw source data object - passed through to prepare handlers.
    */
-  public function prepare($entity, stdClass $source_row) {
+  public function prepare(&$entity, stdClass $source_row) {
     // Add source keys for debugging and identification of migrated data by hooks.
     /* TODO: Restore
     foreach ($migration->sourceKeyMap() as $field_name => $key_name) {
@@ -142,7 +142,7 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
     if (method_exists($migration, 'prepare')) {
       $migration->prepare($entity, $source_row);
     }
-  }
+   }

as well as using a reference in my prepare() of my implementation

mikeryan’s picture

Category: bug » support
Status: Active » Postponed (maintainer needs more info)

Adding an & should not make any difference - objects are effectively passed by reference in PHP (you would only need an & if you were trying to change $node itself, e.g. $node = new stdClass - not if you're changing the contents of the node e.g. $node->title).

How are you confirming that your changes are made in $migration->prepare() but are not present after the call to $this->prepare() in MigrateDestinationNode::import()?

dgtlmoon’s picture

Because if i set a breakpoint in my debugger when the call to ->prepare() returns the variables arent being set the node, and as a result, taxonomy terms arent being saved that i'm specifically setting in my prepare()

also the 'vague patch' supplied makes the import work, without it, it doesnt work

mikeryan’s picture

What does your migration prepare() handler look like? Are you by any chance assigning directly to $node itself, instead of just modifying fields within $node? That's the only scenario I can imagine where making the parameter a reference would make it work for you.

To test, I added this to BeerNodeMigration in migration_example's beer.inc:

  public function prepare($node, $row) {
    $node->foobar = "hello there";
  }

When I do drush_print_r($node) after the call to prepare() in node.inc, the foobar field is there.

dgtlmoon’s picture

What if i wanted to do some string fudging like

 $node->body[LANGUAGE_NONE][0]['value'] = str_replace(...);

should this be done in `prepare` ? doesnt seem to work for me

mikeryan’s picture

That should work - have you tried a drush_print_r($node) after the call to prepare() in node.inc to confirm it's not changing?

mikeryan’s picture

Status: Postponed (maintainer needs more info) » Closed (cannot reproduce)

No further information provided.

dgtlmoon’s picture

OK great, figured it out, I was confusing prepare() with prepareRow(), sorry about that!

cameron tod’s picture

Status: Closed (cannot reproduce) » Needs review
StatusFileSize
new1.02 KB

In migrating some quiz nodes from Drupal 6 to Drupal 7, I experienced this very thing. Quiz needs some fields set on the node before node_save(), or it will freak out about some columns being null.

After lots of debugging, and reading this issue, it seems that changing the $entity object is not permitted. I can change its values, but not its members.

So, adding a & to $entity in MigrateDestinationEntity::prepare() and in my local Migration class seems to fix it right up. I can now add fields to the node object before it is saved.

Here's how my migration is set up:

class MayaQuizMultichoiceMigration extends DrupalNode6Migration {

  // snipped all the construct stuff

/**
   * Makes changes the node object just before it is saved.
   *
   * Called from MigrationEntity::prepare().
   *
   * @see MultiChoiceQuestion::saveNodeProperties()
   */
  function prepare(&$entity, $source_row) {
    $properties = $this->getMultichoiceProperties($source_row->nid, $source_row->vid);
    $entity = (object) array_merge((array) $entity, (array) $properties);
  }

  function getMultichoiceProperties($nid, $vid) {
    $query = DataBase::getConnection('default', $this->sourceConnection)
      ->select('quiz_multichoice_properties', 'p')
      ->fields('p', array('choice_multi', 'choice_random', 'choice_boolean'))
      ->condition('p.nid', $nid)
      ->condition('p.vid', $vid);
    return $query->execute()->fetchObject();
  }

}

Drupal6NodeMigration comes from drupal_to_drupal_data_migration, and implements MigrateDestinationNode.

I've attached a patch, hopefully it is useful to someone.

Status: Needs review » Needs work

The last submitted patch, migrate-prepare_entity_by_reference-1530744-9.patch, failed testing.

mikeryan’s picture

Status: Needs work » Postponed (maintainer needs more info)

The problem is that you are changing the $entity variable itself, not its contents:

  function prepare(&$entity, $source_row) {
    $properties = $this->getMultichoiceProperties($source_row->nid, $source_row->vid);
    $entity = (object) array_merge((array) $entity, (array) $properties);
  }

This is the proper way to update the object:

  function prepare($entity, $source_row) {
    $properties = $this->getMultichoiceProperties($source_row->nid, $source_row->vid);
    foreach ($properties as $key => $value) {
      $entity->$key = $value;
    }
  }
cameron tod’s picture

Wicked, thanks for that tip.

mikeryan’s picture

Status: Postponed (maintainer needs more info) » Fixed

Status: Fixed » Closed (fixed)

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