Problem/Motivation

Some values need to be the same for every piece of content to be imported. One way of achieving this is by using the Feeds Tamper plugin "Default value". However, this will not suffice in these cases:

  • When you have multiple feeds to import and the "default" value differs per feed.
  • You want your editors that are allowed to create feeds to specify the default value themselves.

A part of this functionality is already available. It is however not fully implemented yet. It is already possible to add fields to a feed type, but these are not selectable as mapping sources yet.

Proposed resolution

Complete the implementation of the FeedsSource plugin type.

Remaining tasks

  1. Add a test for importing from an entity reference field.
  2. Include improvements suggested in #24.

Original report by PunamShelke

Hi,

I am working on this feature, very soon i will update the patch...
Needs suggestions

The scenario is - I needs to import content under content type Faq so for that I have created one csv with question and answer only..
But I have some other entity reference fields under faq for this, I created same entity reference fields under the feeds type (Manage Fields) and i am importing faq csv, that should be import along with reference field value.

For This -

  • Created Faq Content type with some entity reference fields, question and anwser field
  • Created Feeds Type and same entity reference field as having in faq
  • Mapping fields created under the feeds type with faq fields
  • Created csv with question and answer value
  • On feed import page filling the import form and uploading csv file
  • After import, faq getting created with all the data provided in csv as well as terms

Comments

PunamShelke created an issue. See original summary.

punamshelke’s picture

Title: Feeds Type - Manage Fields, map that fields » Feeds Type - Mapping fields under feeds type (Manage Fields)
Issue summary: View changes
megachriz’s picture

Title: Feeds Type - Mapping fields under feeds type (Manage Fields) » Provide feeds type fields as mapping sources

This is a useful feature request. It has been requested for the D7 version as well: #1074662: Inherit properties from parent feed node (taxonomy, author, OG, language).

Steps to implement this:

  1. Define an event or hook for providing additional mapping sources. This should work in a similar way as the hook hook_feeds_parser_sources_alter() in the D7 version of Feeds.
  2. The fields on the feed type then need to be exposed as mapping sources via this event/hook.
megachriz’s picture

A perhaps better approach would be using a new type of plugin, called FeedsSource (to mirror this with FeedsTarget plugins). There is already one such plugin available in the Feeds code base, but I don't think it is currently used.

punamshelke’s picture

Hi,

Yes your are right FeedsSource is their and i am using the same.....
its working prefect,
I need to check for some cases and it will be complete...

punamshelke’s picture

StatusFileSize
new4.55 KB
new3.62 KB
new722 bytes

Hi,

Here is the patch for mapping the fields under the managed field of feed type....
Currently, We have to add machine name of field manually and its working fine...

This is the field I have created under the manage fields under feeds Type
Manage Fields

Mapping of newly created field
Mapping

punamshelke’s picture

Status: Active » Needs review
megachriz’s picture

Status: Needs review » Needs work

A source value from an item is allowed to be empty.

Could you implement this feature as a FeedsSource plugin? I think that a start for this feature was made in \Drupal\feeds\Feeds\Source\BasicFieldSource. I see that \Drupal\feeds\Entity\FeedType::getMappingSources() already tries to instantiate Feeds source plugins.

punamshelke’s picture

StatusFileSize
new0 bytes

I have implemented the changes, This one is modified patch, Now all fields under feeds it will come in source drop-down under mapping tab..

punamshelke’s picture

StatusFileSize
new1.35 KB

I have implemented the changes, This one is modified patch, Now all fields under feeds it will come in source drop-down under mapping tab..

punamshelke’s picture

StatusFileSize
new2.06 KB

This is the final patch including SourceFields and mapping of that fields..

  • You have to create field under Manage Fields of feed type
  • Go to mapping page now you can see the created field is coming in source drop-down
  • You can map that field with target
punamshelke’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 11: modified-feeds-field-mapping-2911491.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

megachriz’s picture

  1. +++ b/src/Feeds/Processor/EntityProcessorBase.php
    @@ -679,7 +679,8 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
    +        $value = (empty($item->get($source))) ? $feed->get($source)->getValue() : $item->get($source);
    +        $value = ($column == 'value' && is_array($value)) ? $value[0] : $value;
    

    The code should ask the source plugin for the value.
    And $item->get($source) is allowed to return an empty result. So it shouldn't ask the source plugin based on the condition of an item returning an empty result.

  2. +++ b/src/Feeds/Source/BasicFieldSource.php
    @@ -34,14 +34,14 @@ class BasicFieldSource extends PluginBase implements SourceInterface {
    -    //     $field_definition['label'] = t('Feed: @label', ['@label' => $field_definition['label']]);
    ...
    +        $sources[$field]['label'] = $field_definition->getLabel();
    

    I think the prefix 'Feed: ' for the label should be preserved, to make clear it comes from a field on the feed entity.

  3. +++ b/src/Feeds/Source/BasicFieldSource.php
    @@ -34,14 +34,14 @@ class BasicFieldSource extends PluginBase implements SourceInterface {
    +      if (preg_match('#^field_#i',$field)) {
    

    Field names don't have to start with 'field'. They can have any name. It's only if you add fields via the UI they start with 'field_' by default.

megachriz’s picture

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

This patch might do the trick. I have not tested if data from the feed entity is processed correctly, I just had a use case for using a custom FeedsSource plugin.

punamshelke’s picture

looks good...,

sealionking’s picture

would you please update the release with these patches? @MegaChriz

megachriz’s picture

@sealionking
It would be a good idea indeed. Have you tested if values from fields on the feed type are properly mapped? I did not test that yet.

sealionking’s picture

no,when I patched it, my site encountered unexpected problem when I go to mapping page
I don‘t know how to patch it properly.
I just patched the patch below mannually.
https://www.drupal.org/files/issues/feeds-source-plugin-support-2911491-...

nanc2’s picture

After patch #15 the mapping page not working.

Is there any other way to map the custom fields? I tried "add new source" but didn't work.

andypost’s picture

There's something with class hierarchy somehow
Looks plugin should know the type of feed, but I could be wrong

  1. +++ b/src/Feeds/Processor/EntityProcessorBase.php
    @@ -683,7 +683,12 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
    +        if ($plugin = $feed->getType()->getSourcePlugin($source)) {
    +          $value = $plugin->getSourceElement($feed, $item, $source);
    

    I think plugin should know which $feed it bounded, same time it looks strange that you pass source to getSourcePlugin() and later the same source to plugin method.
    Guess feed & source could be protected properties in plugin

  2. +++ b/src/Feeds/Source/BasicFieldSource.php
    @@ -34,14 +35,14 @@ class BasicFieldSource extends PluginBase implements SourceInterface {
    +        $field_definition['label'] = t('Feed: @label', ['@label' => $field_definition['label']]);
    

    instead t() netter use new TranslatableMarkup() for upcoming test coverage

  3. +++ b/src/Plugin/Type/Source/SourceInterface.php
    @@ -34,6 +35,6 @@ interface SourceInterface extends FeedsPluginInterface {
    -  public function getSourceElement(FeedInterface $feed, array $item, $element_key);
    +  public function getSourceElement(FeedInterface $feed, ItemInterface $item, $element_key);
    

    docblock also needs update

nanc2’s picture

So is it possible to use the feed type fields as mapping source?

I am not a programmer and I have spent a lot of time trying to find a solution. I can easily do this with Drupal 7.

What is the right way to use add "New source..." ? I tried to use the feed type fields machine name but it didn't work.

megachriz’s picture

@nanc2
If the patch doesn't allow you to use feed type fields as mapping sources, then indeed it is not possible right now.

@andypost
I'm trying to understand your comments.

  1. I think plugin should know which $feed it bounded

    What do you mean with this? The static method sources() from a source plugin already receives the feed type and based on that it can return a list of sources or not.

    it looks strange that you pass source to getSourcePlugin() and later the same source to plugin method.

    A source plugin can deliver multiple sources. When getSourceElement() is called, it needs to know for which source it should return a value.

  2. Agreed that for testing $this->t() should be used.
  3. Agreed that the docblock for getSourceElement() is missing docs for the parameter $item.

Here is a sample implementation of a Feeds source plugin. This source plugin is used to set default values for some fields (the main source doesn't deliver values for these) and to set a value based on an other value. It delivers three sources and it only delivers them for feed types which name starts with 'product'. The fields that these sources are mapped to come from the RNG module.
You see that for 'study:registration_type' and 'study:min_registrants' it always delivers the same value. For 'study:max_registrants' it calculates a value.


namespace Drupal\ukkbstudy\Feeds\Source;

use Drupal\feeds\FeedInterface;
use Drupal\feeds\Feeds\Item\ItemInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\Plugin\Type\PluginBase;
use Drupal\feeds\Plugin\Type\Source\SourceInterface;

/**
 * @FeedsSource(
 *   id = "study_source"
 * )
 */
class StudySource extends PluginBase implements SourceInterface {

  /**
   * {@inheritdoc}
   */
  public static function sources(array &$sources, FeedTypeInterface $feed_type, array $definition) {
    // Only provide sources for feed types starting with 'product'.
    if (strpos($feed_type->id(), 'product') !== 0) {
      // Not interested in this feed type.
      return;
    }

    $sources['study:registration_type'] = [
      'label' => t('Registration type'),
      'id' => $definition['id'],
    ];
    $sources['study:min_registrants'] = [
      'label' => t('Minimum registrants'),
      'id' => $definition['id'],
    ];
    $sources['study:max_registrants'] = [
      'label' => t('Maximum registrants'),
      'id' => $definition['id'],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getSourceElement(FeedInterface $feed, ItemInterface $item, $element_key) {
    list(, $field) = explode(':', $element_key);

    switch ($field) {
      case 'registration_type':
        return ['standard_registration'];

      case 'min_registrants':
        return 1;

      case 'max_registrants':
        $max_total = $item->get('Maximum_aantal_deelnemers');
        if ($max_total) {
          return $max_total - $item->get('Aantal_deelnemers');
        }
        return 0;
    }
  }

}

The solution could also have been implemented with Feeds Tamper, but that module wasn't ready yet when above code was written. I made more source plugins, but the one above is the simplest example.

andypost’s picture

+++ b/src/Feeds/Processor/EntityProcessorBase.php
@@ -683,7 +683,12 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
+        if ($plugin = $feed->getType()->getSourcePlugin($source)) {
+          $value = $plugin->getSourceElement($feed, $item, $source);

I mean that \Drupal\feeds\Entity\FeedType::getSourcePlugin() is not part of interface & not documented.
As I see it creates new plugin object depending on source argument & keep ref to feed type

So this source could be stored in plugin instance like feed_type stored in plugin config

      if (isset($sources[$source]['id'])) {
        $configuration = ['feed_type' => $this];
        $this->sourcePlugins[$source] = $this->getSourcePluginManager()->createInstance($sources[$source]['id'], $configuration);

could be changed to

      if (isset($sources[$source]['id'])) {
        $configuration = [
          'feed_type' => $this,
          'source' => $source,
        ];
        $this->sourcePlugins[$source] = $this->getSourcePluginManager()->createInstance($sources[$source]['id'], $configuration);

This way plugin instance will always know from which type it created and for which source

andypost’s picture

So the code above will become

+        if ($plugin = $feed->getType()->getSourcePlugin($source)) {
+          $value = $plugin->getSourceElement($feed, $item);
megachriz’s picture

Issue summary: View changes
Status: Needs review » Needs work
StatusFileSize
new6.67 KB
new4.56 KB

Worked on a fix and test for this issue. Improvement suggestions from #24 not yet included. I hope to look at that later.

The test checks if a custom text field on the feed type can be used as source. It would be good to have another test for an entity reference field. So this still needs work.

I updated the issue summary as well.

megachriz’s picture

Issue summary: View changes
StatusFileSize
new10.67 KB
new4.83 KB

And a test for a taxonomy reference field.

megachriz’s picture

Status: Needs work » Needs review
StatusFileSize
new12.91 KB
new4.66 KB

This implements the suggestion from #24. Thanks @andypost for your suggestion.

  • MegaChriz committed 3d892ff on 8.x-3.x
    Issue #2911491 by MegaChriz, PunamShelke, andypost: Fixed provide feeds...
megachriz’s picture

Status: Needs review » Fixed

Committed #28.

Status: Fixed » Closed (fixed)

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