D8 Entity API Entidades de contenido - pierregermain/MyDrupal GitHub Wiki
Ir a Entity Api
- Entidades de contenido
- Creación de bundles
- Modificar una entidad vista por el usuario
- Tipos de campos personalizados
- Theming en Entidades
- Programación de taxonomías
- Constraints: Validación de Entidades y campos
Entidades de contenido
Creación de Entidades de contenido
Para generar una entidad de contenido lo más fácil es hacerlo con la drupal console
drupal generate:entity:content
Esto nos crea una entidad con su listado que admite operaciones CRUD. La entidad por defecto tiene las siguientes propiedades:
Super Resumen para saber qué hacer para añadir un campo a una Entidad
Para añadir un campo a una entidad se debe de realizar los siguiente:
- Añadir el campo a la interfaz
- Implementar el campo en la Entidad en baseFieldDefinitions()
- Los métodos a poder ser usados son definidos en BaseFieldDefinition
- Añadir el campo en ListBuilder en el caso de que queramos que se muestre en el listado.
Ejemplo
Cómo ejemplo vamos a crear una Entidad llamada Message
Ficheros que hay que implementar:
- Interfaz
- Fichero: /src/Entity/MessageEntityInterface.php
- Características:
- Extiende de ContentEntityInterface, EntityChangedInterface y EntityOwnerInterface
- Por defecto drupal console nos deja el get y set de los campos name y created date
- Deberíamos añadir nuestros getters y setters aqui de los campos que vayamos a crear, y luego en la Entidad resultante ya los implementamos heredando dichos métodos. Pero esto no es obligatorio hacerlo, nada mas es más limpio.
- Entidad Message
- Fichero: src/Entity/MessageEntity.php
- Extiende ContentEntityBase
- Implementa nuestra interfaz creada
- Contiene Annotations con la siguiente información
- base_table: Tabla donde se guardan las entidades creadas
- Cambios a ser realizadas:
- Cambiar
'label' = 'name'.por'label' = 'subject'
- Cambiar
- OJO: Los fields se añaden en el método baseFieldDefinitions()
- Serán campos de base que no se pueden modificar desde la UI
- Hacemos ajustes también al field "name"
- Ejemplo: Añadir campo booleano
- Agregamos los setters y getters de los fields que vayamos a implementar.
- Añadimos la definición del campo con BaseFieldDefinition
Ejemplo de algunos campos:
Booleano
$fields['is_read'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Read'))
->setDescription(t('A boolean indicating whether the Message is read.'))
->setDefaultValue(FALSE);
text_long
$fields['content'] = BaseFieldDefinition::create('text_long')
->setLabel(t('Content'))
->setDescription(t('The content of the Message'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
))
->setDisplayConfigurable('view', TRUE)
->setDisplayOptions('form', array(
'type' => 'text_textfield',
'weight' => 0,
))
->setDisplayConfigurable('form', TRUE);
- Ojo con usar nombres de fields reservados: https://dev.mysql.com/doc/refman/5.7/en/keywords.html
- Más información sobre creación de fields: https://www.drupal.org/docs/8/api/entity-api/defining-and-using-content-entity-field-definitions
- Definición de rutas y enlaces de menú
- Desde el Annotation de la entidad creada ( en el punto 2.) Drupal Console nos define:
- Links para hacer CRUD contra la Entidad
- Se define el "route_provider", dicho fichero en principio no hace falta modificarlo
- Además Drupal Console también nos habrá creado los links y menús en los siguientes ficheros
mymodule.links.menu.yml
mymodule.links.task.yml
mymodule.links.action.yml
- Clase controladora de la entidad
Desde la Annotation de la Entidad creada podemos ver la parte de "handlers".
- "list_builder" es el controlador que lista las entidades creadas
- Fichero: src/MessageEntityListBuilder.php
- Dicho fichero define los headers y rows
- Aqui se añaden los campos que queramos mostrar
- OJO: Si vas a usar DI, no olvides implementar el constructor y la createInstance().
- Fichero: src/MessageEntityListBuilder.php
- "MessageEntityForm": Podemos añadir validaciones necesarias.
- "MessageEntityDeleteForm": No hace falta hacer nada.
- "MessageEntityViewsData": No hace falta hacer nada.
- Bundles
En nuestro ejemplo usamos MessageEntityType. Esto define el tipo de mensaje. Es igual que la entidad de configuración (Ver: https://github.com/pierregermain/MyDrupal/wiki/D8-Entity-API#entidades-de-configuraci%C3%B3n)
Ficheros involucrados (no hace falta modificarlos)
src/Entity/MessageEntityType.phpClase principal de la entidad MessageType.src/Entity/MessageEntityTypeInterface.phpInterfaz de la entidad.src/Form/MessageEntityTypeForm.phpFormulario de creación/edición de tipos de mensajes.src/Form/MessageEntityTypeDeleteForm.phpFormulario de eliminación de tipos de mensajes.src/MessageEntityTypeHtmlRouteProvider.phpProveedor de rutas.src/MessageEntityTypeListBuilder.phpGenera la página de administración de tipos de mensajes.config/schema/mymodule_message_type.schemaEsquema de configuración.
- Permisos
- Fichero: mymodule.permissions.yml define los permisos, no hace falta modificarlo
- Fichero: src/MessageEntityAccessControlHandler.php usa los permisos, en principio no hace falta modificarlo
- Theming
Ficheros:
templates/mymodule-message-content-add-list.html.twigpara listado de mensajes.templates/mymodule-message.html.twigpara mostar un mensaje.mymodule_message.page.incincluye preprocess para el template mymodule-message.html.twig
mymodule.moduleincluye hook_theme y hook_theme_suggestions_HOOK() para hacer funcionar el theming
Probar cambios en entidades
cuando hagamos cambios en entidades, para tomar los cambios sin tener que volver a instalar el módulo podemos ejecutar Ya no se puede hacer.
drush updatedb --entity-updates
alternativamente:
drupal entity:delete myentity --all
drupal mou mymodule
drupal moi mymodule
Tabla base
(...)
Integración con vistas
(...)
Creación de bundles
Para generar un bundle podemos hacerlo asi
drupal generate:entity:bundle
La implementación de un nuevo bundle se realiza a través de archivos de configuración.
- Fichero: mymodule.schema.yml
- Ubicación: /config/schema o /config/install
Con drupal console podemos crear el bundle principal y nos lo dejará en la ubicación /config/install
Recomendamos una vez generado ver si está bien "langcode" y "description"
Los parámetros definidos en este archivo de configuración provienen del archivo /core/modules/node/config/schema/node.schema.yml:
TODO: Menus
Desinstalación del CT
Si quieres que se desinstale el CT creado al desinstalar el módulo nada mas tienens que añadir al yml file lo siguiente:
dependencies:
module:
- menu_ui
enforced:
module:
- mymodule
Campos adicionales
Por defecto drupal console nos instalar el campo "body". Dichos campos no son campos base por lo que se pueden modificar y quitar desde la UI.
/config/install/field.field.node.news.body.yml
TODO: Explicar de donde viene eso
Displays
Por defecto Drupal console nos habrá creado el display para el formulario y las vistas default y display.
Creación de nodos
Ya con esos ficheritos de configuración y volviendo a instalar el módulo seremos capaces de crear nodos del tipo bundle que hayamos elegido. OJO: Estamos creando NODOS, no tiene nada que ver con nuestra entidad creada anteriormnete (Es decir que no tiene las propiedades ni los fields que habíamos creado).
Más información de interés
en el code base /core/profiles/standard/config/install
y en https://www.drupal.org/docs/8/api/entity-api/programming-custom-fields-into-your-content-type
Modificar una entidad vista por el usuario
Para ver todas las entidades existentes en nuestra instancia de drupal podemos correr el siguiente comando:
drush eval "print_r(array_keys(\Drupal::entityManager()->getDefinitions()));"
Una vez que sepamos el nombre de la entidad tenemos hooks para acceder a las entidades cuando sean cargadas o vistas.
Por ejemplo para cuando un usuario de la web mire una entidad podemos usar el siguiente hook:
/**
* Implements hook_ENTITY_TYPE_view().
*/
function mymodule_myentity_view(array &$build,
EntityInterface $entity,
EntityViewDisplayInterface $display,
$view_mode) {
dpm('Hola');
dpm($entity);
}
Tipos de campos personalizados
Mas info en: https://www.drupal.org/docs/8/api/entity-api/fieldtypes-fieldwidgets-and-fieldformatters
Para definir un campo personalizado hay que realizar lo siguiente:
1. Definir el @FieldType que extiende a FieldItemBase
Carpeta: /src/Plugin/Field/FieldType
En dicha carpeta metemos cada FieldType necesario cómo fichero independiente. Por ejemplo: ColorItem.php
Cada FieldType contiene:
- El annotation que define los siguientes parámetros:
- id
- label
- module
- description
- default_widget
- default_formatter
- FieldItemInterface::schema() que devuelve la estructura de la tabla en DB que necesita el FieldType. Puede devolver un array vacío para los casos en los que no necesitemos guardar datos en DB
- FieldItemInterface::propertyDefinitions() que hay que implementar si o si para devolver las propiedades del FieldType
- TypedData::getConstraints() Ver en Validación de Entidades y campos
2. Definir el fichero de configuración
- Carpeta:
/config/schema - Fichero:
mymodule.schema.yml - Sirve para definir la configuración de cada campo
3. Definir @FieldWidget
Sirve para por ejemplo desde la UI en Structure > Content Types > Article > Manage form display > Tags definir el Widget usado; En ese caso tiene entre otras opciones las posibilidades de usar "Autocomplete" y "Select List" cómo Widget.
En nuestro ejemplo definimos un nuevo widget para poder añadir un código hexadecimal de color.
Carpeta: /src/Plugin/Field/FieldWidget
El annotations tiene los siguientes parámetros:
- id
- label
- module
- field_types: Define los id's de los FieldTypes de los campos que aceptan dicho control
4. Definir @FieldFormatter
Carpeta: /src/Plugin/Field/FieldFormatter
Un ejemplo sería en Structure > Content Types > Article > Manage display > Image poder usar "Image" o "URL to Image"
5. Configuraciones adicionales
Tanto los campos en Manage display cómo en Manage form display pueden aceptar configuraciones adicionales.
Métodos relacionados:
settingsForm()Formulario para solicitar los valores de configuración.defaultSettings()Devuelve un array asociativo con los valores de configuración por defecto.settingsSummary()Muestra un resumen con información de los valores de configuración actuales.getSetting(), getSettings(), setSetting(), setSettings()Métodos get y set para obtener y almacenar valores de configuración.
Estos métodos simplemente los usamos dentro de nuestra clase FieldWidget o FieldFormatter.
Theming en Entidades
- hook_entity_view(&$build, $entity, $display, $view_mode)
- hook_ENTITY_TYPE_view(&$build, $entity, $display, $view_mode)
- hook_entity_view_alter(&$build, $entity, $display)
- es posterior a hook_entity_view()
- alternativamente puedes usar hook_ENTITY_TYPE_view_alter(&$build, $entity, $display)
Programación de taxonomías
Crear vocabularios desde yml files
- se hace desde /config/install
- Ejemplo: /config/install/taxonomy.vocabulary.tags.yml crea el vocabulario tags de article
Crear vocabulario desde código
use Drupal\taxonomy\Entity\Vocabulary;
// ...
Vocabulary::create([
'vid' => 'tags',
'name' => 'Tags',
])->save();
Crear terms
$term = Term::create(array(
'name' => t('General discussion'),
'description' => '',
'parent' => array(0),
'vid' => 'forums',
'forum_container' => 0,
));
$term->save();
Crear campos de referencia a taxonomía
Ejemplo: tags en artículo
- Habilitar campo para nodos: core/profiles/standard/config/install/field.storage.node.field_tags.yml
- Instanciar campo en bundle: core/profiles/standard/config/install/field.field.node.article.field_tags.yml
Acceso a taxonomías
Método 1: Usando tid
$term_storage = \Drupal::entityManager->getStorage('taxonomy_term');
$term = $term_storage->load($tid);
Método 2: Usando vid
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', "tags");
$tids = $query->execute();
$term_storage = \Drupal::entityManager->getStorage('taxonomy_term');
$terms = $term_storage->loadMultiple($tids);
Método 3: campos de referencia
foreach ($node->field_tags as $item) {
$term = $item->entity;
// $term->id()
// $term->label()
}
Método 4: Obtener árbol de jerarquía de términos
$term_storage = \Drupal::entityManager()->getStorage('taxonomy_term');
$tree = $term_storage->loadTree('tags');
Constraints: Validación de Entidades y campos
- Más info en:
- Se pueden en D8 validar entidades antes de ser guardadas independientemente del formulario usado.
- Se usan las constraints de Symfony
Contstraints
Par encontrar constraints:
- Drupal:
core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint - Symfony:
vendor/symfony/validator/Constraints/ - buscando
@Constrainten el código
Ejemplo: LengthConstraint.php
- Fichero:
lib/Drupal/Core/Validation/Plugin/Validation/Constraint/LengthConstraint.php - sobrescribe a la clase Length de Symfony
- usando validatedBy() le decimos a la la clase que realice la validación desde Symfony.
Aplicación de constraints
Validación a nivel de entidad
A) En nuestro propio módulo
- Se usa cuando hay que hacer validaciones complejas donde suelen intervenir valores de varios campos.
- Ejemplo: modules/comment/src/Plugin/Validation/Constraint donde tenemos las clases Validator y Constraint.
- Con coversFields() le decimos que campos queremos validar
Las violaciones se generan con código de este tipo:
$this->context->buildViolation($constraint->messageNameTaken, ['%name' => $author_name])
->atPath('name')
->addViolation();
Relación de nuestra clase con una constraints
Lo hacemo usando Annotations.
Ejemplo: /core/modules/comment/src/Entity/Comment.php
* ...
* constraints = {
* "CommentName" = {}
* }
* ...
- Las llaves vacías indican que la constraint no requiere parámetros.
B) Aplicación de constraints a entidades de otros módulos
Se hace usando el siguiente hook:
hook_entity_type_alter(array &$entity_types)
/**
* Implements hook_entity_type_alter().
*/
function mymodule_entity_type_alter(array &$entity_types) {
$node_definition = $entity_types['node'];
$node_definition->addConstraint('ConstraintName', ['options']);
}
Método Validate a nivel de entidad
El validate se ejecuta de forma automática al guardar una entidad. Si queremos que se ejecute de forma manual se puede hacer de la siguiente forma. Esto nos devuelve las violaciones detectadas.
$violations = $entity->validate();
Validación a nivel de campo
También se ejecutan cuando se guarda una entidad.
A) Campo dentro de nuestro módulo
Cuando creas un plugin tipo campo en un módulo puedes añadir directamente en el Annotation la constraint.
Ejemplo:
/core/modules/file/src/Plugin/Field/FieldType/FileItem.php usa la constraint en
/core/modules/file/src/Plugin/Validation/Constraint/
Otras formas de añadir una constraint:
- usando la función propertyDefinitions(): POdemos añadir a un campo
->addConstraint('Length', array('max' => 255))por ejemplo - usando getConstraints(): Este método nos devulve las constraints, pero podemos añadir nuevas.
B) Entidad en nuestro módulo
- se añade desde baseFieldDefinitions()
En este caso:
- El método addConstraint() se utiliza para añadir constraints que no tienen parámetros (UserMailUnique, UserMailRequiered, etc.).
- El método addPropertyConstraints() se utiliza para añadir constraints con parámetros, como es el caso de Range o Length.
C) Campo fuera de nuestro módulo
- hook_entity_base_field_info_alter()
Método Validate a nivel de campo
$violations = $entity->field_text->validate();
Acceso a Entidades y Rutas
Ejemplo:
entity.comment.edit_form:
path: '/comment/{comment}/edit'
defaults:
_title: 'Edit'
_entity_form: 'comment.default'
requirements:
_entity_access: 'comment.update'
comment: \d+
donde _entity_access denota que al hacer update desde comment se tiene acceso a dicha ruta y el \d+ es un regex que dice que el parámetro debe incluir al menos un digito.