Drupal 8: Fields reborn

85
D8: Fields reborn Pablo López - @plopesc DrupalCamp Spain Valencia, May 2014

Transcript of Drupal 8: Fields reborn

Page 1: Drupal 8: Fields reborn

D8: Fields reborn

Pablo López - @plopesc

DrupalCamp SpainValencia, May 2014

Page 2: Drupal 8: Fields reborn

Let's do it with Drupal 8

Rubén Teijeiro

Was earlier today :)

Migrate in core

Christian López Espínola

Saturday 1pm

El universo javascript en Drupal 8

Ramón Vilar

Saturday 5pm

Modes and formatters

Jesús Sánchez Balsera

Sunday 10am

Related sessions

Page 3: Drupal 8: Fields reborn

Credits: @yched & @swentel

Broken record

Page 4: Drupal 8: Fields reborn

Amazing changes• More power for site builders

• Fields & instances moved to config entities

• Formatters, Widgets and Field types are plugins

• Formatters & Widgets work on all fields

• Moved from field to entity storage

Field & Entity API are now one happy big family

Page 5: Drupal 8: Fields reborn
Page 6: Drupal 8: Fields reborn
Page 7: Drupal 8: Fields reborn
Page 8: Drupal 8: Fields reborn

... for the 3rd time

Page 9: Drupal 8: Fields reborn

yched, amateescu, swentel

fago, Berdir, plach,

effulgentsia, andypost...

Page 10: Drupal 8: Fields reborn

Site building

features

Page 11: Drupal 8: Fields reborn

New field types• More power for site builders out of the box

• Not always full ports of the corresponding D7 modules

• Sometimes they are not even modules anymore (email, number, more to

come)

Page 12: Drupal 8: Fields reborn

Entity reference

Fairly complete port of D7 entity_reference.module

Taxonomy, file, image fields: still separate field types

Page 13: Drupal 8: Fields reborn

Date / Datetime

The "repeat" features from D7 stays in contrib

Page 14: Drupal 8: Fields reborn

Link

Basic version (URL, text)

No support for internal paths (e.g. node/1)

Now supports internal paths (https://drupal.org/node/2054011)

Page 15: Drupal 8: Fields reborn

Email• Input validation

• No anti-spam support out of the box

Phone• HTML5 "tel" input

• Basic display (optional "tel:" link)

Page 16: Drupal 8: Fields reborn

In Place Editing

Page 17: Drupal 8: Fields reborn

Fieldable blocks

aka Beans in core

Page 18: Drupal 8: Fields reborn

Form modes• Similar to "view modes", on the form side

• Build several form variants for your entities:

• User registration / user edit

• Creation / edit forms

• Fields can now be hidden in forms

Beware of required fields with no default values...

Page 19: Drupal 8: Fields reborn

Form modes UI

Page 20: Drupal 8: Fields reborn

Field Overview UI

Page 21: Drupal 8: Fields reborn

Fields are tied to an entity typeA field cannot be "shared" across entity types,

only across bundles of a given entity type

Consequences:

• No need to clutter the field name ('comment_body')

node 'body' != comment 'body'

• A $field_name alone is not enough:

$field = FieldConfig::loadByName($entity_type, $field_name);

<div class="field-name-body field-node--body">

Page 22: Drupal 8: Fields reborn
Page 23: Drupal 8: Fields reborn

There will be code

APIs !!!

Page 24: Drupal 8: Fields reborn

Disclaimer: still in flux...

Data structures

Page 25: Drupal 8: Fields reborn

Data model recap• Field value: list of multiple items

• Each item: list of properties depending on the field type

• Fields can be translatable

Page 26: Drupal 8: Fields reborn

Data model - D7

Page 27: Drupal 8: Fields reborn

Data model - D7

Page 28: Drupal 8: Fields reborn

D8 Entity translation• Entity level, not field level

$entity->getTranslation($langcode)

• Implements EntityInterface:

$entity->getTranslation($langcode)->field_foo

• Facets of the same entity

$entity is always loaded/saved as a whole

Page 29: Drupal 8: Fields reborn

Data model - D8

Page 30: Drupal 8: Fields reborn

Data model - D8

Page 31: Drupal 8: Fields reborn

Data model - D8

Page 32: Drupal 8: Fields reborn

Data model - D8

Page 33: Drupal 8: Fields reborn

Data model - D8

Page 34: Drupal 8: Fields reborn

Data model - D8

Page 35: Drupal 8: Fields reborn

Data model - D8

Page 36: Drupal 8: Fields reborn

Data model - D8

Page 37: Drupal 8: Fields reborn

In a nutshell• $entity, $entity->getTranslation('fr') Entity

• $entity->field_foo FieldItemList

• $entity->field_foo[$delta] FieldItem

• $entity->field_foo[$delta]->property

• $entity->field_foo->property for delta 0

Page 38: Drupal 8: Fields reborn

Navigating

Field items are "smart":

$items->getEntity() $items->getLangcode()

$items->getFieldDefinition()

$instances = field_info_instances($entity_type, $bundle);foreach ($instances as $field_name => $instance) { $items = $entity[$field_name][$langcode]; do_something($items, $entity, $langcode, $instance);} D7

foreach ($entity as $field_name => $items) { $items->doSomething(); // or $object->doSomething($items);} D8

Page 39: Drupal 8: Fields reborn

Everything is a field

Page 40: Drupal 8: Fields reborn

Everything

Page 41: Drupal 8: Fields reborn

Everything in a ContentEntity is a field• $node->title FieldItemListInterface

• $node->body FieldItemListInterface

• $node->field_custom FieldItemListInterface

Drawback: $node->title->value;

Mitigation: $node->getTitle();

Page 42: Drupal 8: Fields reborn

Unified APIs and features• field translation

• field access

• constraints / validation

• output in REST

• widgets / formatters

• In Place Editing

• EntityQuery (EFQ in D7)

• field cache (= entity cache!)

Page 43: Drupal 8: Fields reborn

Naming is hard...

Page 44: Drupal 8: Fields reborn

Different kinds of fields...• Base fields (former "entity properties")

• defined in code: MyEntityType::baseFieldDefinitions()

• Bundle fields (variations of base fields per bundle)

• defined in code: MyEntityType::bundleFieldDefinitions()

• e.g. node title

• Configurable fields (former "fields") - field.module

• defined in config through an admin UI

Properties: what you find inside a FieldItem ('value', 'format', 'target_id'...)

Page 45: Drupal 8: Fields reborn

Code architecture• /core/lib/Drupal/Core/Field

The Field system, widgets, formatters...

baked in the lifecycle of (Content)Entities

• /core/modules/field.module

Configurable fields

Page 46: Drupal 8: Fields reborn

Unified FieldStorageDefinitionInterfaceField definition: name, type, label, cardinality, settings, description...

• D7: $field / $instance arrays

$field['type'], $instance['settings']['max']

• D8: FieldStorageDefinitionInterface

$definition->getType(), $definition->getSetting('max')...

• Autocompletion, documentation...

• $field / $instance are mostly abstracted away

Page 47: Drupal 8: Fields reborn

Unified FieldStorageDefinitionInterface

Implementations:

• configurable fields: FieldConfig, FieldInstanceConfig

• base fields: FieldDefinition

interface FieldStorageDefinitionInterface { public function getName(); public function getType(); public function getSettings(); public function getSetting($setting_name); public function isTranslatable(); public function getDescription(); public function getCardinality(); public function isMultiple(); // Some others...

Page 48: Drupal 8: Fields reborn

Grabbing field definitionsfield_info_fields()field_info_field($field_name)field_info_instances($entity_type, $bundle)field_info_instances($entity_type, $bundle, $field_name) D7

$entity_manager->getFieldDefinitions($entity_type, $bundle)

// On an entity:$entity->getFieldDefinitions()$entity->getFieldDefinition($field_name)$entity->hasField($field_name)

// On items:$item->getFieldDefinition(), $items->getFieldDefinition() D8D8D8

Page 49: Drupal 8: Fields reborn

Field storage• D7: pluggable "field storage engines", field per field

• D8: job of the EntityStorageController

• Entities are stored as a whole

• Easier to swap an alternate storage (Mongo...)

• Base class for "generic SQL field storage"

• Per-field tables (same as D7)

• Handles revisions, translations, multi values, multi properties

• ... only for configurable fields

Page 50: Drupal 8: Fields reborn

Field storage (@todo, fingers crossed)

Problem:

• Supporting translatable base fields is hard

• 3rd party code can only add fields through config

Plan:

• Let base fields control how they are stored:

• Optimized storage in the entity base tables

• Generic storage, free translation support

• Custom storage somewhere else...

Page 51: Drupal 8: Fields reborn

CMI

Page 52: Drupal 8: Fields reborn

CMI• Configuration in YML files

• Deployable between environments

• Can be shipped in modules

• ConfigEntities

Page 53: Drupal 8: Fields reborn

Field definition structures• $field : "a data bucket"

(name, type, cardinality, entity type, ...)

• $instance : "a field attached to a specific [entity_type, bundle]"

(label, description, required, ...)

D7:

• deep arrays of hell

• {field_config}, {field_config_instance} db tables

Page 54: Drupal 8: Fields reborn
Page 55: Drupal 8: Fields reborn

D8: Field structures as ConfigEntities• $field :

• FieldConfig (entity type: 'field_config')

• field.field.[entity_type].[field_name].yml

• $instance :

• FieldInstanceConfig (entity type: 'field_instance_config')

• field.instance.[entity_type].[bundle].[field_name].yml

Page 56: Drupal 8: Fields reborn

field.field.node.body.ymlid: node.bodyuuid: d9a197db-89e1-4b8b-b50c-122083aeacb1status: truelangcode: enname: bodyentity_type: nodetype: text_with_summarysettings: { }module: textlocked: falsecardinality: 1translatable: falseindexes: { }dependencies: module: - node - text

123456789101112131415161718

Page 57: Drupal 8: Fields reborn

field.instance.node.article.body.ymlid: node.article.bodyuuid: a378b8b5-39ac-44da-bc07-7ad0a2479a6astatus: truelangcode: enfield_uuid: d9a197db-89e1-4b8b-b50c-122083aeacb1field_name: bodyentity_type: nodebundle: articlelabel: Bodydescription: ''required: falsedefault_value: { }default_value_function: ''settings: display_summary: true text_processing: truedependencies: entity: - field.field.node.body - node.type.articlefield_type: text_with_summary

12345678910111213141516171819202122

Page 58: Drupal 8: Fields reborn

CRUD API - D7Dedicated functions:

+ associated hooks...

field_create_field(array());field_update_field(array());field_delete_field($field_name);............_instance(array()); D7

Page 59: Drupal 8: Fields reborn

CRUD API - D8Regular Entity CRUD API:

+ regular hook_entity_[ENTITY_TYPE]_[OP]() hooks

$field = entity_create('field_config', array( 'name' => 'body', 'entity_type' => 'node', 'type' => 'text_with_summary',);$field->save();

$field->cardinality = 2;$field->save();

$field->delete(); D8

Page 60: Drupal 8: Fields reborn

EntityDisplay

Page 61: Drupal 8: Fields reborn

Display settings in D7Scattered around:

• $instance['display'][$view_mode]

• 'field_bundle_settings_[entity_type]_[bundle]' variable (ew...)

• 3rd party (Display suite, Field groups): in their own tables...

Each with separate "alter" hooks

Loads needless stuff in memory

Page 62: Drupal 8: Fields reborn

EntityViewDisplay (ConfigEntity)• "Full recipe" for displaying an entity in a given view mode

• Lists "components", with order and settings

entity_view($entity, $view_mode) :

• Loads the relevant display

• Alters it as a whole

• Injects it into all the callstack

hook_entity_view_display_alter(EntityViewDisplay $display);

EntityViewBuilder::buildContent(array $entities, array $displays);hook_entity_view(EntityInterface $entity, EntityViewDisplay $display);

Page 63: Drupal 8: Fields reborn

entity.display.node.article.teaser.ymlid: node.article.teaseruuid: ad345f0f-ff44-4210-8900-b8bdfcd8e671targetEntityType: nodebundle: articlemode: teasercontent: field_image: label: hidden type: image settings: image_style: medium image_link: content weight: -1 body: label: hidden type: text_summary_or_trimmed weight: 0 settings: trim_length: 600 field_tags: type: taxonomy_term_reference_link weight: 10 label: above settings: { }

123456789101112131415161718192021222324

Page 64: Drupal 8: Fields reborn

EntityViewDisplay API$display = entity_get_display('node', 'article', 'teaser');

$display->setComponent('body', array( 'type' => 'text_trimmed', 'settings' => array('trim_length' => '600'))->removeComponent('image')->save();

$options = $display->getComponent('body');// array(// 'type' => 'text_default'// 'weight' => 0,// 'settings' => array(),// 'label' => 'hidden',// )// or NULL if 'body' is hidden

Page 65: Drupal 8: Fields reborn

EntityFormDisplay• Same thing for forms :-)

• Allows "form modes"

Page 66: Drupal 8: Fields reborn

Field types

Page 67: Drupal 8: Fields reborn

D7: field type "hooks"function hook_field_info() { }function hook_field_schema($field) { }function hook_field_settings_form($field, $instnce, $has_data) { }function hook_field_instance_settings_form($field, $instance) { }

function hook_field_load($entity_type, $entities, $field, $instances, $langcodefunction hook_field_validate($entity_type, $entity, $field, $instance, $langcodefunction hook_field_presave($entity_type, $entity, $field, $instance, $langcodefunction hook_field_insert($entity_type, $entity, $field, $instance, $langcodefunction hook_field_update($entity_type, $entity, $field, $instance, $langcodefunction hook_field_delete($entity_type, $entity, $field, $instance, $langcodefunction hook_field_delete_revision($entity_type, $entity, $field, $instance,

function hook_field_is_empty($item, $field) { }function hook_field_prepare_view($entity_type, $entities, $field, $instances, function hook_field_prepare_translation($entity_type, $entity, $field, $instance

Page 68: Drupal 8: Fields reborn

FieldType plugin type• Discovery folder: Plugin/Field/FieldType

• Annotation: FieldType

• Interface: FieldItemInterface

Page 69: Drupal 8: Fields reborn

FieldItemInterface Now in 8.x !

interface FieldItemInterface { public static function schema(FieldDefinitionInterface $field_definition); public static function propertyDefinitions(); public function getConstraints(); public function isEmpty();

public static function defaultSettings(); public static function defaultInstanceSettings();

public function settingsForm(array $form, array &$form_state, $has_data); public function instanceSettingsForm(array $form, array &$form_state);

public function prepareCache(); public function preSave(); public function insert(); public function update(); public function delete();

Page 70: Drupal 8: Fields reborn

/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.phpnamespace Drupal\link\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;use Drupal\Core\Field\FieldStorageDefinitionInterface;use Drupal\Core\TypedData\DataDefinition;use Drupal\Core\TypedData\MapDataDefinition;use Drupal\link\LinkItemInterface;

/** * Plugin implementation of the 'link' field type. * * @FieldType( * id = "link", * label = @Translation("Link"), * description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."), * default_widget = "link_default", * default_formatter = "link", * constraints = {"LinkType" = {}} * ) */class LinkItem extends FieldItemBase implements LinkItemInterface {

/**

1234567891011121314151617181920212223

Page 71: Drupal 8: Fields reborn

Formatters

Page 72: Drupal 8: Fields reborn

D7: "hooks" (well, magic callbacks)

• Lost within the 50 other functions in your file

• switch dance when you implement several formatters

function mymodule_field_formatter_info() { }

function mymodule_field_formatter_settings_form($field, $instance, $view_mode,function mymodule_field_formatter_settings_summary($field, $instance, $view_mode

function mymodule_field_formatter_prepare_view($entity_type, $entities, $fieldfunction mymodule_field_formatter_view($entity_type, $entity, $field, $instance

Page 73: Drupal 8: Fields reborn

D8: FieldFormatter plugins

• All logic nicely self contained.

• OO! Classes! ( = Inheritance!)

• FormatterBase class provides stubs and helpers

namespace Drupal\Core\Field;

interface FormatterInterface extends PluginSettingsInterface { public static function defaultSettings() public function settingsForm(array $form, array &$form_state); public function settingsSummary(); public function prepareView(array $entities_items); public function view(FieldItemListInterface $items); public function viewElements(FieldItemListInterface $items);}

Page 74: Drupal 8: Fields reborn

D8: FieldFormatter plugin type• No more _info() hook

• Expose your class as a "Field formatter" plugin

• Discovery folder: Plugin/Field/FieldFormatter

• Annotation: FieldFormatter

Page 75: Drupal 8: Fields reborn

Inheritance example

Page 76: Drupal 8: Fields reborn

Working in formatters• Access the configuration of the formatter:

$this->getSetting('foo'), $this->getSettings()

• Access the definition of the field:

$this->fieldDefinition->getType()

$this->getFieldSetting('foo'), $this->getFieldSettings()

• Manipulate the FieldItemList and FieldItem objects:

$items->getEntity(), $items->getLangcode()

You really want to extend FormatterBase...

Page 77: Drupal 8: Fields reborn

Widgets

Page 78: Drupal 8: Fields reborn

FieldWidget plugin type• Discovery folder: Plugin/Field/FieldWidget

• Annotation: FieldWidget

• WidgetBase class

interface WidgetInterface extends WidgetBaseInterface { public static function defaultSettings() public function settingsForm(array $form, array &$form_state); public function settingsSummary(); public function formElement(FieldItemListInterface $items, $delta, array $element public function errorElement(array $element, ConstraintViolationInterface $violation public function massageFormValues(array $values, array $form, array &$form_state}

Page 79: Drupal 8: Fields reborn

Working in widgets• Same as formatters...

• New in D8: massageFormValues()

Values produced by the FAPI structure → proper field values

• FAPI / render #callbacks:

$form['#process'] = '_my_process_helper';

$form['#process'] = array($this, 'myProcessHelper');

Please use static methods instead, (avoids serializing $this):

$form['#process'] = array(get_class($this), 'myProcessHelper');

Page 80: Drupal 8: Fields reborn

Widgets / formatters on base fields• Powerful flexibility

• Free support for "In Place Editing"

• Hey, EntityDisplays let us store the settings!

Page 81: Drupal 8: Fields reborn

/core/modules/node/lib/Drupal/node/Entity/Node.php public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['title'] = FieldDefinition::create('string') ->setLabel(t('Title')) ->setDescription(t('The title of this node, always treated as non-markup plain text.')) // ... ->setSettings(array( // Array settings... )) ->setDisplayOptions('view', array( // Array options... )) ->setDisplayOptions('form', array( // Array options... )) ->setDisplayConfigurable('form', TRUE); //.... } public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $node_type = node_type_load($bundle); $fields = array(); if (isset($node_type->title_label)) { $fields['title'] = clone $base_field_definitions['title']; $fields['title']->setLabel($node_type->title_label); } return $fields; }

1234567891011121314151617181920212223

Page 82: Drupal 8: Fields reborn

API changes / beta targets coming up• Content translation sync settings to own storage

• A unified repository of field definitions

• Remove field_info_*() (Patch submitted)

• Apply formatters and widgets to * fields (Partially)

• Make delete fields work with config synchronization

Page 83: Drupal 8: Fields reborn

Get involved• [META] Complete the Entity Field API

http://drupal.org/node/2095603

• http://entity.worldempire.ch/

• Weekly IRC meetings: #drupal-entity - Thursday, 18:00 CET

Page 84: Drupal 8: Fields reborn

Thanks!

Page 85: Drupal 8: Fields reborn

Questions?