Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

FieldTypes, FieldWidgets and FieldFormatters

20/05/2020, by maria

Обзор

Drupal 8 поставляется с большой библиотекой базовых классов, которые позволяют вам работать со своим собственным контентом. Когда дело доходит до сущностей контента, вы хотите использовать поля. Важно понимать Поля, поскольку именно там ваши сущности хранят свои данные.

FieldTypes

Основные типы полей:

 

Типы пользовательских полей
Всякий раз, когда вы хотите представить данные способом, который не предоставляет Drupal; Возможно, вы захотите создать новый тип поля для ваших данных.

Допустим, у вас есть объект содержимого, который содержит конфиденциальные данные. Создатель этого контента может разрешить определенным пользователям доступ к объекту с помощью пароля, который отличается для каждого пользователя. Если мы говорим в таблицах базы данных, вы хотите создать что-то вроде этого:

| entity_id | uid | password      |
-----------------------------------
| 1         | 1   | 'helloworld'  |
| 1         | 2   | 'goodbye'     |

Как видите, наша сущность с идентификатором 1 имеет два разных пароля для двух разных пользователей. Итак, как мы можем реализовать это в Drupal, не создавая таблицу вручную? Мы создаем новый тип поля.

Поскольку Drupal реализует логику поля как Plugin, у нас всегда есть базовый класс, от которого мы можем наследовать, чтобы он работал в Drupal. Для нового типа поля вы хотите создать следующую структуру папок в вашем модуле:
modules/custom/MODULENAME/src/Plugin/Field/FieldType
Это довольно долгий путь и немного раздражает, но Drupal облегчает работу со всеми различными функциями, которые могут сосуществовать в ваших модулях.

Для нашего примера мы создаем файл EntityUserAccessField.php

namespace Drupal\MODULENAME\Plugin\Field\FieldType;
     
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
     
/**
 * @FieldType(
 *   id = "entity_user_access",
 *   label = @Translation("Entity User Access"),
 *   description = @Translation("This field stores a reference to a user and a password for this user on the entity."),
 * )
*/
     
class EntityUserAccessField extends FieldItemBase {
  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    //ToDo: Implement this.
  }
     
  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    //ToDo: Implement this.
  }
}

Как вы можете видеть, тип поля очень похож на сущность контента. На самом деле, нет никакой разницы между этими двумя, но это тема для другого узла;)

Прежде всего, у нас есть аннотация для нашего типа поля

  • @FieldType: это вызывает класс аннотации FieldType из библиотеки Drupal
  • id: это имя машины нашего типа поля, поэтому мы можем использовать его повторно. Убедитесь, что не переопределяете любые предопределенные имена из php и тому подобное.
  • label: Это может быть читаемый пользователем перевод имени машины.
  • description: если метки недостаточно, вы также можете добавить описание для типа поля.

 

Во-вторых, наш класс расширяет FieldItemBase, что заставляет нас реализовать два метода, чтобы мы могли правильно использовать этот тип поля:

  • propertyDefinitions(): Этот метод похож на baseFieldDefinition объекта содержимого (это не то же самое!). Мы можем определить данные, которые появляются в формах сущностей, где используется этот тип поля.
  • schema(): На объектах этот метод устарел, но у нас все еще есть в полях. Этот метод должен реализовывать представление данных поля в базе данных. Может отличаться от свойств.

Поскольку не очень очевидно, что записывать в эти методы, давайте добавим к ним некоторый код для удобства.

public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
  $properties['uid'] = DataDefinition::create('integer')
      ->setLabel(t('User ID Reference'))
      ->setDescription(t('The ID of the referenced user.'))
      ->setSetting('unsigned', TRUE);

  $properties['password'] = DataDefinition::create('string')
      ->setLabel(t('Password'))
      ->setDescription(t('A password saved in plain text. That is not safe dude!'));

  $properties['created'] = DataDefinition::create('timestamp')
    ->setLabel(t('Created Time'))
    ->setDescription(t('The time that the entry was created'));

    // ToDo: Add more Properties.
 
    return $properties;
}

Также возможно сохранить идентификатор пользователя через DataReferenceDefinition, это может быть рассмотрено здесь в будущем.

public static function schema(FieldStorageDefinitionInterface $field_definition) {
  $columns = array(
    'uid' => array(
      'description' => 'The ID of the referenced user.',
      'type' => 'int',
      'unsigned' => TRUE,
    ),
    'password' => array(
      'description' => 'A plain text password.',
      'type' => 'varchar',
      'length' => 255,
    ),
    'created' => array(
      'description' => 'A timestamp of when this entry has been created.',
      'type' => 'int',
    ),

    // ToDo: Add more columns.
  );
 
  $schema = array(
    'columns' => $columns,
    'indexes' => array(),
    'foreign keys' => array(),
  );

  return $schema;
}

Schema() необходима, чтобы Drupal узнал о том, как сохранить наши данные. Столбцы схемы должны быть подмножеством свойств, определенных в propertyDefinitions().

Теперь у нас есть совершенно новый тип поля. Он не имеет никакой логики, как обрабатывать любой ввод данных, но его можно использовать в любом объекте контента в качестве поля. Если вы хотите, вы можете использовать его как baseField для объекта:

public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  // Some fields above.
 
  $fields['entity_user_access'] = BaseFieldDefinition::create('entity_user_access')
    ->setLabel(t('Entity User Access'))
    ->setDescription(t('Specify passwords for any user that want to see this entity.'))
    ->setCardinality(-1); // Ensures that you can have more than just one member
 
  // Even more fields below.
 
  return $fields;
}
  • BaseFieldDefinition::create(): Вы должны использовать имя машины типа поля в create ()
  • setCardinality(-1): Количество элементов представляет количество данных поля, которое может иметь один объект. Например. если мы напишем 2 в нем, только два пользователя смогут получить доступ к объекту, 3 будут 3 пользователями и так далее. -1 представляет бесконечных пользователей.

 

FieldWidget

Если у вас есть пользовательские данные, то вам может потребоваться пользовательское представление этих данных. Виджеты используются для представления того, как пользователь может вводить эти пользовательские данные в формы. Например

  • если в форме необходимо указать целое число, но пользователь может только установить флажок
  • если вы хотите автозаполнение для ваших данных
  • если ввод пароля осуществляется через специальный графический интерфейс

и т.п.

Виджет поля в Drupal можно найти под
modules/custom/MODULENAME/src/Plugin/Field/FieldWidget
это тоже очень длинный путь. На этом этапе должно быть понятно, почему Drupal использует этот стиль для разделения файлов .php. Это очень легко пропустить, какие файлы принадлежат, где.

Мы создаем виджет поля в EntityUserAccessWidget.php

<code>namespace Drupal\MODULENAME\Plugin\Field\FieldWidget;
 
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
 
/**
 * Plugin implementation of the 'entity_user_access_w' widget.
 *
 * @FieldWidget(
 *   id = "entity_user_access_w",
 *   label = @Translation("Entity User Access - Widget"),
 *   description = @Translation("Entity User Access - Widget"),
 *   field_types = {
 *     "entity_user_access",
 *   },
 *   multiple_values = TRUE,
 * )
 */
 
class EntityUserAccessWidget extends WidgetBase {
  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    // ToDo: Implement this.
  }
}</code>

Вы уже заметили? Drupal 8 использует этот стиль кода снова и снова, если вы хотите реализовать функции. Есть аннотация и базовый класс, который вы должны унаследовать. Да, Drupal может использовать это!

  • @FieldWidget: определяет класс аннотации
  • id: имя машины виджета
  • field_types: массив имен машин типа поля, которые могут использовать этот виджет
  • множественные значения: по умолчанию FALSE. Если это правда, это позволит вам отправить более одного значения в форме сущности

Если вы теперь хотите использовать этот виджет с вашим типом поля, вы должны отредактировать аннотацию типа поля следующим образом

// ...

/**
 * @FieldType(
 *   id = "entity_user_access",
 *   label = @Translation("Entity User Access"),
 *   description = @Translation("This field stores a reference to a user and a password for this user on the entity."),
 *   default_widget = "entity_user_access_w",
 * )
 */
     
// ...

Да все сделано! Нет, подождите, пока ничего не происходит, потому что мы должны реализовать formElement() в нашем виджете.

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $element['userlist'] = array(
      '#type' => 'select',
      '#title' => t('User'),
      '#description' => t('Select group members from the list.'),
      '#options' => array(
         0 => t('Anonymous'),
         1 => t('Admin'),
         2 => t('foobar'),
         // This should be implemented in a better way!
       ),
  
    );
  
    $element['passwordlist'] = array(
      '#type' => 'password',
      '#title' => t('Password'),
      '#description' => t('Select a password for the user'),
    );

    //setting default value to all fields from above
    $childs = Element::children($element);
    foreach ($childs as $child) {
        $element[$child]['#default_value'] = isset($items[$delta]->{$child}) ? $items[$delta]->{$child} : NULL;
    }
   
    return $element;
}

Если вы сейчас откроете форму с этим виджетом, то увидите как минимум два поля ввода. Один - это выбор пользователя, а другой - поле пароля. Если вы хотите реализовать способ сохранения данных, вам нужно реализовать методы проверки в этом виджете или в форме объекта. См. Drupal 8 Form API для получения дополнительной информации.

К настоящему времени вы проделали большую часть работы с настраиваемым полем. Если вы не понимаете, что происходит, просто попробуйте код или посмотрите на основные модули для более глубокого знания темы.

FieldFormatters

Последнее, что не хватает, - это представление данных в так называемом режиме просмотра объекта - кстати, виджет - это режим формы. Это чаще всего случается, если вы звоните объекту через yourdrupalpage.com/myentity/1/view

Поскольку здесь не так много разговоров, мы перейдем непосредственно к коду. Под модулями /custom/MODULENAME/src/Plugin/Field/FieldFormatter создайте EntityUserAccessFormatter.php

namespace Drupal\MODULENAME\Plugin\Field\FieldFormatter;
     
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
     
/**
 * Plugin implementation of the 'entity_user_access_f' formatter.
 *
 * @FieldFormatter(
 *   id = "entity_user_access_f",
 *   label = @Translation("Entity User Access - Formatter"),
 *   description = @Translation("Entity User Access - Formatter"),
 *   field_types = {
 *     "entity_user_access",
 *   }
 * )
 */
     
class EntityUserAccessFormatter extends FormatterBase {
  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = array();
     
    foreach ($items as $delta => $item) {
      $elements[$delta] = array(
        'uid' => array(
          '#markup' => \Drupal\user\Entity\User::load($item->uid)->getUsername(),
          ),
        // Add more content
      );
    }
     
    return $elements;
  }
}

Этот пример имеет очень похожую аннотацию на виджет, поэтому нам не нужно много говорить об этом. ViewElements() просто показывает имя пользователя сохраненного идентификатора пользователя нашего типа поля. Реализация доступа должна быть выполнена в объекте. Таким образом, эта реализация покажет все имена пользователей, которые имеют пароль на объекте.

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.