0

I want to import the fields "alt", "title" and the custom field "copyright_notice" via feeds using the following code:

function budg_eu_projects_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
  foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {

    $info = field_info_field($name);

    if ($info['type'] == 'file') {
      $targets[$name . ':alt'] = array(
        'name' => t('@label: Alt', array('@label' => $instance['label'])),
        'callback' => 'budg_eu_projects_set_target',
        'description' => t('The alt tag of the @label field.', array('@label' => $instance['label'])),
        'real_target' => $name,
      );
      $targets[$name . ':title'] = array(
        'name' => t('@label: Title', array('@label' => $instance['label'])),
        'callback' => 'budg_eu_projects_set_target',
        'description' => t('The title of the @label field.', array('@label' => $instance['label'])),
        'real_target' => $name,
      );
      $targets[$name . ':copyright_notice'] = array(
        'name' => t('@label: Copyright Notice', array('@label' => $instance['label'])),
        'callback' => 'budg_eu_projects_set_target',
        'description' => t('The title of the @label field.', array('@label' => $instance['label'])),
        'real_target' => $name,
      );
    }
  }

  return $targets;
}

As a callback function I use

/**
 * Callback for mapping file fields.
 */
function budg_eu_projects_set_target(FeedsSource $source, $entity, $target, array $values) {

  list($field_name, $sub_field) = explode(':', $target . ':uri');
  $info = field_info_field($field_name);

  // Populate entity.
  $field = isset($entity->$field_name) ? $entity->$field_name : array(LANGUAGE_NONE => array());
  $delta = 0;

  foreach ($values as $v) {
    if ($info['cardinality'] == $delta) {
      break;
    }

    if (!isset($field[LANGUAGE_NONE][$delta])) {
      $field[LANGUAGE_NONE][$delta] = array();
    }

    $field[LANGUAGE_NONE][$delta][$sub_field] = $v;

    $delta++;
  }

  $entity->$field_name = $field;
}

I managed to add the fields as a mapping target but the data of the fields are not saved. What is missing?

Thanks Rainer

  • what type of file is it? xml? CSV? or,,, – No Sssweat Jan 5 '16 at 14:45
  • It is a csv file – abelass Jan 5 '16 at 14:51
  • Looks like you iterate $values but don't use the value to set anything. Maybe adding $field[LANGUAGE_NONE][$delta][$sub_field] = $v; before $delta++; would help? – Andy Jan 6 '16 at 13:54
  • Right, of course, but even when adding it, still no results. I updated the above code. – abelass Jan 7 '16 at 14:57
  • @abelass if you put someone's handle in your comment they get a little notification. How have you added these fields? IIUC the approach you're using will only work if you've altered the field schema to add those fields. A simpler approach might be to use fieldable files. – Andy Jan 8 '16 at 11:21
1

AFAICT the code you've posted will populate the entity with the values from the CSV file - but nothing's set up to persist those values. The Field API itself can persist values it knows about. Look at file_field_schema()

/**
 * Implements hook_field_schema().
 */
function file_field_schema($field) {
  return array(
    'columns' => array(
      'fid' => array(
        'description' => 'The {file_managed}.fid being referenced in this field.',
        ...
      ),
      'display' => array(
        'description' => 'Flag to control whether this file should be displayed when viewing content.',
        ...
      ),
      'description' => array(
        'description' => 'A description of the file.',
        ...
      ),
    ),
    ...
  );
}

Through this, the Field API knows to store the file ID, whether or not to show the file when viewing content, and a description of the file. Image field is very close to file field, and shares mostly the same code. And looking at image_field_schema() you can see where the title and alt fields come from.

/**
 * Implements hook_field_schema().
 */
function image_field_schema($field) {
  return array(
    'columns' => array(
      'fid' => array(
        'description' => 'The {file_managed}.fid being referenced in this field.',
        ...
      ),
      'alt' => array(
        'description' => "Alternative image text, for the image's 'alt' attribute.",
        ...
      ),
      'title' => array(
        'description' => "Image title text, for the image's 'title' attribute.",
        ...
      ),
      'width' => array(
        'description' => 'The width of the image in pixels.',
        ...
      ),
      'height' => array(
        'description' => 'The height of the image in pixels.',
        ...
      ),
    ),
    ...
  );
}

I think the right way forwards depends on your data model. In the example below there are two nodes with file fields, each referring to the same file. Note that they can have different alt and title fields even though they're referring to the same file.

Node 1:
  file_field:
    fid: 1
    alt: A beautiful beach
    title: Wish you were here?

Node 2:
  file_field:
    fid: 1
    alt: Great swimming
    title: Fun for all the family

The alternative I suggested in a comment was to add the fields to the entity itself.

File 1:
  alt: A beautiful beach
  title: Wish you were here?

Node 1:
  file_field:
    fid: 1

Node 2:
  file_field:
    fid: 1

This means the alt and title will always be the same for a given file. It's obviously for you to decide which model meets your needs more appropriately. If you want to store the data per-field (the first example) you have two choices:

  1. Use an existing field and alter its schema with hook_schema_alter() - a bit painful as there's no hook_field_schema_alter(). Only do this if conceptually every instance of this field should have that extra data.

  2. (I'd prefer this personally) Create a new field that stores the extra columns you're interested in. Image field is the perfect place to look for inspiration, because its functionality's based on file field and it has extra columns.

If you want to store the data per file entity, then you can use fieldable files and I'm fairly sure you should be able to set everything up through the feeds UI. (IE no need for the feeds code above.)

  • Actually I use the Media browser widget that stores thetitle, alt and copyright_notice in the tables field_data_field_file_image_title_text, field_data_field_file_image_alt_text and field_data_field_copyright_notice, and not in field_data_field_image which occurs when the widget file is chosen. The first 2 fields are core media fields and the third one is a custom field to the file type image. So there is no need to create the fields, only how to get them saved. How can I make understand feeds that those information have to be attached to the freshley created image? – abelass Jan 13 '16 at 13:06
  • @abelass if I understand you correctly then it sounds like you're already using fieldable files (could you confirm?) and you've attached an extra field to the image bundle of the file entity type. To import a file with fields you'd want a Feeds file processor, but it looks like Feeds doesn't yet have one built in (patch). I'd suggest trying the patch or files feeds. – Andy Jan 13 '16 at 16:55
  • yes indeed. What I am aiming is a node importer that contains the above mentioned fields. So when importing the nodes the images will be attached to the node and inserted with ther respectif alt, title and copyright_notice values – abelass Jan 14 '16 at 13:39
  • @abelass I'm not sure there's anything I can add to my answer and comments. – Andy Jan 14 '16 at 14:45
  • thanks for you support. What I need is a mix between the current feeds core function of file import and file_feeds – abelass Jan 19 '16 at 9:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.