From b360aa0491faef2acbfb39afbce0f1b8fb4ce0c2 Mon Sep 17 00:00:00 2001 From: Daniel Wehner Date: Tue, 7 Jun 2016 08:26:31 +0200 Subject: [PATCH 1/2] add the field link formatter --- field_formatter.module | 12 + src/Plugin/Field/FieldFormatter/FieldLink.php | 41 +++ .../Field/FieldFormatter/FieldWrapperBase.php | 273 ++++++++++++++++++ .../Field/FieldFormatter/FieldLinkTest.php | 55 ++++ 4 files changed, 381 insertions(+) create mode 100644 field_formatter.module create mode 100644 src/Plugin/Field/FieldFormatter/FieldLink.php create mode 100644 src/Plugin/Field/FieldFormatter/FieldWrapperBase.php create mode 100644 tests/src/Kernel/Plugin/Field/FieldFormatter/FieldLinkTest.php diff --git a/field_formatter.module b/field_formatter.module new file mode 100644 index 0000000..047a98e --- /dev/null +++ b/field_formatter.module @@ -0,0 +1,12 @@ +getDefinitions()); + + $info['field_link']['field_types'] = $field_types; +} diff --git a/src/Plugin/Field/FieldFormatter/FieldLink.php b/src/Plugin/Field/FieldFormatter/FieldLink.php new file mode 100644 index 0000000..bf0052f --- /dev/null +++ b/src/Plugin/Field/FieldFormatter/FieldLink.php @@ -0,0 +1,41 @@ +getFieldOutput($items, $langcode); + + $elements = []; + foreach (Element::children($field_output) as $key) { + $elements[$key] = [ + '#type' => 'link', + '#url' => $items->getEntity()->toUrl(), + '#title' => $field_output[$key], + ]; + } + return $elements; + } + +} diff --git a/src/Plugin/Field/FieldFormatter/FieldWrapperBase.php b/src/Plugin/Field/FieldFormatter/FieldWrapperBase.php new file mode 100644 index 0000000..1ace49a --- /dev/null +++ b/src/Plugin/Field/FieldFormatter/FieldWrapperBase.php @@ -0,0 +1,273 @@ +formatterPluginManager = $formatter_plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['label'], + $configuration['view_mode'], + $configuration['third_party_settings'], + $container->get('plugin.manager.field.formatter') + ); + } + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'field_name' => '', + 'type' => '', + 'settings' => [], + ]; + } + + /** + * Get field definition for given field storage definition + * + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition + * The field storage definition. + * + * @return \Drupal\Core\Field\BaseFieldDefinition + * The field definition. + */ + protected function getFieldDefinition(FieldStorageDefinitionInterface $field_storage_definition) { + return BaseFieldDefinition::createFromFieldStorageDefinition($field_storage_definition); + } + + /** + * Get all available formatters by loading available ones and filtering out. + * + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition + * The field storage definition. + * + * @return string[] + * The field formatter labels keys by plugin ID. + */ + protected function getAvailableFormatterOptions(FieldStorageDefinitionInterface $field_storage_definition) { + $formatters = $this->formatterPluginManager->getOptions($field_storage_definition->getType()); + $formatter_instances = array_map(function($formatter_id) { + $configuration = [ + 'field_definition' => $this->fieldDefinition, + 'settings' => [], + 'label' => '', + 'view_mode' => '', + 'third_party_settings' => [], + ]; + return $this->formatterPluginManager->createInstance($formatter_id, $configuration); + }, array_combine(array_keys($formatters), array_keys($formatters))); + $filtered_formatter_instances = array_filter($formatter_instances, function (FormatterInterface $formatter) { + return $formatter->isApplicable($this->fieldDefinition); + }); + $options = array_map(function (FormatterInterface $formatter) { + return $formatter->getPluginDefinition()['label']; + }, $filtered_formatter_instances); + return $options; + } + + /** + * Ajax submit callback for formatter type change. + * + * @param array $form + * The form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * + * @return array + * The replaced form substructure. + */ + public static function onFormatterTypeChange(array $form, FormStateInterface $form_state) { + return $form['fields'][$form_state->getStorage()['plugin_settings_edit']]['plugin']['settings_edit_form']['settings']['settings']; + } + /** + * Rebuilds the form on select submit. + * + * @param array $form + * The form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public static function rebuildSubmit(array $form, FormStateInterface $form_state) { + $form_state->setRebuild(TRUE); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + // Name of the field this formatter is currently displaying. + $field_name = $this->fieldDefinition->getName(); + $triggering_element = $form_state->getTriggeringElement(); + + $field_storage = $this->fieldDefinition->getFieldStorageDefinition(); + $formatter_options = $this->getAvailableFormatterOptions($field_storage); + if ($field_name) { + // Form state is not updated as long just select elements are triggered. + $formatter_type = $this->getSetting('type'); + if ($triggering_element['#name'] == "fields[$field_name][settings_edit_form][settings][field_name]") { + $formatter_type = key($formatter_options); + } + else if ($triggering_element['#name'] == "fields[$field_name][settings_edit_form][settings][type]") { + // If triggered element is formatter set correct formatter type. + $formatter_type = $triggering_element['#value']; + } + + $form['type'] = [ + '#type' => 'select', + '#title' => $this->t('Formatter'), + '#options' => $formatter_options, + '#default_value' => $formatter_type, + // Note: We cannot use ::foo syntax, because the form is the entity form + // display. + '#ajax' => [ + 'callback' => [static::class, 'onFormatterTypeChange'], + 'wrapper' => 'field-formatter-settings-ajax', + 'method' => 'replace', + ], + '#submit' => [[static::class, 'rebuildSubmit']], + '#executes_submit_callback' => TRUE, + ]; + + $options = [ + 'field_definition' => $this->getFieldDefinition($field_storage), + 'configuration' => [ + 'type' => $formatter_type, + 'settings' => $this->getSetting('settings'), + 'label' => '', + 'weight' => 0, + ], + 'view_mode' => '_custom', + ]; + + // Get the formatter settings form. + $settings_form = ['#value' => []]; + if ($formatter = $this->formatterPluginManager->getInstance($options)) { + $settings_form = $formatter->settingsForm($form, $form_state); + } + $form['settings'] = $settings_form; + $form['settings']['#prefix'] = '
'; + $form['settings']['#suffix'] = '
'; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + if ($type = $this->getSetting('type')) { + $summary[] = $this->t('Formatter %type used.', ['%type' => $type]); + } + else { + $summary[] = $this->t('Formatter not configured.'); + } + + return $summary; + } + + /** + * Returns a view display object used to render the actual content of the + * field. + * @param string $bundle_id + * The bundle ID. + * + * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface + */ + protected function getViewDisplay($bundle_id) { + if (!isset($this->viewDisplay[$bundle_id])) { + + $display = EntityViewDisplay::create([ + 'targetEntityType' => $this->fieldDefinition->getTargetEntityTypeId(), + 'bundle' => $bundle_id, + 'status' => TRUE, + ]); + $display->setComponent($this->fieldDefinition->getName(), [ + 'type' => $this->getSetting('type'), + 'settings' => $this->getSetting('settings'), + ]); + $this->viewDisplay[$bundle_id] = $display; + } + return $this->viewDisplay[$bundle_id]; + } + + /** + * Returns the wrapped field output. + */ + protected function getFieldOutput(FieldItemListInterface $items, $langcode) { + /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ + $entity = $items->getEntity(); + + $build = $this->getViewDisplay($entity->bundle())->build($entity); + return isset($build[$this->fieldDefinition->getName()]) ? $build[$this->fieldDefinition->getName()] : []; + } + +} diff --git a/tests/src/Kernel/Plugin/Field/FieldFormatter/FieldLinkTest.php b/tests/src/Kernel/Plugin/Field/FieldFormatter/FieldLinkTest.php new file mode 100644 index 0000000..8f67ecb --- /dev/null +++ b/tests/src/Kernel/Plugin/Field/FieldFormatter/FieldLinkTest.php @@ -0,0 +1,55 @@ +installEntitySchema('entity_test'); + $this->installEntitySchema('user'); + } + + /** + * @covers ::viewElements + * @covers ::getFieldOutput + * @covers ::getViewDisplay + */ + public function testFieldLinkFormatter() { + $entity = EntityTest::create([ + 'name' => 'test name', + ]); + $entity->save(); + + $build = $entity->name->view([ + 'type' => 'field_link', + 'settings' => [ + 'type' => 'string', + ], + ]); + + $output = \Drupal::service('renderer')->renderRoot($build); + $this->setRawContent($output); + + $href = (string) $this->cssSelect('a')[0]->attributes()['href']; + $this->assertEquals($href, $entity->toUrl()->toString(TRUE)->getGeneratedUrl()); + $this->assertText('test name'); + } + +} From a43a5cb2cac37fc3237dc1548e307c77fb20fa7d Mon Sep 17 00:00:00 2001 From: Daniel Wehner Date: Tue, 7 Jun 2016 08:46:56 +0200 Subject: [PATCH 2/2] add config schema --- config/schema/field_formatter.schema.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/schema/field_formatter.schema.yml b/config/schema/field_formatter.schema.yml index 5fdb3a7..b7fbdf0 100644 --- a/config/schema/field_formatter.schema.yml +++ b/config/schema/field_formatter.schema.yml @@ -15,3 +15,11 @@ field.formatter.settings.field_formatter_with_inline_settings: type: string settings: type: field.formatter.settings.[%parent.type] + +field.formatter.settings.field_link: + type: mapping + mapping: + type: + type: string + settings: + type: field.formatter.settings.[%parent.type]