Drupal 8: Fields reborn
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
746
On Slideshare
746
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
4
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. D8: Fields reborn Pablo López - @plopesc DrupalCamp Spain Valencia, May 2014
  • 2. 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
  • 3. Credits: @yched & @swentel Broken record
  • 4. 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
  • 5. ... for the 3rd time
  • 6. yched, amateescu, swentel fago, Berdir, plach, effulgentsia, andypost...
  • 7. Site building features
  • 8. 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)
  • 9. Entity reference Fairly complete port of D7 entity_reference.module Taxonomy, file, image fields: still separate field types
  • 10. Date / Datetime The "repeat" features from D7 stays in contrib
  • 11. Link Basic version (URL, text) No support for internal paths (e.g. node/1) Now supports internal paths (https://drupal.org/node/2054011)
  • 12. Email • Input validation • No anti-spam support out of the box Phone • HTML5 "tel" input • Basic display (optional "tel:" link)
  • 13. In Place Editing
  • 14. Fieldable blocks aka Beans in core
  • 15. 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...
  • 16. Form modes UI
  • 17. Field Overview UI
  • 18. 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">
  • 19. There will be code APIs !!!
  • 20. Disclaimer: still in flux... Data structures
  • 21. Data model recap • Field value: list of multiple items • Each item: list of properties depending on the field type • Fields can be translatable
  • 22. Data model - D7
  • 23. Data model - D7
  • 24. 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
  • 25. Data model - D8
  • 26. Data model - D8
  • 27. Data model - D8
  • 28. Data model - D8
  • 29. Data model - D8
  • 30. Data model - D8
  • 31. Data model - D8
  • 32. Data model - D8
  • 33. 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
  • 34. 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
  • 35. Everything is a field
  • 36. Everything
  • 37. Everything in a ContentEntity is a field • $node->title FieldItemListInterface • $node->body FieldItemListInterface • $node->field_custom FieldItemListInterface Drawback: $node->title->value; Mitigation: $node->getTitle();
  • 38. 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!)
  • 39. Naming is hard...
  • 40. 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'...)
  • 41. 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
  • 42. 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
  • 43. 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...
  • 44. 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
  • 45. 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
  • 46. 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...
  • 47. CMI
  • 48. CMI • Configuration in YML files • Deployable between environments • Can be shipped in modules • ConfigEntities
  • 49. 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
  • 50. 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
  • 51. 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
  • 52. 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
  • 53. CRUD API - D7 Dedicated functions: + associated hooks... field_create_field(array()); field_update_field(array()); field_delete_field($field_name); ............_instance(array()); D7
  • 54. 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
  • 55. EntityDisplay
  • 56. 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
  • 57. 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);
  • 58. 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
  • 59. 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
  • 60. EntityFormDisplay • Same thing for forms :-) • Allows "form modes"
  • 61. Field types
  • 62. 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
  • 63. FieldType plugin type • Discovery folder: Plugin/Field/FieldType • Annotation: FieldType • Interface: FieldItemInterface
  • 64. 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();
  • 65. /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
  • 66. Formatters
  • 67. 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
  • 68. 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); }
  • 69. D8: FieldFormatter plugin type • No more _info() hook • Expose your class as a "Field formatter" plugin • Discovery folder: Plugin/Field/FieldFormatter • Annotation: FieldFormatter
  • 70. Inheritance example
  • 71. 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...
  • 72. Widgets
  • 73. 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 }
  • 74. 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');
  • 75. Widgets / formatters on base fields • Powerful flexibility • Free support for "In Place Editing" • Hey, EntityDisplays let us store the settings!
  • 76. /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
  • 77. 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
  • 78. 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
  • 79. Thanks!
  • 80. Questions?