Hi,
Thanks a lot to maintainers and developers of this awesome module.

I am building a new Drupal 8 website using Ubercart and using Feeds 8.x-3.0-alpha2 release to import products. I am able to see the mappings for SKU but the mappings for Price or attributes is not visible.

In D7 using uc_feeds module it was possible to import Ubercart prices and attributes but that module is not yet ported to D8.

Since D8 feeds is actively developed, I thought to better ask maintainers here for help.

CommentFileSizeAuthor
#2 target-configuration.png83.98 KBmegachriz

Comments

enlightenedmonk created an issue. See original summary.

megachriz’s picture

Component: Feeds Import (feature) » Code
Category: Feature request » Support request
Status: Active » Fixed
StatusFileSize
new83.98 KB

In order to make targets for these fields appear, you'll need to write FeedsTarget plugins for them. To do that:

Step 1: Figure out the field type

First, figure out what the type is of the field that you want to map to. Look into the module's folder at src/Plugin/Field/FieldType to see what field types the module defines.
For Ubercart's price field this is: "uc_price", look at the value for "id":

/**
 * Defines the Ubercart price field type.
 *
 * @FieldType(
 *   id = "uc_price",
 *   label = @Translation("Price"),
 *   description = @Translation("This field stores a price in the database."),
 *   default_widget = "uc_price",
 *   default_formatter = "uc_price"
 * )
 */

If the field is a "base field" - which means the field is tight to the entity and cannot be deleted or renamed - you can also look for BaseFieldDefinition::create in the module's code to get a clue of what the type of field is that you want to map to. For Ubercart's price field you can find the following, which confirms that the field type is indeed "uc_price":

$fields['price'] = BaseFieldDefinition::create('uc_price')

Step 2: Create a FeedsTarget plugin

Secondly, you'll need to write a FeedsTarget plugin for your field. For a FeedsTarget plugin for a field, you'll need to extend \Drupal\feeds\Plugin\Type\Target\FieldTargetBase or one of the existing FeedsTarget plugins deriving from that class.

UcPriceItem extends NumericItemBase, so I'm assuming here that the price field is similar to a regular numeric field, therefore your FeedsTarget plugin could extend \Drupal\feeds\Feeds\Target\Number.

So your initial FeedsTarget plugin would look like this:

namespace Drupal\uc_feeds\Feeds\Target;

use Drupal\feeds\Feeds\Target\Number;

/**
 * Defines a Ubercart price field mapper.
 *
 * @FeedsTarget(
 *   id = "uc_price",
 *   field_types = {
 *     "uc_price",
 *   }
 * )
 */
class UcPriceItem extends Number {
}

This could just suffice enough for this field. Some fields though would require some extra handling, explained in the next step.

Step 3: Expand the code of the FeedsTarget plugin

Some fields for example have multiple properties or they have a single property that is not named "value". Other fields may require that the value to import is converted first, to fit the format that the field expects.

Support fields with multiple properties

Ubercart's "Dimensions" field for example consists of four properties: "length", "width", "height" and "units", which you can see in the field's propertyDefinitions() method:

/**
 * {@inheritdoc}
 */
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
  $properties['length'] = DataDefinition::create('float')
    ->setLabel(t('Length'));
  $properties['width'] = DataDefinition::create('float')
    ->setLabel(t('Width'));
  $properties['height'] = DataDefinition::create('float')
    ->setLabel(t('Height'));
  $properties['units'] = DataDefinition::create('string')
    ->setLabel(t('Units'));

  return $properties;
}

For such FeedsTarget plugins, you'll need to implement the static prepareTarget() method, which is for example done for the link field:

/**
 * {@inheritdoc}
 */
protected static function prepareTarget(FieldDefinitionInterface $field_definition) {
  return FieldTargetDefinition::createFromFieldDefinition($field_definition)
    ->addProperty('uri')
    ->addProperty('title');
}

Converting values

You also may need to implement the method prepareValue() for converting values to a format that the field expects. A simple example is the target for the boolean field, which converts the value to either a '1' or a '0':

/**
 * {@inheritdoc}
 */
protected function prepareValue($delta, array &$values) {
  $values['value'] = (int) (bool) trim($values['value']);
}

Predefined values for properties

For some properties, you don't want the source to provide a value for it. This is for example the case for formatted text fields. These fields have a property in which it saves the field's text format. Text format is usually something site specific, you don't want to need to specify that in your CSV file (or XML, JSON, etc.), especially if it should be the same for all entries.

These FeedsTarget plugins need to implement the interface \Drupal\feeds\Plugin\Type\Target\ConfigurableTargetInterface so you can provide a configuration form for the target at the mapping page:

You can see how the FeedsTarget plugin "text" does that by implementing the methods defaultConfiguration(), buildConfigurationForm() and getSummary():

/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {
  return ['format' => 'plain_text'];
}

/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $options = [];
  foreach (filter_formats($this->user) as $id => $format) {
    $options[$id] = $format->label();
  }
  $form['format'] = [
    '#type' => 'select',
    '#title' => $this->t('Filter format'),
    '#options' => $options,
    '#default_value' => $this->configuration['format'],
  ];

  return $form;
}

/**
 * {@inheritdoc}
 */
public function getSummary() {
  $formats = \Drupal::entityTypeManager()
    ->getStorage('filter_format')
    ->loadByProperties(['status' => '1', 'format' => $this->configuration['format']]);

  if ($formats) {
    $format = reset($formats);
    return $this->t('Format: %format', ['%format' => $format->label()]);
  }
}

So hopefully, this helps you further with coding the FeedsTarget plugins that you need!

enlightenedmonk’s picture

@MegaChriz Thanks for your detailed guidance and prompt response.

I am afraid my coding skills are not at a level where I can develop plugins also I am new to Drupal 8.

My use case is for digital products with each product having 3 attributes. These attributes doesn't need to contain weight and cost information as the product is not shippable due to it's digital nature. I only need to set prices for 2 out of 3 attributes as default price is set for default attribute option.

In D7 using uc_feeds module I was able to set targets easily. Should I check with maintainers of uc_feeds if they can upgrade that module to D8 using your instructions?

megachriz’s picture

@enlightenedmonk
Yes, you can try to contact the maintainers of the uc_feeds module to see if they have time to port their module to D8, as these target plugins should be written in that project. Feeds only aims to supports targets for fields in Drupal core.

Status: Fixed » Closed (fixed)

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