ENTITIES IN


Popdan Daniel Adrian
Drupal experience: 1,5 years
From Satu-Mare / Cluj-Napoca

About me
Useful abstraction to group together fields
- Node
- Comment
- User
- Taxonomy

Entity type
Implementation of an entity type to which fields can be attached
Can be considered as subtypes of an entity type
Can add different fields for each bundle

Bundles
Reusable piece of content
Primitive data type
Can be added to any bundle

Fields
Instance of an entity type

Entity
An entity type is a base class
A bundle is an extended class
A field is a class member, property, variable or field instance
An entity is an object or instance of a base or extended class

Putting this in Object-Oriented Design/Programming terms...
What we had ?
- fieldable nodes
... non-fieldable users
... non-fieldable comments

Drupal 6
... turn users and comments in nodes
=> everything is a node ...

Solution ?
Drupal 7


- Field API decoupled from nodes
... generic stdClass objects
... introduced later in Core development
=> unfinished ...
- Entity module to the rescue !!!

Drupal 7
- Class based with methods
- Specifically typed objects !!!
- Supported by handlers
- Storage handlers, list handlers, access handlers
- Two different type of entities !!!

Drupal 8
Content + Config


Choose a unique machine name for your entity type
Define an interface for your entity extending \Drupal\Core\Config\Entity\ConfigEntityInterface or \Drupal\Core\Entity\ContentEntityInterface
Define a class for your entity extending ConfigEntityBase or \Drupal\Core\Entity\ContentEntityBase and implementing your interface
Add @ConfigEntityType or @ContentEntityType annotation to your class
Add 'id', 'label' and if has bundles 'bundle_label'

Defining an entity type
Content entity: \Drupal\Core\Entity\EntityListBuilder
Config entity: \Drupal\Core\Entity\ConfigEntityListBuilder

List builder
Entity annotation


Config/Content entity: \Drupal\Core\Entity\EntityForm
Config entity: \Drupal\Core\Entity\ContentEntityForm

Text
Add and edit forms or default form
Config/Content entity: \Drupal\Core\Entity\EntityConfirmFormBase

Delete form
Content entity: \Drupal\content_translation\ContentTranslationHandler
only if 'translatable' is TRUE
Config entity: Configuration Translation module

Translation
Config/Content entity: \Drupal\Core\Entity\EntityViewBuilder

View builder
Content entity: \Drupal\Core\Entity\EntityAccessControlHandler
admin_permission = "Administer ..."
- override checkAccess()
- override createAccess()
- do not override access()

Access
Content entity: \Drupal\Core\Entity\Sql\SqlContentEntityStorage
Config entity: \Drupal\Core\Config\Entity\ConfigEntityStorage

Storage
base_table: The entities base table
data_table: The entities data table
entity keys: The entity keys

Tables and keys
Config/Content entity: \Drupal\views\EntityViewsData

Views data
links -> "entity.$entity_type_id.$link_template_type"
- canonical
- delete-form
- edit-form
- delete-form

Links
field_ui_base_route: field settings
bundle_entity_type: the bundle entity type
bundle_of: the base entity type

Fields and bundles

Entity routes
entity.block.edit_form:
path: '/admin/structure/block/manage/{block}'
defaults:
_entity_form: 'block.default'
_title: 'Configure block'
requirements:
_entity_access: 'block.update'
...
handlers = {
"form" = {
"default" = "Drupal\block\BlockForm",
Entity manager
$storage = \Drupal::entityManager()->getStorage('your_entity_type');
// Or if you have a $container variable:
$storage = $container->get('entity.manager')->getStorage('your_entity_type');
Entity query
// Simple query:
$query = \Drupal::entityQuery('your_entity_type');
// Or, if you have a $container variable:
$query_service = $container->get('entity.query');
$query = $query_service->get('your_entity_type');
Entity Query
$query = \Drupal::entityQueryAggregate('your_entity_type');
// Or:
$query = $query_service->getAggregate('your_entity_type');propertyCondition() or condition() ?

Drupal 7

- only condition()

Drupal 8
$fids = Drupal::entityQuery('file')
->condition('status', FILE_STATUS_PERMANENT, '<>')
->condition('changed', REQUEST_TIME - $age, '<')
->range(0, 100)
->execute();
$files = $storage->loadMultiple($fids);Rendering an entity

$view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type');
// Or if you have a $container variable:
$view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type');
// You can omit the language ID if the default language is being used.
$build = $view_builder->view($entity, 'view_mode_name', $language->getId());
// $build is a render array.
$rendered = drupal_render($build);Access checking on entities

$entity->access($operation);
$entity->nameOfField->access($operation);
Creating a content entity
Create a 'Contact' entity to add, edit, delete contacts.
Fully fieldable.
The full module can be downloaded from https://www.drupal.org/project/examples

Problem description
Contact Interface

namespace Drupal\content_entity_example;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\user\EntityOwnerInterface;
/**
* Provides an interface defining a Contact entity.
* @ingroup content_entity_example
*/
interface ContactInterface extends ContentEntityInterface, EntityOwnerInterface {
}
Contact Annotations
/**
* @ContentEntityType(
* id = "content_entity_example_contact",
* label = @Translation("Contact entity"),
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\content_entity_example\Entity\Controller\ContactListBuilder",
* "form" = {
* "add" = "Drupal\content_entity_example\Form\ContactForm",
* "edit" = "Drupal\content_entity_example\Form\ContactForm",
* "delete" = "Drupal\content_entity_example\Form\ContactDeleteForm",
* },
* "access" = "Drupal\content_entity_example\ContactAccessControlHandler",
* },
* base_table = "contact",
* admin_permission = "administer content_entity_example entity",
* fieldable = TRUE,
* entity_keys = {
* "id" = "id",
* "label" = "name",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "content_entity_example_contact/{content_entity_example_contact}",
* "edit-form" = "/content_entity_example_contact/{content_entity_example_contact}/edit",
* "delete-form" = "/contact/{content_entity_example_contact}/delete",
* "collection" = "/content_entity_example_contact/list"
* },
* field_ui_base_route = "content_entity_example.contact_settings",
* )
*/
HOOK_ENTITY_TYPE_BUILD
function hook_entity_type_build(array &$entity_types) {
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
// Add a form for a custom node form without overriding the default
// node form. To override the default node form, use hook_entity_type_alter().
$entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
}
HOOK_ENTITY_TYPE_ALTER
function hook_entity_type_alter(array &$entity_types) {
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
// Set the controller class for nodes to an alternate implementation of the
// Drupal\Core\Entity\EntityStorageInterface interface.
$entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
}
CONTACT
class Contact extends ContentEntityBase implements ContactInterface {
/**
* {@inheritdoc}
*
* When a new entity instance is added, set the user_id entity reference to
* the current user as the creator of the instance.
*/
public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
parent::preCreate($storage_controller, $values);
$values += array(
'user_id' => \Drupal::currentUser()->id(),
);
}
ACCESS HANDLER
/**
* @file
* Contains \Drupal\content_entity_example\ContactAccessControlHandler
*/
namespace Drupal\content_entity_example;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Access controller for the comment entity.
*
* @see \Drupal\comment\Entity\Comment.
*/
class ContactAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*
* Link the activities to the permissions. checkAccess is called with the
* $operation as defined in the routing.yml file.
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
switch ($operation) {
case 'view':
return AccessResult::allowedIfHasPermission($account, 'view contact entity');
case 'edit':
return AccessResult::allowedIfHasPermission($account, 'edit contact entity');
case 'delete':
return AccessResult::allowedIfHasPermission($account, 'delete contact entity');
}
return AccessResult::allowed();
}
/**
* {@inheritdoc}
*
* Separate from the checkAccess because the entity does not yet exist, it
* will be created during the 'add' process.
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermission($account, 'add contact entity');
}
}Configurations in Drupal 7


Configurations in Drupal 8


- Content types
- Views
- Taxonomy vocabularies
- Contact forms
- Image styles
configuration entities are not fieldable

Configurations Entities

Creating a configuration entity

Example interface
namespace Drupal\example;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a Example entity.
*/
interface ExampleInterface extends ConfigEntityInterface {
// Add get/set methods for your configuration properties here.
}
Example class
/**
* @file
* Contains \Drupal\example\Entity\Example.
*/
namespace Drupal\example\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\example\ExampleInterface;
/**
* Defines the Example entity.
*
* @ConfigEntityType(
* id = "example",
* label = @Translation("Example"),
* handlers = {
* "list_builder" = "Drupal\example\Controller\ExampleListBuilder",
* "form" = {
* "add" = "Drupal\example\Form\ExampleForm",
* "edit" = "Drupal\example\Form\ExampleForm",
* "delete" = "Drupal\example\Form\ExampleDeleteForm"
* }
* },
* config_prefix = "example",
* admin_permission = "administer site configuration",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* },
* links = {
* "edit-form" = "/admin/config/system/example/{example}",
* "delete-form" = "/admin/config/system/example/{example}/delete"
* }
* )
*/
class Example extends ConfigEntityBase implements ExampleInterface {
/**
* The Example ID.
*
* @var string
*/
public $id;
/**
* The Example label.
*
* @var string
*/
public $label;
// Your specific configuration property get/set methods go here,
// implementing the interface.
}
Configuration schema
example.example.*:
type: config_entity
label: 'Example config'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
example/config/schema/example.schema.yml

Entity Form Class
/**
* @file
* Contains \Drupal\example\Form\ExampleForm.
*/
namespace Drupal\example\Form;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;
class ExampleForm extends EntityForm {
/**
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* The entity query.
*/
public function __construct(QueryFactory $entity_query) {
$this->entityQuery = $entity_query;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.query')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$example = $this->entity;
$form['label'] = array(
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $example->label(),
'#description' => $this->t("Label for the Example."),
'#required' => TRUE,
);
$form['id'] = array(
'#type' => 'machine_name',
'#default_value' => $example->id(),
'#machine_name' => array(
'exists' => array($this, 'exist'),
),
'#disabled' => !$example->isNew(),
);
// You will need additional form elements for your custom properties.
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$example = $this->entity;
$status = $example->save();
if ($status) {
drupal_set_message($this->t('Saved the %label Example.', array('%label' => $example->label())));
}
else {
drupal_set_message($this->t('The %label Example was not saved.', array('%label' => $example->label())));
}
$form_state->setRedirect('example.list');
}
/**
* Check if a config with this entity exists.
*
* @param int $id
* The entity id.
*
* @param boolean
* 'TRUE' if exists, 'FALSE' otherwise.
*/
public function exist($id) {
$entity = $this->entityQuery->get('example')
->condition('id', $id)
->execute();
return (bool) $entity;
}
}
example/src/Form/ExampleForm.php














Entities in Drupal 8
By Popdan Daniel
Entities in Drupal 8
Working with entities in Drupal 8
- 1,479