I've created a subscriber to the SalesforceEvents::PULL_PRESAVE event.

When an entity is being inserted for the first time, there does not seem to be an entity available for modification in the SalesforcePullEvent parameter passed into the function.

So in this class:

/**
 * Class SfdcPullSubscriber
 *
 * @package Drupal\gb_sfdc\EventSubscriber
 */
class SfdcPullSubscriber implements EventSubscriberInterface {

  /**
   * @param \Drupal\salesforce_mapping\SalesforcePullEvent $event
   */
  public function pullEntityPresaveAlter(SalesforcePullEvent $event) {

    /** @var MappedObject $mapped_object */
    $mapped_object = $event->getMappedObject();

    /** @var SObject $sf */
    $sf = $mapped_object->getSalesforceRecord();

    /** @var Entity $entity */
    $entity = $event->getEntity();
  }

  /** TRUNCATED **/
}

On an insert, $entity is NULL. On an update, it will be a Drupal entity as expected.

Comments

chrisrikli created an issue. See original summary.

chrisrikli’s picture

I believe this is happening because the contructor for SalesForcePullEvent calls the getMappedEntity() method of MappedObject.

  public function __construct(MappedObjectInterface $mapped_object, $op) {
    $this->mapped_object = $mapped_object;
    $this->entity = $mapped_object->getMappedEntity();
    $this->mapping = $mapped_object->getMapping();
    $this->op = $op;
  }

But the getMappedEntity() method presumes that there is an entity_id, which there wouldn't be on an insert as $entity->save() hasn't yet been executed:

  /**
   * @return EntityInterface
   */
  public function getMappedEntity() {
    $entity_id = $this->entity_id->value;
    $entity_type_id = $this->entity_type_id->value;
    return $this
      ->entityManager()
      ->getStorage($entity_type_id)->load($entity_id);
  }

However, we know that the drupal_entity member variable gets populated by the foreach that loops over the fields in the pull method of MappedObject. So I've modified the getMappedEntity() function as follows. I don't know if this is the best way to do this or I'd submit a patch with the changes.

  /**
   * @return EntityInterface
   */
  public function getMappedEntity() {
    $entity_id = $this->entity_id->value;
    if(!$entity_id) {
      // this is a new entity...
      return $this->drupal_entity;
    }
    else {
      $entity_type_id = $this->entity_type_id->value;
      return $this
        ->entityTypeManager()
        ->getStorage($entity_type_id)->load($entity_id);
  }
aaronbauman’s picture

Looks like a reasonable approach to me.
Please submit a patch, and we'll see if testbot likes it.

chrisrikli’s picture

Will do Aaron. I'll post it in this thread however there are also some other changes I've made.

aaronbauman’s picture

Great, thank you for your contributions.

Patches are more than welcome - we're actively working on better test coverage, porting existing D7 stuff, and adding some new features for D8. I'll try to get them reviewed quickly.

chrisrikli’s picture

StatusFileSize
new7.96 KB

Aaron, attached is a patch that does several things:

  1. Integrates the aforementioned tweak to the getMappedEntity() function
  2. A PULL_POSTSAVE mechanism. I realize the hook_entitiy_update and hook_entity_insert could fulfill this role however there are instances where I only want to act on an entity that's been created via a salesforce_pull. For example, we're using this module to create commerce_product and commerce_product_variation entities. After a commerce_product_variation has been created, we need to add that variation to an appropriate product. But I do not want this firing every time a commerce_product_variation entity is created, only when one has been created via salesforce_pull.
  3. A little cleanup on the example Subscriber. I don't know if it would actually run, I didn't test it. Mainly I just resolved issues that PHPStorm was squawking about
aaronbauman’s picture

Status: Active » Needs review
StatusFileSize
new7.96 KB

I think we have some test coverage for this.
Let's see what testbot says.

Status: Needs review » Needs work

The last submitted patch, 7: salesforce-pull_presave_entity_missing-2855086.patch, failed testing.

aaronbauman’s picture

Status: Needs work » Needs review

3 points:
1. You should be able to use Mapped Object entity save event, coupled with $mapped_object->drupal_entity->isNew() to achieve equivalent functionality to PULL_POSTSAVE. Maybe we can include documentation to explicitly point contrib to this, and/or an example in salesforce_example module?

2. what's with SalesforceMapping::getIterator ? is this cruft from the other work you mentioned? I don't see this used anywhere in the patch.

3. Please re-roll against latest dev. The example subscriber has been updated, which is preventing the patch from applying.

aaronbauman’s picture

Status: Needs review » Needs work
chrisrikli’s picture

1. So you're saying used a hook_entity_update/hook_entity_insert to look for the MappedObject entity save and react to that? A couple things I like about having a SubscriberEvent for PULL_POSTSAVE is that it keeps all my code in the same Subscriber file and able to use a Trait I'd developed for other activities. As opposed to having some code in the Subscriber class and some in a .module file.

2. Ah, yes, I forgot to mention that. When I try to use kint() or ksm() to take a look at the object, it was failing due to the lack of a getIterator() method. It never really worked so I'll remove it from the code.

3. Will do.

aaronbauman’s picture

1. re PULL_PRESAVE: take a look at #2551893: Add events for matching entity hooks. While I appreciate your position as a developer, as a maintainer I want to maintain as little code as possible. The more hooks i can eliminate, the better. Another implementation idea: you could use your .module file to emit your own PULL_PRESAVE event, and delegate that back to your subscriber. It's slightly more overhead, but would allow you to encapsulate your logic in the subscriber. And you can ditch the .module altogether when 2551893 lands.

2. i've had no end of issues with kint. There's a poorly documented "max-level" setting that may help you debugging:
http://drupal.stackexchange.com/questions/211928/how-can-i-make-kint-loa...
I've also had some luck using var-dumper instead of kint (no module needed, just install with composer and enable via devel settings):
https://www.drupal.org/project/vardumper

chrisrikli’s picture

StatusFileSize
new3.71 KB

1. Fair enough. I removed the PULL_POSTSAVE modification from the attached patch.

2. Yes, I've got that max level set. Thank you for the steer to vardumper.

Attached patch has:

  1. SalesforceExampleSubscriber.php: needed a few 'use' statements for the example functions.
  2. MappedObject.php: update to getMappedEntity that kicked this all off
  3. PullBase.php: there's an attempt to log an Exception outside the scope of a try/catch statement. I couldn't see that an Exception was thrown from deeper function so I moved it inside the catch statement.
  4. salesforce.api.php: very minor issue, PHPStorm want to see empty brackets after a function declaration. This file of course never gets called but...I don't like red squigglies in my IDE> :)
aaronbauman’s picture

Status: Needs work » Needs review
StatusFileSize
new3.71 KB

This looks great to me.
One more run through testbot.

  • aaronbauman committed 7d283ed on 8.x-3.x
    Issue #2855086 by chrisrikli: No entity on insert for a PULL_PRESAVE...
aaronbauman’s picture

Status: Needs review » Fixed

OK, this is in.
Thanks again for the contrib

Status: Fixed » Closed (fixed)

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