D8: Fields reborn
Pablo López - @plopesc
DrupalCamp Spain
Valencia, May 2014
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
Credits: @yched & @swentel
Broken record
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
... for the 3rd time
yched, amateescu, swentel
fago, Berdir, plach,
effulgentsia, andypost...
Site building
features
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)
Entity reference
Fairly complete port of D7 entity_reference.module
Taxonomy, file, image fields: still separate field types
Date / Datetime
The "repeat" features from D7 stays in contrib
Link
Basic version (URL, text)
No support for internal paths (e.g. node/1)
Now supports internal paths (https://drupal.org/node/2054011)
Email
• Input validation
• No anti-spam support out of the box
Phone
• HTML5 "tel" input
• Basic display (optional "tel:" link)
In Place Editing
Fieldable blocks
aka Beans in core
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...
Form modes UI
Field Overview UI
Fields are tied to an entity type
A 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">
There will be code
APIs !!!
Disclaimer: still in flux...
Data structures
Data model recap
• Field value: list of multiple items
• Each item: list of properties depending on the field type
• Fields can be translatable
Data model - D7
Data model - D7
D8 Entity translation
• Entity level, not field level
$entity->getTranslation($langcode)
• Implements EntityInterface:
$entity->getTranslation($langcode)->field_foo
• Facets of the same entity
$entityis always loaded/saved as a whole
Data model - D8
Data model - D8
Data model - D8
Data model - D8
Data model - D8
Data model - D8
Data model - D8
Data model - D8
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->propertyfor delta 0
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
Everything is a field
Everything
Everything in a ContentEntity is a field
• $node->title FieldItemListInterface
• $node->body FieldItemListInterface
• $node->field_custom FieldItemListInterface
Drawback: $node->title->value;
Mitigation: $node->getTitle();
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!)
Naming is hard...
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'...)
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
Unified FieldStorageDefinitionInterface
Field 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
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...
Grabbing field definitions
field_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
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
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...
CMI
CMI
• Configuration in YML files
• Deployable between environments
• Can be shipped in modules
• ConfigEntities
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
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
field.field.node.body.yml
id: node.body
uuid: d9a197db-89e1-4b8b-b50c-122083aeacb1
status: true
langcode: en
name: body
entity_type: node
type: text_with_summary
settings: { }
module: text
locked: false
cardinality: 1
translatable: false
indexes: { }
dependencies:
module:
- node
- text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
field.instance.node.article.body.yml
id: node.article.body
uuid: a378b8b5-39ac-44da-bc07-7ad0a2479a6a
status: true
langcode: en
field_uuid: d9a197db-89e1-4b8b-b50c-122083aeacb1
field_name: body
entity_type: node
bundle: article
label: Body
description: ''
required: false
default_value: { }
default_value_function: ''
settings:
display_summary: true
text_processing: true
dependencies:
entity:
- field.field.node.body
- node.type.article
field_type: text_with_summary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CRUD API - D7
Dedicated functions:
+ associated hooks...
field_create_field(array());
field_update_field(array());
field_delete_field($field_name);
............_instance(array()); D7
CRUD API - D8
Regular 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
EntityDisplay
Display settings in D7
Scattered 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
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);
entity.display.node.article.teaser.yml
id: node.article.teaser
uuid: ad345f0f-ff44-4210-8900-b8bdfcd8e671
targetEntityType: node
bundle: article
mode: teaser
content:
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
EntityFormDisplay
• Same thing for forms :-)
• Allows "form modes"
Field types
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, $langcode
function hook_field_validate($entity_type, $entity, $field, $instance, $langcode
function hook_field_presave($entity_type, $entity, $field, $instance, $langcode
function hook_field_insert($entity_type, $entity, $field, $instance, $langcode
function hook_field_update($entity_type, $entity, $field, $instance, $langcode
function hook_field_delete($entity_type, $entity, $field, $instance, $langcode
function 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
FieldType plugin type
• Discovery folder: Plugin/Field/FieldType
• Annotation: FieldType
• Interface: FieldItemInterface
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();
/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
namespace DrupallinkPluginFieldFieldType;
use DrupalCoreFieldFieldItemBase;
use DrupalCoreFieldFieldStorageDefinitionInterface;
use DrupalCoreTypedDataDataDefinition;
use DrupalCoreTypedDataMapDataDefinition;
use DrupallinkLinkItemInterface;
/**
* 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 bl
* default_widget = "link_default",
* default_formatter = "link",
* constraints = {"LinkType" = {}}
* )
*/
class LinkItem extends FieldItemBase implements LinkItemInterface {
/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Formatters
D7: "hooks" (well, magic callbacks)
• Lost within the 50 other functions in your file
• switchdance 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, $field
function mymodule_field_formatter_view($entity_type, $entity, $field, $instance
D8: FieldFormatter plugins
• All logic nicely self contained.
• OO! Classes! ( = Inheritance!)
• FormatterBase class provides stubs and helpers
namespace DrupalCoreField;
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);
}
D8: FieldFormatter plugin type
• No more _info() hook
• Expose your class as a "Field formatter" plugin
• Discovery folder: Plugin/Field/FieldFormatter
• Annotation: FieldFormatter
Inheritance example
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...
Widgets
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
}
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');
Widgets / formatters on base fields
• Powerful flexibility
• Free support for "In Place Editing"
• Hey, EntityDisplays let us store the settings!
/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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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
Thanks!
Questions?

Drupal 8: Fields reborn

  • 1.
    D8: Fields reborn PabloLópez - @plopesc DrupalCamp Spain Valencia, May 2014
  • 2.
    Let's do itwith 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
  • 3.
    Credits: @yched &@swentel Broken record
  • 4.
    Amazing changes • Morepower 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
  • 8.
    ... for the3rd time
  • 9.
    yched, amateescu, swentel fago,Berdir, plach, effulgentsia, andypost...
  • 10.
  • 11.
    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)
  • 12.
    Entity reference Fairly completeport of D7 entity_reference.module Taxonomy, file, image fields: still separate field types
  • 13.
    Date / Datetime The"repeat" features from D7 stays in contrib
  • 14.
    Link Basic version (URL,text) No support for internal paths (e.g. node/1) Now supports internal paths (https://drupal.org/node/2054011)
  • 15.
    Email • Input validation •No anti-spam support out of the box Phone • HTML5 "tel" input • Basic display (optional "tel:" link)
  • 16.
  • 17.
  • 18.
    Form modes • Similarto "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...
  • 19.
  • 20.
  • 21.
    Fields are tiedto an entity type A 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">
  • 23.
    There will becode APIs !!!
  • 24.
    Disclaimer: still influx... Data structures
  • 25.
    Data model recap •Field value: list of multiple items • Each item: list of properties depending on the field type • Fields can be translatable
  • 26.
  • 27.
  • 28.
    D8 Entity translation •Entity level, not field level $entity->getTranslation($langcode) • Implements EntityInterface: $entity->getTranslation($langcode)->field_foo • Facets of the same entity $entityis always loaded/saved as a whole
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    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->propertyfor delta 0
  • 38.
    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
  • 39.
  • 40.
  • 41.
    Everything in aContentEntity is a field • $node->title FieldItemListInterface • $node->body FieldItemListInterface • $node->field_custom FieldItemListInterface Drawback: $node->title->value; Mitigation: $node->getTitle();
  • 42.
    Unified APIs andfeatures • field translation • field access • constraints / validation • output in REST • widgets / formatters • In Place Editing • EntityQuery (EFQ in D7) • field cache (= entity cache!)
  • 43.
  • 44.
    Different kinds offields... • 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'...)
  • 45.
    Code architecture • /core/lib/Drupal/Core/Field TheField system, widgets, formatters... baked in the lifecycle of (Content)Entities • /core/modules/field.module Configurable fields
  • 46.
    Unified FieldStorageDefinitionInterface Field 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
  • 47.
    Unified FieldStorageDefinitionInterface Implementations: • configurablefields: 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...
  • 48.
    Grabbing field definitions field_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
  • 49.
    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
  • 50.
    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...
  • 51.
  • 52.
    CMI • Configuration inYML files • Deployable between environments • Can be shipped in modules • ConfigEntities
  • 53.
    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
  • 55.
    D8: Field structuresas 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
  • 56.
    field.field.node.body.yml id: node.body uuid: d9a197db-89e1-4b8b-b50c-122083aeacb1 status:true langcode: en name: body entity_type: node type: text_with_summary settings: { } module: text locked: false cardinality: 1 translatable: false indexes: { } dependencies: module: - node - text 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  • 57.
    field.instance.node.article.body.yml id: node.article.body uuid: a378b8b5-39ac-44da-bc07-7ad0a2479a6a status:true langcode: en field_uuid: d9a197db-89e1-4b8b-b50c-122083aeacb1 field_name: body entity_type: node bundle: article label: Body description: '' required: false default_value: { } default_value_function: '' settings: display_summary: true text_processing: true dependencies: entity: - field.field.node.body - node.type.article field_type: text_with_summary 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  • 58.
    CRUD API -D7 Dedicated functions: + associated hooks... field_create_field(array()); field_update_field(array()); field_delete_field($field_name); ............_instance(array()); D7
  • 59.
    CRUD API -D8 Regular 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
  • 60.
  • 61.
    Display settings inD7 Scattered 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
  • 62.
    EntityViewDisplay (ConfigEntity) • "Fullrecipe" 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);
  • 63.
    entity.display.node.article.teaser.yml id: node.article.teaser uuid: ad345f0f-ff44-4210-8900-b8bdfcd8e671 targetEntityType:node bundle: article mode: teaser content: 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  • 64.
    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
  • 65.
    EntityFormDisplay • Same thingfor forms :-) • Allows "form modes"
  • 66.
  • 67.
    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, $langcode function hook_field_validate($entity_type, $entity, $field, $instance, $langcode function hook_field_presave($entity_type, $entity, $field, $instance, $langcode function hook_field_insert($entity_type, $entity, $field, $instance, $langcode function hook_field_update($entity_type, $entity, $field, $instance, $langcode function hook_field_delete($entity_type, $entity, $field, $instance, $langcode function 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
  • 68.
    FieldType plugin type •Discovery folder: Plugin/Field/FieldType • Annotation: FieldType • Interface: FieldItemInterface
  • 69.
    FieldItemInterface Now in8.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();
  • 70.
    /core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php namespace DrupallinkPluginFieldFieldType; use DrupalCoreFieldFieldItemBase; useDrupalCoreFieldFieldStorageDefinitionInterface; use DrupalCoreTypedDataDataDefinition; use DrupalCoreTypedDataMapDataDefinition; use DrupallinkLinkItemInterface; /** * 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 bl * default_widget = "link_default", * default_formatter = "link", * constraints = {"LinkType" = {}} * ) */ class LinkItem extends FieldItemBase implements LinkItemInterface { /** 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  • 71.
  • 72.
    D7: "hooks" (well,magic callbacks) • Lost within the 50 other functions in your file • switchdance 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, $field function mymodule_field_formatter_view($entity_type, $entity, $field, $instance
  • 73.
    D8: FieldFormatter plugins •All logic nicely self contained. • OO! Classes! ( = Inheritance!) • FormatterBase class provides stubs and helpers namespace DrupalCoreField; 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); }
  • 74.
    D8: FieldFormatter plugintype • No more _info() hook • Expose your class as a "Field formatter" plugin • Discovery folder: Plugin/Field/FieldFormatter • Annotation: FieldFormatter
  • 75.
  • 76.
    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...
  • 77.
  • 78.
    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 }
  • 79.
    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');
  • 80.
    Widgets / formatterson base fields • Powerful flexibility • Free support for "In Place Editing" • Hey, EntityDisplays let us store the settings!
  • 81.
    /core/modules/node/lib/Drupal/node/Entity/Node.php public static functionbaseFieldDefinitions(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; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  • 82.
    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
  • 83.
    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
  • 84.
  • 85.