You are here

class ViewsFieldFormatter in Views field formatter 8.2

Same name and namespace in other branches
  1. 8 src/Plugin/Field/FieldFormatter/ViewsFieldFormatter.php \Drupal\views_field_formatter\Plugin\Field\FieldFormatter\ViewsFieldFormatter

Class ViewsFieldFormatter.

Plugin annotation


@FieldFormatter(
  id = "views_field_formatter",
  label = @Translation("View"),
  description = @Translation("Todo"),
  weight = 100,
  field_types = {
    "boolean",
    "changed",
    "comment",
    "computed",
    "created",
    "datetime",
    "decimal",
    "email",
    "entity_reference",
    "entity_reference_revisions",
    "expression_field",
    "file",
    "float",
    "image",
    "integer",
    "language",
    "link",
    "list_float",
    "list_integer",
    "list_string",
    "map",
    "path",
    "string",
    "string_long",
    "taxonomy_term_reference",
    "text",
    "text_long",
    "text_with_summary",
    "timestamp",
    "uri",
    "uuid"
    }
)

Hierarchy

Expanded class hierarchy of ViewsFieldFormatter

File

src/Plugin/Field/FieldFormatter/ViewsFieldFormatter.php, line 58

Namespace

Drupal\views_field_formatter\Plugin\Field\FieldFormatter
View source
class ViewsFieldFormatter extends FormatterBase {

  /**
   * Custom ajax callback.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form element.
   */
  public function ajaxAddRow(array &$form, FormStateInterface $form_state) : array {

    /** @var \Drupal\field\FieldConfigInterface $fieldConfig */
    $fieldConfig = $this->fieldDefinition;
    return $form['fields'][$fieldConfig
      ->getName()]['plugin']['settings_edit_form']['settings']['arguments'];
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    $dependencies = parent::calculateDependencies();
    list($view_id) = \explode('::', $this
      ->getSetting('view'), 2);

    // Don't call the current view, as it would result into an
    // infinite recursion.
    // TODO: Check for infinite loop here.
    if ($view_id !== NULL && ($view = View::load($view_id))) {
      $dependencies[$view
        ->getConfigDependencyKey()][] = $view
        ->getConfigDependencyName();
    }
    return $dependencies;
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return [
      'view' => '',
      'arguments' => [],
      'hide_empty' => FALSE,
      'multiple' => FALSE,
      'implode_character' => '',
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $element = parent::settingsForm($form, $form_state);
    $settings = $this
      ->getSettings();

    // Get all views select options.
    $options = [];
    foreach (Views::getAllViews() as $view) {
      foreach ($view
        ->get('display') as $display) {
        $label = $view
          ->get('label');
        $options[$label][$view
          ->get('id') . '::' . $display['id']] = \sprintf('%s - %s', $label, $display['display_title']);
      }
    }

    // Early return if there is no views.
    if ([] === $options) {
      $element['help'] = [
        '#markup' => '<p>' . $this
          ->t('No available Views were found.') . '</p>',
      ];
      return $element;
    }
    $checked_arguments = \array_filter((array) $settings['arguments'], function ($argument) {
      return $argument['checked'];
    });

    // Make sure we only save arguments that are enabled.
    $settings['arguments'] = \array_values($checked_arguments);
    $this
      ->setSettings($settings);
    $ajax_arguments_count = 'ajax_arguments_count_' . $this->fieldDefinition
      ->getName();
    if ($form_state
      ->get($ajax_arguments_count) === NULL) {
      $form_state
        ->set($ajax_arguments_count, \count($checked_arguments));
    }

    // Ensure we clicked the Ajax button.
    // @todo Is there a better way to detect this ?
    $trigger = $form_state
      ->getTriggeringElement();
    if (\is_array($trigger['#array_parents']) && \end($trigger['#array_parents']) === 'addRow') {
      $form_state
        ->set($ajax_arguments_count, $form_state
        ->get($ajax_arguments_count) + 1);
    }
    $element['view'] = [
      '#title' => $this
        ->t('View'),
      '#description' => $this
        ->t("Select the view that will be displayed instead of the field's value."),
      '#type' => 'select',
      '#default_value' => $this
        ->getSetting('view'),
      '#options' => $options,
    ];
    $element['arguments'] = [
      '#prefix' => '<div id="ajax_form_table_arguments">',
      '#suffix' => '</div>',
      '#type' => 'table',
      '#header' => [
        '',
        $this
          ->t('Weight'),
        $this
          ->t('Argument index'),
        $this
          ->t('String or token'),
      ],
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'arguments-order-weight',
        ],
      ],
      '#caption' => $this
        ->t('Select,add and reorder the arguments that will be used by the selected
         view as contextual filters.
         To remove some rows, uncheck the checkbox and save.'),
    ];
    for ($i = 0; $i < $form_state
      ->get($ajax_arguments_count); $i++) {
      $element['arguments'][] = [
        'checked' => [
          '#type' => 'checkbox',
          '#title' => '',
          '#default_value' => $this
            ->getSettings()['arguments'][$i]['checked'],
        ],
        'weight' => [
          '#type' => 'weight',
          '#title' => $this
            ->t('Weight for @title', [
            '@title' => 'token',
          ]),
          '#title_display' => 'invisible',
          '#attributes' => [
            'class' => [
              'arguments-order-weight',
            ],
          ],
        ],
        'argument_index' => [
          '#markup' => $i,
        ],
        'token' => [
          '#type' => 'textfield',
          '#title' => 'Argument',
          '#description' => $this
            ->t('Use a static string or a Drupal token - You can temporary use <em>%value%</em> until a proper token is found.'),
          '#default_value' => $this
            ->getSettings()['arguments'][$i]['token'],
        ],
        '#attributes' => [
          'class' => [
            'draggable',
          ],
        ],
      ];
    }
    $element['addRow'] = [
      '#type' => 'button',
      '#button_type' => 'secondary',
      '#value' => t('Add a new argument'),
      '#ajax' => [
        'callback' => [
          $this,
          'ajaxAddRow',
        ],
        'event' => 'click',
        'wrapper' => 'ajax_form_table_arguments',
      ],
    ];
    $types = [
      'site',
      'user',
      'entity',
      'field',
      'date',
    ];
    switch ($this->fieldDefinition
      ->getTargetEntityTypeId()) {
      case 'taxonomy_term':
        $types[] = 'term';
        $types[] = 'vocabulary';
        break;
      default:
        $types[] = $this->fieldDefinition
          ->getTargetEntityTypeId();
        break;
    }
    $token = \Drupal::token();
    $info = $token
      ->getInfo();
    $available_token = \array_intersect_key($info['tokens'], \array_flip($types));
    $token_items = [];
    foreach ($available_token as $type => $tokens) {
      $item = [
        '#markup' => $this
          ->t('@type tokens', [
          '@type' => \ucfirst($type),
        ]),
        'children' => [],
      ];
      foreach ($tokens as $name => $info) {
        $info += [
          'description' => $this
            ->t('No description available'),
        ];
        $item['children'][$name] = \sprintf('[%s:%s] - %s: %s', $type, $name, $info['name'], $info['description']);
      }
      $token_items[$type] = $item;
    }
    $element['token_tree_link'] = [
      '#type' => 'details',
      '#title' => $this
        ->t('Available token replacements'),
      'description' => [
        '#markup' => $this
          ->t('To have more tokens, please install the <a href="@token">Token contrib module</a>.', [
          '@token' => 'https://drupal.org/project/token',
        ]),
      ],
    ];
    $element['token_tree_link']['list'] = [
      '#theme' => 'item_list',
      '#items' => $token_items,
      '#attributes' => [
        'class' => [
          'global-tokens',
        ],
      ],
    ];
    if (\Drupal::moduleHandler()
      ->moduleExists('token')) {
      $element['token_tree_link'] = [
        '#theme' => 'token_tree_link',
        '#token_types' => $types,
      ];
    }
    $element['hide_empty'] = [
      '#title' => $this
        ->t('Hide empty views'),
      '#description' => $this
        ->t('Do not display the field if the view is empty.'),
      '#type' => 'checkbox',
      '#default_value' => (bool) $this
        ->getSetting('hide_empty'),
    ];
    $element['multiple'] = [
      '#title' => $this
        ->t('Multiple'),
      '#description' => $this
        ->t('If the field is configured as multiple (<em>greater than one</em>),
         should we display a view per item ? If selected, there will be one view per item.'),
      '#type' => 'checkbox',
      '#default_value' => (bool) $this
        ->getSetting('multiple'),
    ];
    $element['implode_character'] = [
      '#title' => $this
        ->t('Concatenate arguments'),
      '#description' => $this
        ->t('If it is set, all arguments will be concatenated with the chosen character (<em>ex: a simple comma</em>)
         and sent as one argument. Empty to disable.'),
      '#type' => 'textfield',
      '#default_value' => $this
        ->getSetting('implode_character'),
    ];
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = [];
    $settings = $this
      ->getSettings();

    // For default settings, don't show a summary.
    if ($settings['view'] === '') {
      return [
        $this
          ->t('Not configured yet.'),
      ];
    }
    list($view, $view_display) = \explode('::', $settings['view'], 2);
    $multiple = (bool) $settings['multiple'] === TRUE ? 'Enabled' : 'Disabled';
    $hide_empty = (bool) $settings['hide_empty'] === TRUE ? 'Hide' : 'Display';
    $arguments = \array_map(function ($argument) {
      return 'Token';
    }, \array_keys(\array_filter((array) $settings['arguments'], function ($argument) {
      return $argument['checked'];
    })));
    if ([] === $arguments) {
      $arguments[] = $this
        ->t('None');
    }
    if ($view !== NULL) {
      $summary[] = t('View: @view', [
        '@view' => $view,
      ]);
      $summary[] = t('Display: @display', [
        '@display' => $view_display,
      ]);
      $summary[] = t('Argument(s): @arguments', [
        '@arguments' => \implode(', ', $arguments),
      ]);
      $summary[] = t('Empty views: @hide_empty empty views', [
        '@hide_empty' => $hide_empty,
      ]);
      $summary[] = t('Multiple: @multiple', [
        '@multiple' => $multiple,
      ]);
    }
    if ((bool) $settings['multiple'] === TRUE && $settings['implode_character'] !== '') {
      $summary[] = t('Implode character: @character', [
        '@character' => $settings['implode_character'],
      ]);
    }
    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = [];
    $settings = $this
      ->getSettings();
    if (isset($settings['view']) && !empty($settings['view']) && \strpos($settings['view'], '::') !== FALSE) {
      list($view_id, $view_display) = \explode('::', $settings['view'], 2);
    }
    else {
      return $elements;
    }

    // First check the availability of the view.
    $view = Views::getView($view_id);
    if (!$view || !$view
      ->access($view_display)) {
      return $elements;
    }
    $user_arguments = \array_filter((array) $this
      ->getSetting('arguments'), function ($argument) {
      return $argument['checked'];
    });

    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $items
      ->getParent()
      ->getValue();
    $token = \Drupal::token();
    $arguments = [];
    foreach ($user_arguments as $delta_argument => $item_argument) {
      foreach ($items as $delta_item => $item) {

        // This is temporary until we find a solution to get that same value
        // using a tokens.
        if ($item_argument['token'] === '%value%') {
          $columns = array_keys($items
            ->getFieldDefinition()
            ->getFieldStorageDefinition()
            ->getSchema()['columns']);
          $column = array_shift($columns);
          $arguments[$delta_argument][] = !empty($column) && isset($item
            ->getValue()[$column]) ? $item
            ->getValue()[$column] : NULL;
          continue;
        }
        $replacements = [
          $entity
            ->getEntityTypeId() => $entity,
          'entity' => $entity,
          'views_field_formatter' => [
            'delta' => $delta_item,
            'item' => $item,
            'items' => $items,
          ],
        ];
        switch ($this->fieldDefinition
          ->getTargetEntityTypeId()) {
          case 'taxonomy_term':
            $replacements['term'] = $entity;
            $replacements['vocabulary'] = Vocabulary::load($entity
              ->getVocabularyId());
            break;
        }
        $arguments[$delta_argument][] = $token
          ->replace($item_argument['token'], $replacements);
      }
    }
    if ((bool) $settings['multiple'] === TRUE) {
      foreach ($items as $delta => $item) {
        $viewArray = $this
          ->getViewArray($view, $view_display, \array_column($arguments, $delta), $settings);
        if ([] !== $viewArray) {
          $elements[$delta] = $viewArray;
        }
      }
    }
    else {
      foreach ($arguments as $delta_argument => $item_argument) {
        $arguments[$delta_argument] = \implode($settings['implode_character'], $arguments[$delta_argument]);
      }
      $viewArray = $this
        ->getViewArray($view, $view_display, $arguments, $settings);
      if ([] !== $viewArray) {
        $elements[0] = $viewArray;
      }
    }
    return $elements;
  }

  /**
   * Custom function to generate a view render array.
   *
   * @param \Drupal\views\ViewExecutable $view
   *   The view.
   * @param string $view_display
   *   The view display.
   * @param array $arguments
   *   The arguments to pass to the view.
   * @param array $settings
   *   The field formatter settings.
   *
   * @return array
   *   A render array.
   */
  private function getViewArray(ViewExecutable $view, $view_display, array $arguments, array $settings) : array {
    if ((bool) $settings['hide_empty'] === TRUE) {
      $view
        ->setArguments($arguments);
      $view
        ->setDisplay($view_display);
      $view
        ->preExecute();
      $view
        ->execute();
      if (empty($view->result)) {
        return [];
      }
    }
    return [
      '#type' => 'view',
      '#name' => $view
        ->id(),
      '#display_id' => $view_display,
      '#arguments' => $arguments,
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormatterBase::$fieldDefinition protected property The field definition.
FormatterBase::$label protected property The label display setting.
FormatterBase::$settings protected property The formatter settings. Overrides PluginSettingsBase::$settings
FormatterBase::$viewMode protected property The view mode.
FormatterBase::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 11
FormatterBase::getFieldSetting protected function Returns the value of a field setting.
FormatterBase::getFieldSettings protected function Returns the array of field settings.
FormatterBase::isApplicable public static function Returns if the formatter can be used for the provided field. Overrides FormatterInterface::isApplicable 14
FormatterBase::prepareView public function Allows formatters to load information for field values being displayed. Overrides FormatterInterface::prepareView 2
FormatterBase::view public function Builds a renderable array for a fully themed field. Overrides FormatterInterface::view 1
FormatterBase::__construct public function Constructs a FormatterBase object. Overrides PluginBase::__construct 11
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginSettingsBase::$defaultSettingsMerged protected property Whether default settings have been merged into the current $settings.
PluginSettingsBase::$thirdPartySettings protected property The plugin settings injected by third party modules.
PluginSettingsBase::getSetting public function Returns the value of a setting, or its default value if absent. Overrides PluginSettingsInterface::getSetting
PluginSettingsBase::getSettings public function Returns the array of settings, including defaults for missing settings. Overrides PluginSettingsInterface::getSettings
PluginSettingsBase::getThirdPartyProviders public function Gets the list of third parties that store information. Overrides ThirdPartySettingsInterface::getThirdPartyProviders
PluginSettingsBase::getThirdPartySetting public function Gets the value of a third-party setting. Overrides ThirdPartySettingsInterface::getThirdPartySetting
PluginSettingsBase::getThirdPartySettings public function Gets all third-party settings of a given module. Overrides ThirdPartySettingsInterface::getThirdPartySettings
PluginSettingsBase::mergeDefaults protected function Merges default settings values into $settings.
PluginSettingsBase::onDependencyRemoval public function Informs the plugin that some configuration it depends on will be deleted. Overrides PluginSettingsInterface::onDependencyRemoval 3
PluginSettingsBase::setSetting public function Sets the value of a setting for the plugin. Overrides PluginSettingsInterface::setSetting
PluginSettingsBase::setSettings public function Sets the settings for the plugin. Overrides PluginSettingsInterface::setSettings
PluginSettingsBase::setThirdPartySetting public function Sets the value of a third-party setting. Overrides ThirdPartySettingsInterface::setThirdPartySetting
PluginSettingsBase::unsetThirdPartySetting public function Unsets a third-party setting. Overrides ThirdPartySettingsInterface::unsetThirdPartySetting
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
ViewsFieldFormatter::ajaxAddRow public function Custom ajax callback.
ViewsFieldFormatter::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides PluginSettingsBase::calculateDependencies
ViewsFieldFormatter::defaultSettings public static function Defines the default settings for this plugin. Overrides PluginSettingsBase::defaultSettings
ViewsFieldFormatter::getViewArray private function Custom function to generate a view render array.
ViewsFieldFormatter::settingsForm public function Returns a form to configure settings for the formatter. Overrides FormatterBase::settingsForm
ViewsFieldFormatter::settingsSummary public function Returns a short summary for the current formatter settings. Overrides FormatterBase::settingsSummary
ViewsFieldFormatter::viewElements public function Builds a renderable array for a field value. Overrides FormatterInterface::viewElements