We need to port the D7 feeds_tamper EFQ Finder plugin to D8 tamper.

CommentFileSizeAuthor
#56 interdiff_50_56.txt11.31 KBericgsmith
#56 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-56.patch11.88 KBericgsmith
#54 interdiff_50_54.txt11.31 KBericgsmith
#54 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-54.patch11.88 KBericgsmith
#50 interdiff_48-50.txt2.26 KBjamesdixon
#50 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-50.patch13.8 KBjamesdixon
#48 interdiff_47-48.txt3.08 KBjamesdixon
#48 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-48.patch13.5 KBjamesdixon
#47 interdiff_42-47.txt3.01 KBjamesdixon
#47 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-47.patch13.06 KBjamesdixon
#42 interdiff-37-42.txt2.37 KBjamesdixon
#42 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-42.patch13.21 KBjamesdixon
#37 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-37.patch12.83 KBzabej
#37 interdiff_33-37.txt4.54 KBzabej
#35 create.png78.31 KBjamesdixon
#35 constructor.png86.06 KBjamesdixon
#35 type-manager.png40.57 KBjamesdixon
#33 interdiff_29-33.txt8.27 KBzabej
#33 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-33.patch10.12 KBzabej
#32 entity_finder_to_D8-2976175-31.patch5.94 KBzabej
#31 Selection_058.png129.03 KBzabej
#31 entity-finder-plugin-2976175-30.patch201 byteszabej
#29 tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-29.patch10.1 KBtbenice
#23 entity-lookup-2976175-23.patch768 bytesliquidcms
#16 interdiff-12-16.txt2.23 KBjhodgdon
#16 interdiff-6-12.txt13.92 KBjhodgdon
#16 2976175-16.patch9.97 KBjhodgdon
#6 entity_finder-2976175-6.patch10.54 KBvijay.mayilsamy

Issue fork tamper-2976175

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jamesdixon created an issue. See original summary.

jamesdixon’s picture

megachriz’s picture

I recommend to rename this plugin to "Entity finder", as I think that name makes more sense to the end user. "EFQ" is more a developer term and it refers to the technique that is used to find the entity.

jamesdixon’s picture

Title: EFQ Finder » D7 EFQ Finder to D8 Entity finder

Sounds good to me, I had no idea what EFQ Finder even meant!

vijay.mayilsamy’s picture

Assigned: Unassigned » vijay.mayilsamy
vijay.mayilsamy’s picture

StatusFileSize
new10.54 KB

Note to @myself - WIP Patch

chegor’s picture

Looks ok!

volkswagenchick’s picture

Issue tags: +dcasheville19

Tagging for DrupalCamp Asheville

volkswagenchick’s picture

Issue tags: +dcco2019

Tagging for the next to North American contrib days, Asheville and Colorado

DrupalCamp Asheville contrib days are July 13-14, 2019
DrupalCamp Colorado contrib day is Aug 4, 2019

meecect’s picture

any other movement on this? I installed the patch but it doesn't seem to be complete. I don't see Entity Finder in the list of available plugins in the feeds tamper.

What still needs to be implemented to make this work?

meecect’s picture

ok, I dug into this a bit...I think I see the components that are missing or wrong. I'm going to take a crack at finishing it and will share here when I am done.

meecect’s picture

Here is the new EntityFinder.php file. I was able to use it to import members into my OG groups, ie, it was able to create OG Membership entities using this tamper plugin to convert the 'group title' to an entity_id.

<?php

namespace Drupal\tamper\Plugin\Tamper;

use Drupal\Core\Form\FormStateInterface;
use Drupal\tamper\Exception\TamperException;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Component\Utility\NestedArray;

/**
 * Plugin implementation of the explode plugin.
 *
 * @Tamper(
 *   id = "entity_finder",
 *   label = @Translation("Entity Finder"),
 *   description = @Translation("Finds an Entity based on properties and fields.  Returns the ID of the entity"),
 *   category = "Other",
 *   handle_multiples = TRUE
 * )
 */
class EntityFinder extends TamperBase {

  const SETTING_ENTITY_TYPE = 'entity_type';
  const SETTING_BUNDLE = 'bundle';
  const SETTING_FIELD = 'field';

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    $config = parent::defaultConfiguration();
    $config[self::SETTING_ENTITY_TYPE] = '';
    $config[self::SETTING_BUNDLE] = '';
    $config[self::SETTING_FIELD] = '';
    return $config;
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {

    $form['#prefix'] = '<div id="feeds-tamper-entity-finder-wrapper">';
    $form['#suffix'] = '</div>';

    // $form_state->setCached(FALSE);

    // Gets the button that triggers the ajax call
    $triggering_element = $form_state->getTriggeringElement();

    // Retrieve the parents, so we can climb back up the tree and do not have to hard code the unknown position of our subform
    $parents = array_slice($triggering_element['#array_parents'], 0, -1);
    if($parents) {
      $all_values = $form_state->getCompleteFormState()->getValues();
      $values = NestedArray::getValue($all_values, $parents);
    }

    $entityTypes = $this->getEntityTypes();

    $form[self::SETTING_ENTITY_TYPE] = [
      '#type' => 'select',
      '#title' => $this->t('Entity type'),
      '#options' => $entityTypes,
      '#default_value' => $this->getSetting(self::SETTING_ENTITY_TYPE),
      '#ajax' => array(
         'callback' => [$this,'changeSelect'],
         'event' => 'change',
         'wrapper' => 'feeds-tamper-entity-finder-wrapper',
      ),
      '#required' => TRUE,
      '#empty_option' => t('-- Select --'),
    ];

    // Set EntityType to the value in the config settings, or populate it with the form_state (from ajax)
    $entityType = $values[self::SETTING_ENTITY_TYPE] ? $values[self::SETTING_ENTITY_TYPE] : $this->getSetting(self::SETTING_ENTITY_TYPE);
    $bundles =  $this->getBundles($entityType);

    $form[self::SETTING_BUNDLE] = array(
      '#type' => 'select',
      '#title' => $this->t('Bundle'),
      '#options' => $bundles,
      '#default_value' => $this->getSetting(self::SETTING_BUNDLE),
      '#ajax' => array(
          'callback' => [$this,'changeSelect'],
          'event' => 'change',
          'wrapper' => 'feeds-tamper-entity-finder-wrapper',
      ),
      '#empty_option' => t('-- Select --'),
    );

    // Set bundle to the value in the config settings, or populate it with the form_state (from ajax)
    $bundle = $values[self::SETTING_BUNDLE] ? $values[self::SETTING_BUNDLE] : $this->getSetting(self::SETTING_BUNDLE);

    // Gather field definitions.
    $fields = $this->getFields($entityType, $bundle);

    $form[self::SETTING_FIELD] = array(
      '#type' => 'select',
      '#title' => $this->t('Field'),
      '#options' => $fields,
      '#default_value' => $this->getSetting(self::SETTING_FIELD),
      '#empty_option' => t('-- Select --'),
    );

    return $form;
  }

  /**
   * Ajax callback for form changes.
   */
  public function changeSelect(array &$form, FormStateInterface $form_state) {
    // Gets the button that triggers the ajax call
    $triggering_element = $form_state->getTriggeringElement();
    // Retrieve the parents, so we can climb back up the tree and do not have to hard code the unknown position of our subform
    $parents = array_slice($triggering_element['#array_parents'], 0, -1);

    // Use NestedArray to get the element in `$form` at the path that `$parents` describes.
    return NestedArray::getValue($form, $parents);
  }

  /**
   * Get all Entity types.
   *
   * @return array
   *   A list of Entity Types.
   */
  protected function getEntityTypes() {
    // Get some info on entity types.
    $entity_types = array();
    $definitions = \Drupal::entityTypeManager()->getDefinitions();
    foreach ($definitions as $machine_name => $info) {
      $entity_types[$machine_name] = SafeMarkup::checkPlain($info->getLabel());
    }
    return $entity_types;
  }

  /**
   * Get the bundles for an entity type
   *
   * @return array
   *   A list of Bundles
   */
  protected function getBundles($entity_type) {
    if ($entity_type) {
      $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type);
      foreach ($bundle_info as $machine_name => $info) {
        $bundles[$machine_name] = SafeMarkup::checkPlain($info['label']);
      }
      return $bundles;
    } else {
      return array();
    }
  }


  /**
   * Get the fields for an entity type and bundle
   *
   * @return array
   *   A list of Bundles
   */
  protected function getfields($entity_type, $bundle) {
    if ($entity_type && $bundle) {
      $field_info = \Drupal::service('entity_field.manager')->getFieldDefinitions($entity_type, $bundle);
      foreach ($field_info as $field_name => $field_definition) {
        $fields[$field_name] = SafeMarkup::checkPlain($field_definition->getLabel());
      }
      return $fields;
    } else {
      return array();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    // Test the regex.
    // $test = @preg_replace($form_state->getValue(self::SETTING_ENTITY_TYPE), '', 'asdfsadf');
    // if ($test === NULL) {
    //   $form_state->setErrorByName(self::SETTING_FIND, $this->t('Invalid regular expression.'));
    // }
  }


  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::submitConfigurationForm($form, $form_state);
    $this->setConfiguration([
      self::SETTING_ENTITY_TYPE => $form_state->getValue(self::SETTING_ENTITY_TYPE),
      self::SETTING_BUNDLE => $form_state->getValue(self::SETTING_BUNDLE),
      self::SETTING_FIELD => $form_state->getValue(self::SETTING_FIELD),
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function tamper($data, TamperableItemInterface $item = NULL) {
    $entityType = $this->getSetting(self::SETTING_ENTITY_TYPE);
    $bundle = $this->getSetting(self::SETTING_BUNDLE);
    $field = $this->getSetting(self::SETTING_FIELD);
    if ($field === 'uuid') {
      if (NULL !== ($entity = \Drupal::entityRepository()->loadEntityByUuid($entityType, $data))) {
        return $entity->id();
      }
    }
    else {
      //$query = $this->queryFactory->get($entityType);
      $query = \Drupal::entityTypeManager()->getStorage($entityType)->getQuery();

      if ($bundle) {
        $query->condition($this->getBundleKey($entityType), $bundle, '=');
      }

      $ids = array_filter($query->condition($field, $data)->range(0, 1)->execute());
      if ($ids) {
        return reset($ids);
      }
    }
  }

  /**
   * Returns the entity type's bundle key.
   *
   * @return string
   *   The bundle key of the entity type.
   */
  protected function getBundleKey($entity) {
    return \Drupal::entityTypeManager()->getDefinition($entity)->getKey('bundle');
  }

}
jhodgdon’s picture

This works great (patch above + revision in comment #12)! +1 for adding it to Tamper. There is one error saying that the $value variable is undefined in line 97 of this EntityFinder.php file, but it seems to work anyway.

davidburns’s picture

Status: Active » Needs work

This does work, but still a few warnings and notices that need to be cleaned up and a re-rolled patch which includes comment #12.

Warning: array_slice() expects parameter 1 to be array, null given in Drupal\tamper\Plugin\Tamper\EntityFinder->buildConfigurationForm() (line 57 of modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php).
Notice: Undefined variable: values in Drupal\tamper\Plugin\Tamper\EntityFinder->buildConfigurationForm() (line 80 of modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php).
Notice: Undefined variable: values in Drupal\tamper\Plugin\Tamper\EntityFinder->buildConfigurationForm() (line 97 of modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php).
megachriz’s picture

It would be cool if the changes from #12 are incorporated in the patch and that the tests cover the issues noted in #14.

jhodgdon’s picture

Status: Needs work » Needs review
StatusFileSize
new9.97 KB
new13.92 KB
new2.23 KB

Here's a start anyway:
- The first interdiff is the differences between the patch in #6 and the plugin file from the text in comment #12.
- The second interdiff is an untested attempt to fix the errors from #14 (which also cover the errors I mentioned in #13).
- The new patch has both of these changes.

I'm not currently sitting on the machine where I can run tests, so let's see if we can get the test bot to respond...

Status: Needs review » Needs work

The last submitted patch, 16: 2976175-16.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

megachriz’s picture

@jhodgdon
Thanks for giving it a go.

A few things I see in the patch:

  1. The provided tests make no sense. This seems to be a copy of the tests for the implode plugin. Check the tests from the D7 version of Feeds Tamper and see if you can port these to a kernel test: FeedsTamperEfqFinderTestCase.
  2. An unit test is needed as well for a patch to be accepted.
  3. Since AJAX is involved here, we might need a Javascript test as well.
  4. There are coding standard issues.
  5. There are references to \Drupal in the code that needs to be replaced with dependency injection. This is required to get a useful unit test.
  6. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,241 @@
    +      $entity_types[$machine_name] = SafeMarkup::checkPlain($info->getLabel());
    

    SafeMarkup::checkPlain() is deprecated.

  7. +++ b/config/schema/tamper.schema.yml
    @@ -136,3 +136,21 @@ tamper.truncate_text:
    +    update:
    +      type: string
    +      label: 'Update'
    

    'update' is not a configuration option for the plugin, so this can be removed.

I know this is not the easiest Tamper plugin to port. Keep up the good work!

jhodgdon’s picture

Yeah, I didn't really look at the tests, or the code... I tested the plugin with the code in #12 and it seemed to work, so we used it. And I at least made the existing working (probably? mostly?) code into a patch, so anyone needing this functionality can at least use it for now. But definitely, it needs some work!

Getting this totally viable is probably not my highest priority right now. I'm kind of in the middle of about 3 other fairly intensive efforts with my Drupal contribution time... so hopefully someone else will take it on.

liquidcms’s picture

Curious what release this patch is against. I know it should be against -dev; but -dev is older than the beta2 release (how does that happen?) and the patch doesn't apply there. So wondering what is lost if using -dev and applying patch there?

I'll look at manually applying changes to beta2 as i assume it is more recent than -dev.

liquidcms’s picture

ah.. my bad.. this is on Tamper, not Feeds Tamper.

liquidcms’s picture

now that i have patched the correct module....

- i have a feed value for Email that i want to match to a user account (Authored By for the Node i cam trying to create), it shows as this in Mapping: Authored by (uid): The username of the content author. Reference by: User ID
- I see the tamper action: Entity Finder and UI seems to work as i'd expect.
- in EF tamper i set entity/bundle to User and set field to Email

when i try to import, i get this error:

Drupal\Core\Entity\Query\QueryException: '' not found in Drupal\Core\Entity\Query\Sql\Tables->ensureEntityTable() (line 367 of core\lib\Drupal\Core\Entity\Query\Sql\Tables.php).

liquidcms’s picture

StatusFileSize
new768 bytes

i think i have found the issue.

in the tamper you add a query condition like this:

$query->condition($this->getBundleKey($entityType), $bundle, '=');

and getBundleKey is this:

\Drupal::entityTypeManager()->getDefinition($entity)->getKey('bundle');

For "node" bundles this returns "type", which is a filter on the items in the node table to limit search to this bundle. For "user", this returns "" and causes the error I reported above.

I made a small tweak to the code such that if there is no bundleKey then don't include that condition. Not sure this will work for all entity types; but it now allows it to work with users (which is probably a pretty popular one to set content ownership).

jamesdixon’s picture

Thanks everyone. Queue up the test bot to see if the latest patch passes.

jamesdixon’s picture

Lets see if we can get tests passing and try and test this one out manually to ensure it's working.

zabej’s picture

Hello @jamesdixon,

As far as I know patch-23 addon (not independent) to patch-16. Why does patch 23 not include 16....

Sure we can merge them, but patch 23 not passed.

Just for clear understanding to complete the issue we need to
1. Apply and test patch 16
2. If it needs to be fixed do it and retest.
3. If it passed successfully then merge a code from patch 23.
4. Name should be like entity-lookup-2976175-xx.patch

And a question about interdiff. Should it be compared with 16?

Will be thanks full for any comment

jamesdixon’s picture

@zabej: Thanks for your comment. It looks like the patch in #23 is not a full patch as you correctly identified. Lets try rolling patches #16 and #23 together.

1) Apply patch #16
2) Apply patch #23
3) Profit (hopefully)

jamesdixon’s picture

I don't feel I was super clear there so here's a code example of what I mean:

1) cd tamper
2) wget https://www.drupal.org/files/issues/2019-08-08/2976175-16.patch
3) git apply -v 2976175-16.patch
4) wget https://www.drupal.org/files/issues/2020-03-17/entity-lookup-2976175-23....
5) git apply -v entity-lookup-2976175-23.patch
6) Test it out
7) If it fails tests we may need to add that 4th parameter when creating the test objects

tbenice’s picture

Status: Needs work » Needs review
StatusFileSize
new10.1 KB

I combined the two patches. The first one did not apply any more against 8.x-1.x, so I re-created. Not sure what to make an interdiff against since this is combined so will leave that alone.

Status: Needs review » Needs work

The last submitted patch, 29: tamper-add_d7_efq_finder_to_d8_entity_finder-2976175-29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

zabej’s picture

StatusFileSize
new201 bytes
new129.03 KB

@jamesdixon,

I've cleaned the code and add several fixes here. But there still exits an error with Drupal::setContainer().
I will talk with @megaChriz

There were 2 errors:

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithMultipleValues
Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

/home/jedi/structured/code/feedsmigrates2/web/core/lib/Drupal.php:130
/home/jedi/structured/code/feedsmigrates2/web/core/lib/Drupal.php:291
/home/jedi/structured/code/feedsmigrates2/web/modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php:225
/home/jedi/structured/code/feedsmigrates2/web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:41

Screenshot is attached.

zabej’s picture

StatusFileSize
new5.94 KB

@jamesdixon updated code

zabej’s picture

When I added fourth parameter ($this->getMockSourceDefinition()) creating new object of EntityFinder class

  protected function instantiatePlugin() {
        $config = [
            EntityFinder::SETTING_GLUE => ',',
          ];
    return new EntityFinder($config, 'entity_finder', [], $this->getMockSourceDefinition());
  }

There appears error:

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithMultipleValues
Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

It is the same for

testEntityFinderWithSingleValue
as well as
testBuildConfigurationForm
methods. The last one out of the test file.

The error links to the lines in EntityFinder.php file


...
$definitions = \Drupal::entityTypeManager()->getDefinitions();
...
$query = \Drupal::entityTypeManager()->getStorage($entityType)->getQuery();

in EntityFinderTest.php

...
$this->plugin->tamper($original);
....
$this->assertEquals($expected, $this->plugin->tamper($original));
...

I've made online search around the similar issues.

https://drupal.stackexchange.com/questions/258233/how-to-create-a-test-c...
Based on the link above I guess it is something around declaration and access to particular methods from test class and from tamper class to drupal core. Need help and advice of more experienced developers.

I've added the patch derived from the comment #29. Fixed code style there following drupal linter and const SETTING_GLUE constant

megachriz’s picture

+++ b/src/Plugin/Tamper/EntityFinder.php
@@ -0,0 +1,249 @@
+  /**
+   * Returns the entity type's bundle key.
+   *
+   * @return string
+   *   The bundle key of the entity type.
+   */
+  protected function getBundleKey($entity) {
+    return \Drupal::entityTypeManager()->getDefinition($entity)->getKey('bundle');
+  }

The entity type manager should be injected into this class. This way you could do $this->entityTypeManager->getDefinition() instead of \Drupal::entityTypeManager()->getDefinition().

To inject the entity type manager, you need to:

  1. In the class, define a property called $entityTypeManager.
  2. Override the constructor and add a parameter typehinting EntityTypeManagerInterface.
  3. Add a create() method - Implement \Drupal\Core\Plugin\ContainerFactoryPluginInterface.

For an example, see \Drupal\feeds\Feeds\Target\EntityReference, which is also injecting the entity type manager.

Next, in the unit test, you would need to mock \Drupal\Core\Entity\EntityTypeManagerInterface and make sure that it returns the correct thing for getDefinition(). That thing may also be a mock that should return something for the call to getKey().
You could take a look at what the unit test for \Drupal\feeds\Feeds\Target\EntityReference does in \Drupal\Tests\feeds\Unit\Feeds\Target\EntityReferenceTest, though it could look a bit overwhelming when seeing that code for the first time. (I remember it being a bit overwhelming to me when I saw it for the first time.)

jamesdixon’s picture

StatusFileSize
new40.57 KB
new86.06 KB
new78.31 KB

Adding code examples to support @megachriz's suggestions:

jamesdixon’s picture

@zabej and I took a crack at this and @zabej will be posting results at some point.

zabej’s picture

Hello @megachriz,
We've generate such a code (attached). Is it a right direction on you opinion?

Test gives the 8 errors. We continue to work on it

Testing Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest EEEEEEEE 8 / 8 (100%)

Time: 464 ms, Memory: 6.00 MB

There were 8 errors:

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithSingleValue
Error: Call to a member function setStringTranslation() on null

/.../web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/TamperPluginTestBase.php:27
/.../web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:34

...
jamesdixon’s picture

@zabej: Looks like we need to call parent:setUp() at end of setUp() function instead of beginning. That may fix issues.

If that doesn't work maybe we need to call $this->setUp() from top of instantiatePlugin().

megachriz’s picture

@zabej

  1. Only looking at how the entity type manager is injected, then yes - this is a step in the right direction. The create() method does need to pass a SourceDefinition object as well to the constructor. Looking at TamperManager that object appears to be in $configuration['source_definition']:
    /**
     * {@inheritdoc}
     */
    public function createInstance($plugin_id, array $configuration = []) {
      $plugin_definition = $this->getDefinition($plugin_id);
      $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
    
      // If the plugin provides a factory method, pass the container to it.
      if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
        return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
      }
    
      return new $plugin_class($configuration, $plugin_id, $plugin_definition, $configuration['source_definition']);
    }
    
  2. SourceDefinitionInterface need to be imported at the top of the file: use Drupal\tamper\SourceDefinitionInterface;
  3. +++ b/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php
    @@ -14,13 +14,58 @@
    +    // Entity type manager.
    +    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
    +
    +    // Entity storage (needed for entity query's).
    +    $this->entityStorage = $this->prophesize(EntityStorageInterface::class);
    +    $this->entityTypeManager->getStorage('referenceable_entity_type')->willReturn($this->entityStorage);
    +
    +    // Made-up entity type that we are referencing to.
    +    $referenceable_entity_type = $this->prophesize(EntityTypeInterface::class);
    +    $referenceable_entity_type->entityClassImplements('\Drupal\Core\Entity\ContentEntityInterface')->willReturn(TRUE)->shouldBeCalled();
    +    $referenceable_entity_type->getKey('label')->willReturn('referenceable_entity_type label');
    +    $this->entityTypeManager->getDefinition('referenceable_entity_type')->willReturn($referenceable_entity_type)->shouldBeCalled();
    +
    +    // EntityReference::prepareTarget() accesses the entity type manager from
    +    // the global container.
    +    // @see \Drupal\feeds\Feeds\Target\EntityReference::prepareTarget()
    +    $container = new ContainerBuilder();
    +    $container->set('entity_type.manager', $this->entityTypeManager->reveal());
    +    $container->set('string_translation', $this->getStringTranslationStub());
    +    \Drupal::setContainer($container);
    

    I think you won't need all of this code (but you do need some of it), but I suppose you're were still working on this bit?

zabej’s picture

@jamesdixon thanks for the advice I tried both suggestions but they do not work

The error move to

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithSingleValue
Prophecy\Exception\Doubler\MethodNotFoundException: Method `Double\stdClass\P1::getStorage()` is not defined.

for tests are absent in the EntityFinderTest directly:

For example,

3) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testGetPluginId
Prophecy\Exception\Doubler\MethodNotFoundException: Method `Double\stdClass\P1::getStorage()` is not defined.

/home/jedi/structured/code/feedsmigrates2/web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:41

4) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testGetPluginDefinition
Prophecy\Exception\Doubler\MethodNotFoundException: Method `Double\stdClass\P1::getStorage()` is not defined.

/home/jedi/structured/code/feedsmigrates2/web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:41

5) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testGetConfiguration
Prophecy\Exception\Doubler\MethodNotFoundException: Method `Double\stdClass\P1::getStorage()` is not defined.

/home/jedi/structured/code/feedsmigrates2/web/modules/contrib/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:41

6) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testDefaultConfiguration
Prophecy\Exception\Doubler\MethodNotFoundException: Method `Double\stdClass\P1::getStorage()` is not defined.

@megachriz
I'm a bit confused with you remark.

1.1. In the create method we pass the $configuration array. Do we need put this in the constructor directly as a separate parameter?
1.2 Or we need to add createInstance method where provides Factory method and pass all the parameter as they are in it?
1.3 Is it not enough to SourceDefinitionInterface in the constructor having the parameter already?

3. Honestly, I would be delete almost all, because a lot of code is not familiar what it does
Remain only like this

  public function setUp() {

    // Entity type manager.
    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);

    // EntityReference::prepareTarget() accesses the entity type manager from
    // the global container.
    // @see \Drupal\feeds\Feeds\Target\EntityReference::prepareTarget()
    $container = new ContainerBuilder();
    $container->set('entity_type.manager', $this->entityTypeManager->reveal());
    $container->set('string_translation', $this->getStringTranslationStub());
    \Drupal::setContainer($container);

    $config = [
      EntityFinder::SETTING_GLUE => ',',
    ];

    $this->targetPlugin = new EntityFinder($config, 'entity_finder', [], $this->getMockSourceDefinition(), $this->entityTypeManager->reveal());

    parent::setUp();
  }
megachriz’s picture

1.1. In the create method we pass the $configuration array. Do we need put this in the constructor directly as a separate parameter?

  1. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,280 @@
    +    parent::__construct($configuration, $plugin_id, $plugin_definition);
    

    $source_definition needs to be passed to the parent.

  2. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,280 @@
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    +    return new static(
    +      $configuration,
    +      $plugin_id,
    +      $plugin_definition,
    +      $container->get('entity_type.manager')
    +    );
    +  }
    

    This needs an extra line between $plugin_definition and $container->get('entity_type.manager').
    Most likely this should be $configuration['source_definition'], based on the code from TamperManager::createInstance().

1.2 Or we need to add createInstance method where provides Factory method and pass all the parameter as they are in it?

Not sure if I understood this question, but we don't need a new factory or createInstance() method.

1.3 Is it not enough to SourceDefinitionInterface in the constructor having the parameter already?

SourceDefinitionInterface does not exist in the current namespace and thus it needs to be referenced at the top of the file. SourceDefinitionInterface lives in the namespace Drupal\tamper and the plugin's namespace is Drupal\tamper\Plugin\Tamper
Add the following line:
use Drupal\tamper\SourceDefinitionInterface;

3. Honestly, I would be delete almost all, because a lot of code is not familiar what it does

That's the hard part indeed. Try if you can break it down in parts so it becomes somewhat understandable.

jamesdixon’s picture

Assigned: vijay.mayilsamy » Unassigned
StatusFileSize
new13.21 KB
new2.37 KB

I pushed this along a little further.

Some classes needed including through use at the top and followed direction from @megachriz. Thanks for the help.

We're closer but I think we need to mock up more test functionality probably surrounding the entity type configuration for the EntityFinder.

Now tests are giving me:

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithMultipleValues
Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.

/app/core/lib/Drupal.php:130
/app/core/lib/Drupal.php:283
/app/modules/tamper/src/Plugin/Tamper/EntityFinder.php:257
/app/modules/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:91

The EntityReferenceTest example got us this far, and now I believe we need to determine what mock functionality we need specifically for the EntityFinder Tamper plugin as it's requirements are different.

danharper’s picture

I've applied this patch but I'm now getting this error when editing any of my feeds that previously used the entity finder.

ArgumentCountError: Too few arguments to function Drupal\tamper\Plugin\Tamper\EntityFinder::__construct(), 4 passed in /app/web/modules/contrib/tamper/src/TamperManager.php on line 50 and exactly 5 expected in Drupal\tamper\Plugin\Tamper\EntityFinder->__construct() (line 46 of modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php).

liquidcms’s picture

Yes, i had this patch installed and working for a while; but haven't touched the importer in a while. Since then i migrated all contrib over to composer install and tried to make sure i included all the patches. Missed this one. Now that i have added the latest patch i get the same reported in #43.

.. and by "editing any feed", i think @danharper likely means when going to Tamper tab of any Feed Type.

liquidcms’s picture

#29 still works.

jamesdixon’s picture

Making notes from @megachriz:

1)

I think the following line is wrong in the Entity Finder class:
$query = \Drupal::entityTypeManager()->getStorage($entityType)->getQuery();
The code should not call \Drupal::entityTypeManager() here, but use the injected service. Use $this->entityTypeManager instead.

2) I see there are more \Drupal::service() calls, they should all be replaced with injected services.

jamesdixon’s picture

Made some progress based on @megachriz's feedback. The latest patch is a work in progress, but likely isn't functional yet.

jamesdixon’s picture

Made some more progress on the test. Since Entities have so many moving parts, there's lots of classes to prophesize!

Now I'm working on getting the prophesized QueryInterface $entity_query returning the correct values. It's returning null for it's functions instead of behaving properly. I've included the QueryInterface class up top, trying to figure out what's up there.

This is likely not a functional patch so I'd avoid using it in production.

There were 2 errors:

1) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testEntityFinderWithMultipleValues
Error: Call to a member function range() on null

/app/modules/tamper/src/Plugin/Tamper/EntityFinder.php:259
/app/modules/tamper/tests/src/Unit/Plugin/Tamper/EntityFinderTest.php:103

2) Drupal\Tests\tamper\Unit\Plugin\Tamper\EntityFinderTest::testBuildConfigurationForm
Invalid argument supplied for foreach()

/app/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php:164
/app/modules/tamper/src/Plugin/Tamper/EntityFinder.php:170
/app/modules/tamper/src/Plugin/Tamper/EntityFinder.php:93
/app/modules/tamper/tests/src/Unit/Plugin/Tamper/TamperPluginTestBase.php:86

jamesdixon’s picture

Actually correction: $entity_query is not being returned at all by the EntityStorage object, it's coming back as NULL. That's the issue. Not sure why the getStorage() function is failing to return $entity_query properly.

jamesdixon’s picture

Made more progress.

Still running into the same issue but at least crafted the $entity_query->condition() statement closer to what it should, and made $entity_query->execute() return what it is supposed to I think.

For some reason EntityFinder is still returning NULL on it's $entity_query->condition() call so I need to see what kind of query parameters it's actually passing and make sure the test matches up as a next step.

Gotta love debugging. :)

liquidcms’s picture

tried #50 but still getting error reported in #43

tried #29 in D9 (last patch i know still works for D8) but now getting this error:

Error: Class 'Drupal\Component\Utility\SafeMarkup' not found in Drupal\tamper\Plugin\Tamper\EntityFinder->getEntityTypes() (line 138 of modules/contrib/tamper/src/Plugin/Tamper/EntityFinder.php).

due to use of deprecated SafeMarkup.

would provide patch to fix this but unclear the direction since #29 as i don't think any of those work.

kazah’s picture

Any update for drupal 9.3

liquidcms’s picture

Went back to patch from #29 and fixed the deprecated method issues and it worked with D9.5

ericgsmith’s picture

Hello all.

Firstly I wanted to say thank you to everybody who has contributed to this issue. It has been a very long slog and so many people have contributed.

I have taken a quick look and made some tidy ups.

Please consider this a work in progress, and I have removed a bunch of stuff to get the tests going. I am not trying to discourage those ideas, I just wanted to share progress before I put them down.

Namely
- removed entity repository. Querying by uuid still works with entity query, I couldn't see why we needed to introduce another service for this field
- used a Kernel test. We can maybe revisit this, but this is a complicate plugin - the current state of the unit test was not complete, and being unfamiliar with the work so far it was easier for me to look at this with a kernel test.

Now a warning - I only looked at this via tests - I have not even looked at the UI or how that renders.

Adding patch now but it is still needs a few improvements. I will try tidy up the issue summary and remaining steps soon.

ericgsmith’s picture

  1. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,280 @@
    +    // $form_state->setCached(FALSE);
    

    I need to revisit the form side - this should either be there or removed, advice welcomed on what this is for and what other plugins are doing.

  2. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,280 @@
    +      '#empty_option' => t('-- Select --'),
    

    Should be $this->t

  3. +++ b/src/Plugin/Tamper/EntityFinder.php
    @@ -0,0 +1,280 @@
    +  protected function getfields($entity_type, $bundle) {
    

    Case needs to match where this is called

ericgsmith’s picture

StatusFileSize
new11.88 KB
new11.31 KB

Oops - wrong namespace in test - ignore 54

ericgsmith’s picture

Ok cool, green test! A few coding standard violations in the plugin and test noted in ci

kopeboy’s picture

Thank you for the work you're doing to finally take tamper out of alpha stability! 🙏🏻 Unfortunately I'm not skilled enough to help directly with code but I'm with you emotionally and can test when ready 💪🏻

jrochate’s picture

Does not work with current DEV branch.

megachriz’s picture

I'm hiding all the patches. Work should continue in the MR.

And I wonder if this plugin should support config entity types? I limited it to content entity types for now, because I accidentally selected a config entity type and it resulted into the error that the entity type did not have base field definitions.

hepabolu’s picture

FYI I've created a similar tamper module: https://github.com/hepabolu/feeds_tamper_lookup_entity

This one works for me, but since this is my first Drupal module feel free to correct and improve or assimilate into your version.

megachriz’s picture

Status: Needs work » Needs review

Tests are passing! This looks ready for a new review!

Highlights of the changes:

  • Column selection: if a field has more than one 'column', you can select a column. For example, a link field has "uri" and "title"; a body field has "value", "summary" and "format".
  • Selecting a bundle is optional. A field can be chosen without needing to select a bundle first. Selecting a bundle does narrow down the list of available fields.
  • The list of entity types are grouped by provider (usually the module defining them), making it easier to find the one you need.
  • Made updating the form via AJAX work outside of Feeds Tamper context.
  • Lots of automated tests.

@hepabolu
Thanks for your contribution 🙂. I see that your plugin has one feature that looks useful that Entity Finder currently doesn't have: a return field. Entity Finder now always returns the entity ID, but being able to select a different field to return sounds very useful.
I'm not sure yet if we should add it to Entity Finder right now (since this issue has been open for a very long time already) or if it would be better to add it in a follow-up. Do you want to help adding that feature here?

  • megachriz committed 5fbc0a76 on 8.x-1.x
    Issue #2976175 by megachriz, jamesdixon, zabej, ericgsmith, jhodgdon,...
megachriz’s picture

Category: Task » Feature request
Status: Needs review » Fixed

I merged the code! Thanks all who contributed to this issue.

megachriz’s picture

I've opened a follow-up for adding an additional configuration option (called "Return field") for the Entity Finder plugin: #3509334: Entity Finder: add return field option

The idea comes from @hepabolu who added this option to a plugin similar to Entity Finder in https://github.com/hepabolu/feeds_tamper_lookup_entity, so it would be great if we could port that option to the Entity Finder plugin.

Status: Fixed » Closed (fixed)

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