Planète

Par admin

Compte Rendu du Sprint média

Message de Woprrr, qui a coordonné ce sprint :

"Cela c'est bien passé de notre côté."

Du point de vue du sprint nous avons décidé d'élargir le périmètre d'action ("Au moins un module qui pourrait avoir un impact sur la stack média") du sprint afin d'inciter à participer un peu plus pour les entreprises.

Points extrêmement positif "vpeltot" et "woprrr" (moi même), avons porté deux modules qui je pense seront extrêmement pratiques dans l'utilisation de Drupal 8.

1er initiative d'utilisation des form_modes mis à disposition par Drupal 8, qui permet une prise en main rapide et simple de ceux-ci. L'avantage est qu'il utilise entièrement les concepts de Drupal 8 empruntés à SF2, c'est à dire qu'il n'est pas un alter de la structure de D8 afin de combler le manque des form_modes dans Drupal 8, mais il s'intègre à n'importe quelle entité qui est capable d'utiliser les form_modes et utilise les routes de cette dernière. Il a été décidé que ce seraitune priorité, son intégration avec EntityBrowser et offrira la possibilité de pouvoir gérer les modes de rendu des formulaires disponible via EB, avec aussi une intégration de prévue avec Inline_entity_form pour tirer au maximum parti du potentiel des form_modes d'entities.

Lien du module : https://www.drupal.org/project/form_mode_manager

Seconde initiative et pas des moindres Entity_clone, comme le nom l'indique il s'agit de proposer la possibilité de cloner n'importe quelle entité de manière assez intelligente. Il peut s'agir d'un pur clonage, mais aussi d'un clone qui clone en cascade les références de ce dernier. Ainsi permettant de cloner un contenu à l'identique, mais de proposer une vie propre à ce clone. Même ci il s'agit d'une initiative très large en terme d'utilisation, l'intérêt pour Média est clairement de pouvoir cloner les différents Média sans devoir re-uploader les fichiers, pour un media sans pour autant avoir les memes champs au niveau de l'entité. Il permet également de pouvoir cloner les différents Providers offerts par EntityBrowser.

Lien du module : https://www.drupal.org/project/entity_clone

Dernier point aborder pendant le sprint ImageWidgetCrop qui lui est un pur produit Média. Il y a eu stabilisation et release stable du module et nous avons commencé à reprendre les différentes features request proposées par différents contributeurs et utilisateurs. Mais nous manquons encore de monde pour l'essayer et améliorer sont utilisation par des "novices" ou encore par les Sites Builders qui n'ont encore pas de notions.
Et enfin quelques contributions sur le Gitbook média (https://drupal-media.gitbooks.io/drupal8-guide/content/modules/image_widget_crop/intro.html).

Lien du module : https://www.drupal.org/project/image_widget_crop

En conclusion :
Un sprint "calme" concernant l'implication d'acteurs extérieurs à média, mais de belles initiatives sont nées. Je pense également que nous devons également remercier Degetel pour son implication et le temps qui a été consacré à ce sprint. J'espère dans le futur pouvoir organiser d'autres sprints ou initiative au sein de Drupal et plus particulièrement au sein de la communauté française qui je pense à un énorme potentiel, car nous avons de belles choses à faire valoir !

En page d'accueil : 
Par admin

Compte Rendu du Sprint média

Message de Woprrr, qui a coordonné ce sprint :

"Cela c'est bien passé de notre côté."

Du point de vue du sprint nous avons décidé d'élargir le périmètre d'action ("Au moins un module qui pourrait avoir un impact sur la stack média") du sprint afin d'inciter à participer un peu plus pour les entreprises.

Points extrêmement positif "vpeltot" et "woprrr" (moi même), avons porté deux modules qui je pense seront extrêmement pratiques dans l'utilisation de Drupal 8.

1er initiative d'utilisation des form_modes mis à disposition par Drupal 8, qui permet une prise en main rapide et simple de ceux-ci. L'avantage est qu'il utilise entièrement les concepts de Drupal 8 empruntés à SF2, c'est à dire qu'il n'est pas un alter de la structure de D8 afin de combler le manque des form_modes dans Drupal 8, mais il s'intègre à n'importe quelle entité qui est capable d'utiliser les form_modes et utilise les routes de cette dernière. Il a été décidé que ce seraitune priorité, son intégration avec EntityBrowser et offrira la possibilité de pouvoir gérer les modes de rendu des formulaires disponible via EB, avec aussi une intégration de prévue avec Inline_entity_form pour tirer au maximum parti du potentiel des form_modes d'entities.

Lien du module : https://www.drupal.org/project/form_mode_manager

Seconde initiative et pas des moindres Entity_clone, comme le nom l'indique il s'agit de proposer la possibilité de cloner n'importe quelle entité de manière assez intelligente. Il peut s'agir d'un pur clonage, mais aussi d'un clone qui clone en cascade les références de ce dernier. Ainsi permettant de cloner un contenu à l'identique, mais de proposer une vie propre à ce clone. Même ci il s'agit d'une initiative très large en terme d'utilisation, l'intérêt pour Média est clairement de pouvoir cloner les différents Média sans devoir re-uploader les fichiers, pour un media sans pour autant avoir les memes champs au niveau de l'entité. Il permet également de pouvoir cloner les différents Providers offerts par EntityBrowser.

Lien du module : https://www.drupal.org/project/entity_clone

Dernier point aborder pendant le sprint ImageWidgetCrop qui lui est un pur produit Média. Il y a eu stabilisation et release stable du module et nous avons commencé à reprendre les différentes features request proposées par différents contributeurs et utilisateurs. Mais nous manquons encore de monde pour l'essayer et améliorer sont utilisation par des "novices" ou encore par les Sites Builders qui n'ont encore pas de notions.
Et enfin quelques contributions sur le Gitbook média (https://drupal-media.gitbooks.io/drupal8-guide/content/modules/image_widget_crop/intro.html).

Lien du module : https://www.drupal.org/project/image_widget_crop

En conclusion :
Un sprint "calme" concernant l'implication d'acteurs extérieurs à média, mais de belles initiatives sont nées. Je pense également que nous devons également remercier Degetel pour son implication et le temps qui a été consacré à ce sprint. J'espère dans le futur pouvoir organiser d'autres sprints ou initiative au sein de Drupal et plus particulièrement au sein de la communauté française qui je pense à un énorme potentiel, car nous avons de belles choses à faire valoir !

En page d'accueil : 
Par Artusamak
Julien Dubois

Drupal 8 : Field API / Créer un nouveau type de champ

Drupal 8 : Field API / Créer un nouveau type de champ
mar, 12/04/2016 - 15:25
Artusamak

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Principes

Après l’intégration de CCK dans le coeur de Drupal 7, la Field API a continué d’évoluer. Il était frustrant de ne pas pouvoir appliquer aux propriétés des entités des formateurs ou des widgets. C’est maintenant possible si la déclaration de ces propriétés le permet ou en l’altérant (Cela est nécessaire pour l’entité Node et les champs title, author, created, etc.).

Les propriétés s’appellent des champs de base (Base fields) et les champs “classiques” sont des champs configurables (Configurable fields). Les propriétés n’ont pas totalement disparu : les champs sont composés de propriétés. Exemple : une valeur pour un champ de texte simple (Textfield), une valeur et un format de texte pour un champ de texte long (Textarea).
Dans la plupart des cas, les propriétés seront associées à une colonne dans une table mais pas toujours. Il est possible d’avoir des propriétés qui stockent des données calculées. Dans le cas précédent, le texte rendu dans le format de texte sera une donnée calculée que nous stockerons dans une propriété. Ces propriétés calculées combinées au cache de l’API de rendu permettent d’optimiser les performances. Pour voir comment implémenter ces données calculées, référez-vous à la documentation.

Les types de champs, formateurs et widgets sont des Plugins. Si vous souhaitez implémenter l’un de ces trois type de Plugin il suffit d’implémenter l’interface associée ou, si vous ne souhaitez pas réinventer la roue, étendre la classe annotée de base de chaque type :

TYPE DE PLUGIN

ANNOTATION

INTERFACE

CLASSE

Type de champ

@FieldType

FieldItemInterface

FieldItemBase

Widget

@FieldWidget

WidgetInterface

WidgetBase

Formateur

@FieldFormatter

FormatterInterface

FormatterBase

Le chemin PSR-4 de votre classe prend la forme suivante :  src/Plugin/Field/Field<Type|Widget|Formatter>/<nomPlugin>.php

À noter également que les champs sont maintenant stockés par type d’entité. Il devient donc possible d’utiliser le même nom de champ à plusieurs endroits.

Pour les développeurs habitués à développer avec les champs dans Drupal 7, un changement sémantique intervient avec Drupal 8. La notion de “champ” (Field) définissant la structure des données est maintenant appelé FieldStorage alors que la notion “d’instance de champ” identifiant la configuration d’un champ associé à une entité s’appelle désormais Field.

Exemple

La création d’un type de champ peut être nécessaire dans Drupal, notamment pour réaliser un champ composé de plusieurs données (comme link qui propose un titre et une URL). Cela permet de s’affranchir des modules comme Field collection ou Paragraphs pour simplifier le modèle de données ou compléter un type de champ existant proche de nos besoins. Nous allons voir l'implémentation d'un type de champ collectant un ISBN de livre à 10 ou 13 chiffres (2 champs de collecte).

Dans Drupal un champ est composé de 3 parties. Une principale, le type de champ (FieldType) qui est la définition technique du champ, et deux parties d’interface ; à savoir : le widget (FieldWidget) utilisé pendant l’édition d’un contenu et le formateur (FieldFormatter) qui s’occupe du rendu du champ lors de son affichage.

Ces deux derniers éléments peuvent être créés indépendamment du FieldType, ce qui permet de proposer des FieldWidget ou des FieldFormatter pour n’importe quel FieldType.

Chacune de ces 3 parties est gérée à l’aide de Plugins. Voici pour chacune les informations nécessaires à leur implémentation ainsi qu’un aperçu des méthodes qui remplissent les fonctions d’anciens hooks sous Drupal 7.

Le stockage des données

FieldType 

Interface : Drupal\Core\Field\FieldItemInterface

Classe abstraite : Drupal\Core\Field\FieldItemBase

Répertoire d’implémentation : /src/Plugin/Field/FieldType/

Namespace à utiliser : Drupal\<module>\Plugin\Field\FieldType

Nom du hook Drupal 7

Équivalent Drupal 8

hook_field_info()

Annotation de type @FieldType

hook_field_schema()

FieldItemInterface::schema()

hook_field_is_empty()

ComplexDataInterface::isEmpty()

L’Annotation de ce Plugin est assez simple, l’identifiant machine, un label, une description et les valeurs par défaut du widget et du formateur utilisé pour ce champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token operator">/</span><span class="token operator">*</span><span class="token operator">*</span>
<span class="token operator">*</span> Plugin implementation of the <span class="token string">'isbn'</span> field type<span class="token punctuation">.</span>
<span class="token operator">*</span>
<span class="token operator">*</span> @<span class="token function">FieldType</span><span class="token punctuation">(</span>
<span class="token operator">*</span>   id <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   label <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Isbn"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   description <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Stores a ISBN string in various format."</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_widget <span class="token operator">=</span> <span class="token string">"isbn_default"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_formatter <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span> <span class="token punctuation">)</span>
<span class="token operator">*</span>

La création d’un type de champ passe par la définition du modèle de données de ce champ. Pour cela il faut implémenter les méthodes schema() et propertyDefinitions(). Comme pour Drupal 7, avec le hook_field_schema() il s’agit de décrire la table SQL qui va recevoir les données.

Dans notre cas nous aurons 2 valeurs de l’ISBN à stocker.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">schema</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
   <span class="token string">'columns'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
     <span class="token string">'isbn_13'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 13 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
     <span class="token string">'isbn_10'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 10 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">10</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
   <span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La méthode propertyDefinitions() quant à elle permet une description au niveau de Drupal et propose plus d’informations. La description des propriétés se fait grâce à la Typed Data API qui permet d’interagir avec les données et leurs meta-données. Exemple : donner un nom plus compréhensible par un humain avec setLabel(), rendre un champ obligatoire avec setRequired(), définir des contraintes de validation avec addConstraint()...

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">propertyDefinitions</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token variable">$properties</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Si l’un veut créer un champ composé, deux autres méthodes sont particulièrement importantes. isEmpty() permet à Drupal de savoir si votre champ doit être considéré comme vide pour afficher ou non le champ par exemple. Dans notre cas, on va considérer que c’est la valeur de la propriété ‘isbn_13’ qui va déterminer cela.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$value</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'isbn_13'</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">empty</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La deuxième est mainPropertyName() qui permet de définir le nom de la propriété principale. La plupart des champs de base utilisent ‘value’ mais cela devient vite gênant quand on construit des champs complexes. Il est donc essentiel de fournir cette information aux autres modules pour qu’ils puissent utiliser au mieux notre champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">mainPropertyName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">'isbn_13'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Bien sûr il existe encore de multiples méthodes, notamment les fieldSettingsForm(), preSave(), delete() et autres pour agir à différents moments de la vie de nos données de champ mais je vous laisse découvrir cela en regardant l’interface Drupal\Core\Field\FieldItemInterface.

On notera la présence de generateSampleValue() permettant de fournir un jeu de données basiques jouant le rôle de données de substitution lors de la génération de contenus fictifs. (Avec Devel generate par exemple).

Le widget du champ

FieldWidget

Interface : Drupal\Core\Field\WidgetInterface

Classe abstraite : Drupal\Core\Field\WidgetBase

Répertoire d’implémentation : /src/Plugin/Field/FieldWidget/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldWidget

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_widget_info()

Annotation de type @FieldWidget

hook_field_widget_form()

WidgetInterface::formElement()

hook_field_widget_error()

WidgetInterface::errorElement()

Encore une fois, on utilisera un Plugin pour créer le widget de notre champ. Nous allons donc le définir à l’aide d’une Annotation.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token comment" spellcheck="true">/**
* Plugin implementation of the 'isbn' widget.
*
* @FieldWidget(
*   id = "isbn_default",
*   label = @Translation("ISBN"),
*   field_types = {
*     "isbn"
*   }
* )
*/</span>

Ensuite, nous allons définir le formulaire qui sera utilisé dans l’interface pour réaliser la saisie des valeurs du champs dans la méthode formElement().

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">formElement</span><span class="token punctuation">(</span>FieldItemListInterface <span class="token variable">$items</span><span class="token punctuation">,</span> <span class="token variable">$delta</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$element</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token operator">&amp;</span><span class="token variable">$form</span><span class="token punctuation">,</span> FormStateInterface <span class="token variable">$form_state</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
      <span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> <span class="token variable">$element</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

On remarquera l’utilisation de getSettings() qui permet de récupérer de la configuration qui pourrait être définie via settingsForm() et configurable dans l’interface de gestion de l’affichage du formulaire.

Le formateur du champ

FieldFormatter

Interface : Drupal\Core\Field\FormatterInterface

Classe abstraite : Drupal\Core\Field\FormatterBase

Répertoire d’implémentation : /src/Plugin/Field/FieldFormatter/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldFormatter

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_formatter_info()

Annotation de type @FieldFormatter

hook_field_formatter_view()

FormatterInterface::viewElements()

hook_field_formatter_settings_form()

FormatterInterface::settingsForm()

hook_field_formatter_settings_summary()

FormatterInterface::settingsSummary()

Pour le formateur d’un champ, le travail est le même, cela débute par l’implémentation d’un Plugin avec une Annotation @FieldFormatter. Il faut ensuite implémenter viewElements() pour définir le rendu des valeurs. Enfin settingsForm() et settingsSummary() permettent de définir le formulaire des paramètres du champ et le résumé de leur valeur utilisés dans l’interface de gestion des View modes..

Une version détaillée de cette partie est visible dans notre article sur la création d'un formateur de champs.

Validation des données

Drupal introduit un concept de validateurs de contraintes issu de Symfony permettant de contrôler les valeurs d’un fieldType à la sauvegarde. On pourrait rajouter par exemple la ligne

<span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">addConstraint</span><span class="token punctuation">(</span><span class="token string">'Length'</span><span class="token punctuation">,</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token string">'max'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span> min <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

sur les propriétés définies dans propertyDefinitions() pour que cette validation soit faite automatiquement. Il en existe de plusieurs type (unicité, plage, bundle, etc) et il est possible de les étendre car ce sont des plugins de type @Constraint, cela sera vu dans un autre chapitre.

De manière plus classique, il est toujours possible de faire des validations au niveau du formulaire du widget en utilisant la Form API. La validation d’un élément du formulaire utilise toujours #element_validate, par contre on passe maintenant un tableau avec la classe utilisée et la méthode de la classe, plutôt qu’un nom de fonction.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
<span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
<span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$default_isbn_value</span><span class="token punctuation">,</span>
<span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">'#element_validate'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token keyword">array</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token punctuation">,</span> <span class="token string">'validateIsbnElement'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

Schéma et Configuration du champ

La plupart des Plugins implémentés durant cet exercice peuvent s’enrichir de configurations. Dans ce cas, elles sont stockées à l’aide d’entités de configuration et il faut déclarer le schéma de ces entités pour qu’elles puissent être exportées par le gestionnaire de configuration, profiter de la traduction et permettre le typage automatique des données.

La déclaration de ces schémas se fait dans le fichier /config/schema/isbn.schema.yml comme cela a été vue dans le chapitre sur la configuration (Configuration : fondements)

<span class="token comment" spellcheck="true">#/config/schema/isbn.schema.yml</span>

<span class="token key atrule">field.widget.settings.isbn_default</span><span class="token punctuation">:</span>
<span class="token key atrule">type</span><span class="token punctuation">:</span> mapping
<span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Isbn format settings'</span>
<span class="token key atrule">mapping</span><span class="token punctuation">:</span>
   <span class="token key atrule">placeholder_isbn_10</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 10'</span>
   <span class="token key atrule">placeholder_isbn_13</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 13'</span>
Par Artusamak
Julien Dubois

Drupal 8 : Field API / Créer un nouveau type de champ

Drupal 8 : Field API / Créer un nouveau type de champ
Artusamak
mar 12/04/2016 - 15:25

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Principes

Après l’intégration de CCK dans le coeur de Drupal 7, la Field API a continué d’évoluer. Il était frustrant de ne pas pouvoir appliquer aux propriétés des entités des formateurs ou des widgets. C’est maintenant possible si la déclaration de ces propriétés le permet ou en l’altérant (Cela est nécessaire pour l’entité Node et les champs title, author, created, etc.).

Les propriétés s’appellent des champs de base (Base fields) et les champs “classiques” sont des champs configurables (Configurable fields). Les propriétés n’ont pas totalement disparu : les champs sont composés de propriétés. Exemple : une valeur pour un champ de texte simple (Textfield), une valeur et un format de texte pour un champ de texte long (Textarea).
Dans la plupart des cas, les propriétés seront associées à une colonne dans une table mais pas toujours. Il est possible d’avoir des propriétés qui stockent des données calculées. Dans le cas précédent, le texte rendu dans le format de texte sera une donnée calculée que nous stockerons dans une propriété. Ces propriétés calculées combinées au cache de l’API de rendu permettent d’optimiser les performances. Pour voir comment implémenter ces données calculées, référez-vous à la documentation.

Les types de champs, formateurs et widgets sont des Plugins. Si vous souhaitez implémenter l’un de ces trois type de Plugin il suffit d’implémenter l’interface associée ou, si vous ne souhaitez pas réinventer la roue, étendre la classe annotée de base de chaque type :

TYPE DE PLUGIN

ANNOTATION

INTERFACE

CLASSE

Type de champ

@FieldType

FieldItemInterface

FieldItemBase

Widget

@FieldWidget

WidgetInterface

WidgetBase

Formateur

@FieldFormatter

FormatterInterface

FormatterBase

Le chemin PSR-4 de votre classe prend la forme suivante :  src/Plugin/Field/Field&lt;Type|Widget|Formatter&gt;/&lt;nomPlugin&gt;.php

À noter également que les champs sont maintenant stockés par type d’entité. Il devient donc possible d’utiliser le même nom de champ à plusieurs endroits.

Pour les développeurs habitués à développer avec les champs dans Drupal 7, un changement sémantique intervient avec Drupal 8. La notion de “champ” (Field) définissant la structure des données est maintenant appelé FieldStorage alors que la notion “d’instance de champ” identifiant la configuration d’un champ associé à une entité s’appelle désormais Field.

Exemple

La création d’un type de champ peut être nécessaire dans Drupal, notamment pour réaliser un champ composé de plusieurs données (comme link qui propose un titre et une URL). Cela permet de s’affranchir des modules comme Field collection ou Paragraphs pour simplifier le modèle de données ou compléter un type de champ existant proche de nos besoins. Nous allons voir l'implémentation d'un type de champ collectant un ISBN de livre à 10 ou 13 chiffres (2 champs de collecte).

Dans Drupal un champ est composé de 3 parties. Une principale, le type de champ (FieldType) qui est la définition technique du champ, et deux parties d’interface ; à savoir : le widget (FieldWidget) utilisé pendant l’édition d’un contenu et le formateur (FieldFormatter) qui s’occupe du rendu du champ lors de son affichage.

Ces deux derniers éléments peuvent être créés indépendamment du FieldType, ce qui permet de proposer des FieldWidget ou des FieldFormatter pour n’importe quel FieldType.

Chacune de ces 3 parties est gérée à l’aide de Plugins. Voici pour chacune les informations nécessaires à leur implémentation ainsi qu’un aperçu des méthodes qui remplissent les fonctions d’anciens hooks sous Drupal 7.

Le stockage des données

FieldType 

Interface : Drupal\Core\Field\FieldItemInterface

Classe abstraite : Drupal\Core\Field\FieldItemBase

Répertoire d’implémentation : /src/Plugin/Field/FieldType/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldType

Nom du hook Drupal 7

Équivalent Drupal 8

hook_field_info()

Annotation de type @FieldType

hook_field_schema()

FieldItemInterface::schema()

hook_field_is_empty()

ComplexDataInterface::isEmpty()

L’Annotation de ce Plugin est assez simple, l’identifiant machine, un label, une description et les valeurs par défaut du widget et du formateur utilisé pour ce champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token operator">/</span><span class="token operator">*</span><span class="token operator">*</span>
<span class="token operator">*</span> Plugin implementation of the <span class="token string">'isbn'</span> field type<span class="token punctuation">.</span>
<span class="token operator">*</span>
<span class="token operator">*</span> @<span class="token function">FieldType</span><span class="token punctuation">(</span>
<span class="token operator">*</span>   id <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   label <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Isbn"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   description <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Stores a ISBN string in various format."</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_widget <span class="token operator">=</span> <span class="token string">"isbn_default"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_formatter <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span> <span class="token punctuation">)</span>
<span class="token operator">*</span>

La création d’un type de champ passe par la définition du modèle de données de ce champ. Pour cela il faut implémenter les méthodes schema() et propertyDefinitions(). Comme pour Drupal 7, avec le hook_field_schema() il s’agit de décrire la table SQL qui va recevoir les données.

Dans notre cas nous aurons 2 valeurs de l’ISBN à stocker.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">schema</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
   <span class="token string">'columns'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
     <span class="token string">'isbn_13'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 13 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
     <span class="token string">'isbn_10'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 10 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">10</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
   <span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La méthode propertyDefinitions() quant à elle permet une description au niveau de Drupal et propose plus d’informations. La description des propriétés se fait grâce à la Typed Data API qui permet d’interagir avec les données et leurs meta-données. Exemple : donner un nom plus compréhensible par un humain avec setLabel(), rendre un champ obligatoire avec setRequired(), définir des contraintes de validation avec addConstraint()...

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">propertyDefinitions</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token variable">$properties</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Si l’un veut créer un champ composé, deux autres méthodes sont particulièrement importantes. isEmpty() permet à Drupal de savoir si votre champ doit être considéré comme vide pour afficher ou non le champ par exemple. Dans notre cas, on va considérer que c’est la valeur de la propriété ‘isbn_13’ qui va déterminer cela.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$value</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'isbn_13'</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">empty</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La deuxième est mainPropertyName() qui permet de définir le nom de la propriété principale. La plupart des champs de base utilisent ‘value’ mais cela devient vite gênant quand on construit des champs complexes. Il est donc essentiel de fournir cette information aux autres modules pour qu’ils puissent utiliser au mieux notre champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">mainPropertyName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">'isbn_13'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Bien sûr il existe encore de multiples méthodes, notamment les fieldSettingsForm(), preSave(), delete() et autres pour agir à différents moments de la vie de nos données de champ mais je vous laisse découvrir cela en regardant l’interface Drupal\Core\Field\FieldItemInterface.

On notera la présence de generateSampleValue() permettant de fournir un jeu de données basiques jouant le rôle de données de substitution lors de la génération de contenus fictifs. (Avec Devel generate par exemple).

Le widget du champ

FieldWidget

Interface : Drupal\Core\Field\WidgetInterface

Classe abstraite : Drupal\Core\Field\WidgetBase

Répertoire d’implémentation : /src/Plugin/Field/FieldWidget/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldWidget

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_widget_info()

Annotation de type @FieldWidget

hook_field_widget_form()

WidgetInterface::formElement()

hook_field_widget_error()

WidgetInterface::errorElement()

Encore une fois, on utilisera un Plugin pour créer le widget de notre champ. Nous allons donc le définir à l’aide d’une Annotation.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token comment" spellcheck="true">/**
* Plugin implementation of the 'isbn' widget.
*
* @FieldWidget(
*   id = "isbn_default",
*   label = @Translation("ISBN"),
*   field_types = {
*     "isbn"
*   }
* )
*/</span>

Ensuite, nous allons définir le formulaire qui sera utilisé dans l’interface pour réaliser la saisie des valeurs du champs dans la méthode formElement().

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">formElement</span><span class="token punctuation">(</span>FieldItemListInterface <span class="token variable">$items</span><span class="token punctuation">,</span> <span class="token variable">$delta</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$element</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token operator">&amp;</span><span class="token variable">$form</span><span class="token punctuation">,</span> FormStateInterface <span class="token variable">$form_state</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
      <span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> <span class="token variable">$element</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

On remarquera l’utilisation de getSettings() qui permet de récupérer de la configuration qui pourrait être définie via settingsForm() et configurable dans l’interface de gestion de l’affichage du formulaire.

Le formateur du champ

FieldFormatter

Interface : Drupal\Core\Field\FormatterInterface

Classe abstraite : Drupal\Core\Field\FormatterBase

Répertoire d’implémentation : /src/Plugin/Field/FieldFormatter/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldFormatter

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_formatter_info()

Annotation de type @FieldFormatter

hook_field_formatter_view()

FormatterInterface::viewElements()

hook_field_formatter_settings_form()

FormatterInterface::settingsForm()

hook_field_formatter_settings_summary()

FormatterInterface::settingsSummary()

Pour le formateur d’un champ, le travail est le même, cela débute par l’implémentation d’un Plugin avec une Annotation @FieldFormatter. Il faut ensuite implémenter viewElements() pour définir le rendu des valeurs. Enfin settingsForm() et settingsSummary() permettent de définir le formulaire des paramètres du champ et le résumé de leur valeur utilisés dans l’interface de gestion des View modes..

Une version détaillée de cette partie est visible dans notre article sur la création d'un formateur de champs.

Validation des données

Drupal introduit un concept de validateurs de contraintes issu de Symfony permettant de contrôler les valeurs d’un fieldType à la sauvegarde. On pourrait rajouter par exemple la ligne

<span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">addConstraint</span><span class="token punctuation">(</span><span class="token string">'Length'</span><span class="token punctuation">,</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token string">'max'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span> min <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

sur les propriétés définies dans propertyDefinitions() pour que cette validation soit faite automatiquement. Il en existe de plusieurs type (unicité, plage, bundle, etc) et il est possible de les étendre car ce sont des plugins de type @Constraint, cela sera vu dans un autre chapitre.

De manière plus classique, il est toujours possible de faire des validations au niveau du formulaire du widget en utilisant la Form API. La validation d’un élément du formulaire utilise toujours #element_validate, par contre on passe maintenant un tableau avec la classe utilisée et la méthode de la classe, plutôt qu’un nom de fonction.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
<span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
<span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$default_isbn_value</span><span class="token punctuation">,</span>
<span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">'#element_validate'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token keyword">array</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token punctuation">,</span> <span class="token string">'validateIsbnElement'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

Schéma et Configuration du champ

La plupart des Plugins implémentés durant cet exercice peuvent s’enrichir de configurations. Dans ce cas, elles sont stockées à l’aide d’entités de configuration et il faut déclarer le schéma de ces entités pour qu’elles puissent être exportées par le gestionnaire de configuration, profiter de la traduction et permettre le typage automatique des données.

La déclaration de ces schémas se fait dans le fichier /config/schema/isbn.schema.yml comme cela a été vue dans le chapitre sur la configuration (Configuration : fondements)

<span class="token comment" spellcheck="true">#/config/schema/isbn.schema.yml</span>

<span class="token key atrule">field.widget.settings.isbn_default</span><span class="token punctuation">:</span>
<span class="token key atrule">type</span><span class="token punctuation">:</span> mapping
<span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Isbn format settings'</span>
<span class="token key atrule">mapping</span><span class="token punctuation">:</span>
   <span class="token key atrule">placeholder_isbn_10</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 10'</span>
   <span class="token key atrule">placeholder_isbn_13</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 13'</span>
Par Artusamak
Julien Dubois

Drupal 8 : Field API / Créer un nouveau type de champ

Drupal 8 : Field API / Créer un nouveau type de champ
Artusamak
mar 12/04/2016 - 15:25

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Principes

Après l’intégration de CCK dans le coeur de Drupal 7, la Field API a continué d’évoluer. Il était frustrant de ne pas pouvoir appliquer aux propriétés des entités des formateurs ou des widgets. C’est maintenant possible si la déclaration de ces propriétés le permet ou en l’altérant (Cela est nécessaire pour l’entité Node et les champs title, author, created, etc.).

Les propriétés s’appellent des champs de base (Base fields) et les champs “classiques” sont des champs configurables (Configurable fields). Les propriétés n’ont pas totalement disparu : les champs sont composés de propriétés. Exemple : une valeur pour un champ de texte simple (Textfield), une valeur et un format de texte pour un champ de texte long (Textarea).
Dans la plupart des cas, les propriétés seront associées à une colonne dans une table mais pas toujours. Il est possible d’avoir des propriétés qui stockent des données calculées. Dans le cas précédent, le texte rendu dans le format de texte sera une donnée calculée que nous stockerons dans une propriété. Ces propriétés calculées combinées au cache de l’API de rendu permettent d’optimiser les performances. Pour voir comment implémenter ces données calculées, référez-vous à la documentation.

Les types de champs, formateurs et widgets sont des Plugins. Si vous souhaitez implémenter l’un de ces trois type de Plugin il suffit d’implémenter l’interface associée ou, si vous ne souhaitez pas réinventer la roue, étendre la classe annotée de base de chaque type :

TYPE DE PLUGIN

ANNOTATION

INTERFACE

CLASSE

Type de champ

@FieldType

FieldItemInterface

FieldItemBase

Widget

@FieldWidget

WidgetInterface

WidgetBase

Formateur

@FieldFormatter

FormatterInterface

FormatterBase

Le chemin PSR-4 de votre classe prend la forme suivante :  src/Plugin/Field/Field&lt;Type|Widget|Formatter&gt;/&lt;nomPlugin&gt;.php

À noter également que les champs sont maintenant stockés par type d’entité. Il devient donc possible d’utiliser le même nom de champ à plusieurs endroits.

Pour les développeurs habitués à développer avec les champs dans Drupal 7, un changement sémantique intervient avec Drupal 8. La notion de “champ” (Field) définissant la structure des données est maintenant appelé FieldStorage alors que la notion “d’instance de champ” identifiant la configuration d’un champ associé à une entité s’appelle désormais Field.

Exemple

La création d’un type de champ peut être nécessaire dans Drupal, notamment pour réaliser un champ composé de plusieurs données (comme link qui propose un titre et une URL). Cela permet de s’affranchir des modules comme Field collection ou Paragraphs pour simplifier le modèle de données ou compléter un type de champ existant proche de nos besoins. Nous allons voir l'implémentation d'un type de champ collectant un ISBN de livre à 10 ou 13 chiffres (2 champs de collecte).

Dans Drupal un champ est composé de 3 parties. Une principale, le type de champ (FieldType) qui est la définition technique du champ, et deux parties d’interface ; à savoir : le widget (FieldWidget) utilisé pendant l’édition d’un contenu et le formateur (FieldFormatter) qui s’occupe du rendu du champ lors de son affichage.

Ces deux derniers éléments peuvent être créés indépendamment du FieldType, ce qui permet de proposer des FieldWidget ou des FieldFormatter pour n’importe quel FieldType.

Chacune de ces 3 parties est gérée à l’aide de Plugins. Voici pour chacune les informations nécessaires à leur implémentation ainsi qu’un aperçu des méthodes qui remplissent les fonctions d’anciens hooks sous Drupal 7.

Le stockage des données

FieldType 

Interface : Drupal\Core\Field\FieldItemInterface

Classe abstraite : Drupal\Core\Field\FieldItemBase

Répertoire d’implémentation : /src/Plugin/Field/FieldType/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldType

Nom du hook Drupal 7

Équivalent Drupal 8

hook_field_info()

Annotation de type @FieldType

hook_field_schema()

FieldItemInterface::schema()

hook_field_is_empty()

ComplexDataInterface::isEmpty()

L’Annotation de ce Plugin est assez simple, l’identifiant machine, un label, une description et les valeurs par défaut du widget et du formateur utilisé pour ce champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token operator">/</span><span class="token operator">*</span><span class="token operator">*</span>
<span class="token operator">*</span> Plugin implementation of the <span class="token string">'isbn'</span> field type<span class="token punctuation">.</span>
<span class="token operator">*</span>
<span class="token operator">*</span> @<span class="token function">FieldType</span><span class="token punctuation">(</span>
<span class="token operator">*</span>   id <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   label <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Isbn"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   description <span class="token operator">=</span> @<span class="token function">Translation</span><span class="token punctuation">(</span><span class="token string">"Stores a ISBN string in various format."</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_widget <span class="token operator">=</span> <span class="token string">"isbn_default"</span><span class="token punctuation">,</span>
<span class="token operator">*</span>   default_formatter <span class="token operator">=</span> <span class="token string">"isbn"</span><span class="token punctuation">,</span>
<span class="token operator">*</span> <span class="token punctuation">)</span>
<span class="token operator">*</span>

La création d’un type de champ passe par la définition du modèle de données de ce champ. Pour cela il faut implémenter les méthodes schema() et propertyDefinitions(). Comme pour Drupal 7, avec le hook_field_schema() il s’agit de décrire la table SQL qui va recevoir les données.

Dans notre cas nous aurons 2 valeurs de l’ISBN à stocker.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">schema</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
   <span class="token string">'columns'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
     <span class="token string">'isbn_13'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 13 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
     <span class="token string">'isbn_10'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
       <span class="token string">'description'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'The isbn number with 10 digits.'</span><span class="token punctuation">,</span>
       <span class="token string">'type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'varchar'</span><span class="token punctuation">,</span>
       <span class="token string">'length'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">10</span><span class="token punctuation">,</span>
     <span class="token punctuation">)</span><span class="token punctuation">,</span>
   <span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La méthode propertyDefinitions() quant à elle permet une description au niveau de Drupal et propose plus d’informations. La description des propriétés se fait grâce à la Typed Data API qui permet d’interagir avec les données et leurs meta-données. Exemple : donner un nom plus compréhensible par un humain avec setLabel(), rendre un champ obligatoire avec setRequired(), définir des contraintes de validation avec addConstraint()...

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">propertyDefinitions</span><span class="token punctuation">(</span>FieldStorageDefinitionInterface <span class="token variable">$field_definition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token variable">$properties</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> DataDefinition<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token string">'string'</span><span class="token punctuation">)</span>
   <span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">setLabel</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token variable">$properties</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Si l’un veut créer un champ composé, deux autres méthodes sont particulièrement importantes. isEmpty() permet à Drupal de savoir si votre champ doit être considéré comme vide pour afficher ou non le champ par exemple. Dans notre cas, on va considérer que c’est la valeur de la propriété ‘isbn_13’ qui va déterminer cela.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$value</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'isbn_13'</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">empty</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

La deuxième est mainPropertyName() qui permet de définir le nom de la propriété principale. La plupart des champs de base utilisent ‘value’ mais cela devient vite gênant quand on construit des champs complexes. Il est donc essentiel de fournir cette information aux autres modules pour qu’ils puissent utiliser au mieux notre champ.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldType/IsbnItem.php</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">mainPropertyName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">'isbn_13'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Bien sûr il existe encore de multiples méthodes, notamment les fieldSettingsForm(), preSave(), delete() et autres pour agir à différents moments de la vie de nos données de champ mais je vous laisse découvrir cela en regardant l’interface Drupal\Core\Field\FieldItemInterface.

On notera la présence de generateSampleValue() permettant de fournir un jeu de données basiques jouant le rôle de données de substitution lors de la génération de contenus fictifs. (Avec Devel generate par exemple).

Le widget du champ

FieldWidget

Interface : Drupal\Core\Field\WidgetInterface

Classe abstraite : Drupal\Core\Field\WidgetBase

Répertoire d’implémentation : /src/Plugin/Field/FieldWidget/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldWidget

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_widget_info()

Annotation de type @FieldWidget

hook_field_widget_form()

WidgetInterface::formElement()

hook_field_widget_error()

WidgetInterface::errorElement()

Encore une fois, on utilisera un Plugin pour créer le widget de notre champ. Nous allons donc le définir à l’aide d’une Annotation.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token comment" spellcheck="true">/**
* Plugin implementation of the 'isbn' widget.
*
* @FieldWidget(
*   id = "isbn_default",
*   label = @Translation("ISBN"),
*   field_types = {
*     "isbn"
*   }
* )
*/</span>

Ensuite, nous allons définir le formulaire qui sera utilisé dans l’interface pour réaliser la saisie des valeurs du champs dans la méthode formElement().

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">formElement</span><span class="token punctuation">(</span>FieldItemListInterface <span class="token variable">$items</span><span class="token punctuation">,</span> <span class="token variable">$delta</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$element</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token operator">&amp;</span><span class="token variable">$form</span><span class="token punctuation">,</span> FormStateInterface <span class="token variable">$form_state</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_13</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
      <span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_10'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
      <span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
      <span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_10'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">isset</span><span class="token punctuation">(</span><span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$items</span><span class="token punctuation">[</span><span class="token variable">$delta</span><span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">isbn_10</span> <span class="token punctuation">:</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> <span class="token variable">$element</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

On remarquera l’utilisation de getSettings() qui permet de récupérer de la configuration qui pourrait être définie via settingsForm() et configurable dans l’interface de gestion de l’affichage du formulaire.

Le formateur du champ

FieldFormatter

Interface : Drupal\Core\Field\FormatterInterface

Classe abstraite : Drupal\Core\Field\FormatterBase

Répertoire d’implémentation : /src/Plugin/Field/FieldFormatter/

Namespace à utiliser : Drupal\&lt;module&gt;\Plugin\Field\FieldFormatter

Nom du hook Drupal 7

Equivalent Drupal 8

hook_field_formatter_info()

Annotation de type @FieldFormatter

hook_field_formatter_view()

FormatterInterface::viewElements()

hook_field_formatter_settings_form()

FormatterInterface::settingsForm()

hook_field_formatter_settings_summary()

FormatterInterface::settingsSummary()

Pour le formateur d’un champ, le travail est le même, cela débute par l’implémentation d’un Plugin avec une Annotation @FieldFormatter. Il faut ensuite implémenter viewElements() pour définir le rendu des valeurs. Enfin settingsForm() et settingsSummary() permettent de définir le formulaire des paramètres du champ et le résumé de leur valeur utilisés dans l’interface de gestion des View modes..

Une version détaillée de cette partie est visible dans notre article sur la création d'un formateur de champs.

Validation des données

Drupal introduit un concept de validateurs de contraintes issu de Symfony permettant de contrôler les valeurs d’un fieldType à la sauvegarde. On pourrait rajouter par exemple la ligne

<span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">addConstraint</span><span class="token punctuation">(</span><span class="token string">'Length'</span><span class="token punctuation">,</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token string">'max'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">,</span> min <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token number">13</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

sur les propriétés définies dans propertyDefinitions() pour que cette validation soit faite automatiquement. Il en existe de plusieurs type (unicité, plage, bundle, etc) et il est possible de les étendre car ce sont des plugins de type @Constraint, cela sera vu dans un autre chapitre.

De manière plus classique, il est toujours possible de faire des validations au niveau du formulaire du widget en utilisant la Form API. La validation d’un élément du formulaire utilise toujours #element_validate, par contre on passe maintenant un tableau avec la classe utilisée et la méthode de la classe, plutôt qu’un nom de fonction.

<span class="token shell-comment comment">#/src/Plugin/Field/FieldWidget/IsbnWidget.php</span>

<span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'isbn_13'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
<span class="token string">'#type'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'textfield'</span><span class="token punctuation">,</span>
<span class="token string">'#title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'ISBN-13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#placeholder'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">getSetting</span><span class="token punctuation">(</span><span class="token string">'placeholder_isbn_13'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">'#default_value'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$default_isbn_value</span><span class="token punctuation">,</span>
<span class="token string">'#required'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$element</span><span class="token punctuation">[</span><span class="token string">'#required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">'#element_validate'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span><span class="token keyword">array</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token punctuation">,</span> <span class="token string">'validateIsbnElement'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

Schéma et Configuration du champ

La plupart des Plugins implémentés durant cet exercice peuvent s’enrichir de configurations. Dans ce cas, elles sont stockées à l’aide d’entités de configuration et il faut déclarer le schéma de ces entités pour qu’elles puissent être exportées par le gestionnaire de configuration, profiter de la traduction et permettre le typage automatique des données.

La déclaration de ces schémas se fait dans le fichier /config/schema/isbn.schema.yml comme cela a été vue dans le chapitre sur la configuration (Configuration : fondements)

<span class="token comment" spellcheck="true">#/config/schema/isbn.schema.yml</span>

<span class="token key atrule">field.widget.settings.isbn_default</span><span class="token punctuation">:</span>
<span class="token key atrule">type</span><span class="token punctuation">:</span> mapping
<span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Isbn format settings'</span>
<span class="token key atrule">mapping</span><span class="token punctuation">:</span>
   <span class="token key atrule">placeholder_isbn_10</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 10'</span>
   <span class="token key atrule">placeholder_isbn_13</span><span class="token punctuation">:</span>
     <span class="token key atrule">type</span><span class="token punctuation">:</span> string
     <span class="token key atrule">label</span><span class="token punctuation">:</span> <span class="token string">'Placeholder for ISBN 13'</span>
Par flocondetoile
Adhérent

Une application métier avec Drupal ?

Ce billet est une restranscription synthétique de la conférence Retour d'éxpérience : une application métier avec Drupal donnée au meetup Drupal Lyon le 7 avril 2016.

Drupal est particulièrement reconnu en tant que Gestionnaire de contenus (CMS) et plateforme de développement (CMF). Sa vocation principale est de propulser des sites Internet. Mais Drupal peut-il être utilisé comme support pour développer une application métier ?

Par flocondetoile
Adhérent

Une application métier avec Drupal ?

Ce billet est une restranscription synthétique de la conférence Retour d'expérience : une application métier avec Drupal donnée au meetup Drupal Lyon le 7 avril 2016.

Drupal est particulièrement reconnu en tant que Gestionnaire de contenus (CMS) et plateforme de développement (CMF). Sa vocation principale est de propulser des sites Internet. Mais Drupal peut-il être utilisé comme support pour développer une application métier ?

Par Artusamak
Julien Dubois

Drupal 8 : Plugins et types de plugins

Drupal 8 : Plugins et types de plugins
ven, 08/04/2016 - 09:24
Artusamak

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Le principe de plugins dans Drupal est de permettre au système de fournir une fonctionnalité extensible et remplaçable de manière simple.

D’une certaine manière les plugins remplacent bon nombre de hooks de Drupal 7 (les hook_*_info() et associés).

Deux concepts clés sont liés aux Plugins :

  • Les Plugins
  • Les types de Plugins (Plugins Type)

Si plusieurs plugins remplissent la même fonctionnalité, ils sont du même type (Plugin Type).

Les blocs sont par exemple des Plugins, chaque bloc en est un. Ils sont du Plugin Type Block.

Un autre exemple de Plugin Type est les ImageEffects, ils définissent les actions applicables sur une image. Chaque action ou effet est un Plugin.

Les Plugins sont utiles quand il est nécessaire de pouvoir facilement étendre une fonctionnalité générique mais que les implémentations possibles ne partagent que peu de code commun.
Pour les effets d'image utilisés par les styles d'image, chaque effet rempli le même but « Transformer une image » mais chaque implémentation peut être complètement différente (altérer les couleurs, redimensionner l’image).

Par Artusamak
Julien Dubois

Drupal 8 : Plugins et types de plugins

Drupal 8 : Plugins et types de plugins
Artusamak
ven 08/04/2016 - 09:24

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Le principe de plugins dans Drupal est de permettre au système de fournir une fonctionnalité extensible et remplaçable de manière simple.

D’une certaine manière les plugins remplacent bon nombre de hooks de Drupal 7 (les hook_*_info() et associés).

Deux concepts clés sont liés aux Plugins :

  • Les Plugins
  • Les types de Plugins (Plugins Type)

Si plusieurs plugins remplissent la même fonctionnalité, ils sont du même type (Plugin Type).

Les blocs sont par exemple des Plugins, chaque bloc en est un. Ils sont du Plugin Type Block.

Un autre exemple de Plugin Type est les ImageEffects, ils définissent les actions applicables sur une image. Chaque action ou effet est un Plugin.

Les Plugins sont utiles quand il est nécessaire de pouvoir facilement étendre une fonctionnalité générique mais que les implémentations possibles ne partagent que peu de code commun.
Pour les effets d'image utilisés par les styles d'image, chaque effet rempli le même but « Transformer une image » mais chaque implémentation peut être complètement différente (altérer les couleurs, redimensionner l’image).

Par Artusamak
Julien Dubois

Drupal 8 : Plugins et types de plugins

Drupal 8 : Plugins et types de plugins
Artusamak
ven 08/04/2016 - 09:24

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Le principe de plugins dans Drupal est de permettre au système de fournir une fonctionnalité extensible et remplaçable de manière simple.

D’une certaine manière les plugins remplacent bon nombre de hooks de Drupal 7 (les hook_*_info() et associés).

Deux concepts clés sont liés aux Plugins :

  • Les Plugins
  • Les types de Plugins (Plugins Type)

Si plusieurs plugins remplissent la même fonctionnalité, ils sont du même type (Plugin Type).

Les blocs sont par exemple des Plugins, chaque bloc en est un. Ils sont du Plugin Type Block.

Un autre exemple de Plugin Type est les ImageEffects, ils définissent les actions applicables sur une image. Chaque action ou effet est un Plugin.

Les Plugins sont utiles quand il est nécessaire de pouvoir facilement étendre une fonctionnalité générique mais que les implémentations possibles ne partagent que peu de code commun.
Pour les effets d'image utilisés par les styles d'image, chaque effet rempli le même but « Transformer une image » mais chaque implémentation peut être complètement différente (altérer les couleurs, redimensionner l’image).

Par vincent59
Vincent Liefooghe

Cartographie Drupal : Views + Geofield Map

Dans les articles précédents (stockage des données notamment) , nous avons vu comment ajouter un champ de type Geofield et l'afficher sous forme de carte.

Nous allons voir ici comment afficher plusieurs points sur une seule carte. Pour cela, nous devons juste installer Views, et activer les modules Views, Views UI et Geofield Map.

drush dl views
drush en views views_ui geofield_map

Création de la vue

Il faut ensuite créer une vue. Pour cela, on va dans Structure /  Vues, puis on va ajouter une vue (admin/structure/views/add). Nous allons nous baser sur le type de contenu créé auparavant,  qui contient un champ Geofield. On donne un nom à la vue, puis on sélectionne le type de contenu (Magasin). On peut créer un block, sur la base d'une liste de champs (Unformatted list of fields).

Si on veut on peut également créer une page, tout dépend de ce que l'on veut faire.

On clique ensuite sur Continue & Edit pour continuer la création de la vue.

Choix des champs

Par défaut, seul le titre est affiché :

Il faut alors cliquer sur "Add", puis choisir le champ Coordonnées qui est de type Geofield.

 

On supprime l'affichage du libellé, et on laisse les valeurs par défaut (y compris le formatter en Well Known Text). En effet c'est dans le type de formatage global que l'on choisira Google Map.

On valide tout cela. Dans les chjamps, on doit donc avoir Title et Coordonnées.

Choix du format d'affichage

A ce stade, on a uniquement le titre et les coordonnées. Rien de très sympathique. Il faut alors changer le format de la vue :

A ce niveau, on peut choisir "Geofield Map" :

On valide en cliquant sur "Apply". On peut alors choisir quel champ sert de source. On va choisir le champ field_coordonnees que l'on vient d'ajouter à notre vue :

On peut laisser les valeurs par défaut dans un premier temps. Si on se rend à l'url de la vue, on a un premier résultat :

Si on clique sur l'un des marqueurs, le titre apparaît.

Amélioration de l'affichage

En modifiant la vue on peut facilement :

  • Ajouter des informations dans la Pop-Up
  • Permettre le scroll dans la carte (ScrollWheel)
  • Mettre un niveau de zoom par défaut (Zoom / Zoom minimum et maximum)

Dans l'exemple, la hauteur de la carte a été modifiée à 450 pixels, et on a mis la description plutôt que le titre dans la pop-up.

C'est globalement la limite du couple Views + Geofield Map, qui offre un premier niveau de formatage, sans beaucoup de souplesse.

On peut aller plus loin, avec d'autres modules. Ceci fera l'objet d'un autre article.

 

 

 

Catégorie: 


Tag: 

Par Artusamak
Julien Dubois

Drupal 8 : les annotations

Drupal 8 : les annotations
mer, 06/04/2016 - 09:48
Artusamak

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Une partie des hooks déclaratifs de Drupal 7 ont disparu avec Drupal 8 (les hook_quelque_chose_info() par exemple). Certains ont été remplacés par l’utilisation des fichiers YAML (hook_menu() ou hook_permission()). Pour d’autres, c’est le principe d’annotations qui a été choisi.

Une annotation est un élément de programmation permettant d’ajouter des méta-données à une structure à l'aide d'un code descriptif. Avec PHP, cela passe par l’utilisation des Docblocks. Dans Drupal 8, les annotations sont pour le moment essentiellement utilisées par les plugins, elles sont à placer juste avant la déclaration de la classe du plugin.
Voilà à quoi ressemble la déclaration d’un bloc sous Drupal 8 :

<span class="comment shell-comment token"># Plugin/Block/CustomBlock.php</span>
<span class="comment token" spellcheck="true">/**
* Declare a block.
*
* @Block(
*   id = "custom_block",
*   admin_label = @Translation("Custom block"),
* )
*/</span>

La déclaration du même block en Drupal 7 passait par un hook_block_info().

<span class="comment token" spellcheck="true">/**
* Implements hook_block_info().
*/</span>
<span class="keyword token">function</span> <span class="function token">mymodule_core_block_info</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="token variable">$blocks</span><span class="punctuation token">[</span><span class="string token">'custom_block'</span><span class="punctuation token">]</span> <span class="operator token">=</span> <span class="keyword token">array</span><span class="punctuation token">(</span>
    <span class="string token">'info'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="function token">t</span><span class="punctuation token">(</span><span class="string token">'Custom block'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
  <span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

Bien que les annotations soient des Docblocks leur syntaxe est précise et cela pourra entraîner des erreurs si elle n’est pas valide. Une annotation est composée du nom de la classe définissant l’annotation précédé d’un @ et d’un ensemble de clés / valeurs.

Il existe quelques règles concernant les valeurs, il faut retenir que :

  • les chaînes doivent être entre guillemets : “chaînes”
  • les nombres tel quel : 42
  • les listes sont représentées par des accolades : { }
  • les booléens par : TRUE ou FALSE

Il est possible de connaître l'ensemble des propriétés d'une annotation en regardant la classe qui définie cette annotation. Par exemple, l’annotation @Block est définie dans la classe \Drupal\Core\Block\Annotation\Block se trouvant dans le fichier /core/lib/Drupal/Core/Block/Annotation/Block.php.

<span class="comment shell-comment token"># Block.php</span>
<span class="keyword token">class</span> <span class="class-name token">Block</span> <span class="keyword token">extends</span> <span class="class-name token">Plugin</span> <span class="punctuation token">{</span>
  <span class="comment token" spellcheck="true">/**
   * The plugin ID.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$id</span><span class="punctuation token">;</span>
  <span class="comment token" spellcheck="true">/**
   * The administrative label of the block.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$admin_label</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * The category in the admin UI where the block will be listed.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$category</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * Class used to retrieve derivative definitions of the block.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$derivative</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

On remarquera alors que chaque propriété de la classe correspond à une ligne de l'annotation pouvant être déclarée.

Par Artusamak
Julien Dubois

Drupal 8 : les annotations

Drupal 8 : les annotations
Artusamak
mer 06/04/2016 - 09:48

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Une partie des hooks déclaratifs de Drupal 7 ont disparu avec Drupal 8 (les hook_quelque_chose_info() par exemple). Certains ont été remplacés par l’utilisation des fichiers YAML (hook_menu() ou hook_permission()). Pour d’autres, c’est le principe d’annotations qui a été choisi.

Une annotation est un élément de programmation permettant d’ajouter des méta-données à une structure à l'aide d'un code descriptif. Avec PHP, cela passe par l’utilisation des Docblocks. Dans Drupal 8, les annotations sont pour le moment essentiellement utilisées par les plugins, elles sont à placer juste avant la déclaration de la classe du plugin.
Voilà à quoi ressemble la déclaration d’un bloc sous Drupal 8 :

<span class="comment shell-comment token"># Plugin/Block/CustomBlock.php</span>
<span class="comment token" spellcheck="true">/**
* Declare a block.
*
* @Block(
*   id = "custom_block",
*   admin_label = @Translation("Custom block"),
* )
*/</span>

La déclaration du même block en Drupal 7 passait par un hook_block_info().

<span class="comment token" spellcheck="true">/**
* Implements hook_block_info().
*/</span>
<span class="keyword token">function</span> <span class="function token">mymodule_core_block_info</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="token variable">$blocks</span><span class="punctuation token">[</span><span class="string token">'custom_block'</span><span class="punctuation token">]</span> <span class="operator token">=</span> <span class="keyword token">array</span><span class="punctuation token">(</span>
    <span class="string token">'info'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="function token">t</span><span class="punctuation token">(</span><span class="string token">'Custom block'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
  <span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

Bien que les annotations soient des Docblocks leur syntaxe est précise et cela pourra entraîner des erreurs si elle n’est pas valide. Une annotation est composée du nom de la classe définissant l’annotation précédé d’un @ et d’un ensemble de clés / valeurs.

Il existe quelques règles concernant les valeurs, il faut retenir que :

  • les chaînes doivent être entre guillemets : “chaînes”
  • les nombres tel quel : 42
  • les listes sont représentées par des accolades : { }
  • les booléens par : TRUE ou FALSE

Il est possible de connaître l'ensemble des propriétés d'une annotation en regardant la classe qui définie cette annotation. Par exemple, l’annotation @Block est définie dans la classe \Drupal\Core\Block\Annotation\Block se trouvant dans le fichier /core/lib/Drupal/Core/Block/Annotation/Block.php.

<span class="comment shell-comment token"># Block.php</span>
<span class="keyword token">class</span> <span class="class-name token">Block</span> <span class="keyword token">extends</span> <span class="class-name token">Plugin</span> <span class="punctuation token">{</span>
  <span class="comment token" spellcheck="true">/**
   * The plugin ID.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$id</span><span class="punctuation token">;</span>
  <span class="comment token" spellcheck="true">/**
   * The administrative label of the block.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$admin_label</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * The category in the admin UI where the block will be listed.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$category</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * Class used to retrieve derivative definitions of the block.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$derivative</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

On remarquera alors que chaque propriété de la classe correspond à une ligne de l'annotation pouvant être déclarée.

Par Artusamak
Julien Dubois

Drupal 8 : les annotations

Drupal 8 : les annotations
Artusamak
mer 06/04/2016 - 09:48

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Une partie des hooks déclaratifs de Drupal 7 ont disparu avec Drupal 8 (les hook_quelque_chose_info() par exemple). Certains ont été remplacés par l’utilisation des fichiers YAML (hook_menu() ou hook_permission()). Pour d’autres, c’est le principe d’annotations qui a été choisi.

Une annotation est un élément de programmation permettant d’ajouter des méta-données à une structure à l'aide d'un code descriptif. Avec PHP, cela passe par l’utilisation des Docblocks. Dans Drupal 8, les annotations sont pour le moment essentiellement utilisées par les plugins, elles sont à placer juste avant la déclaration de la classe du plugin.
Voilà à quoi ressemble la déclaration d’un bloc sous Drupal 8 :

<span class="comment shell-comment token"># Plugin/Block/CustomBlock.php</span>
<span class="comment token" spellcheck="true">/**
* Declare a block.
*
* @Block(
*   id = "custom_block",
*   admin_label = @Translation("Custom block"),
* )
*/</span>

La déclaration du même block en Drupal 7 passait par un hook_block_info().

<span class="comment token" spellcheck="true">/**
* Implements hook_block_info().
*/</span>
<span class="keyword token">function</span> <span class="function token">mymodule_core_block_info</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="token variable">$blocks</span><span class="punctuation token">[</span><span class="string token">'custom_block'</span><span class="punctuation token">]</span> <span class="operator token">=</span> <span class="keyword token">array</span><span class="punctuation token">(</span>
    <span class="string token">'info'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="function token">t</span><span class="punctuation token">(</span><span class="string token">'Custom block'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
  <span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

Bien que les annotations soient des Docblocks leur syntaxe est précise et cela pourra entraîner des erreurs si elle n’est pas valide. Une annotation est composée du nom de la classe définissant l’annotation précédé d’un @ et d’un ensemble de clés / valeurs.

Il existe quelques règles concernant les valeurs, il faut retenir que :

  • les chaînes doivent être entre guillemets : “chaînes”
  • les nombres tel quel : 42
  • les listes sont représentées par des accolades : { }
  • les booléens par : TRUE ou FALSE

Il est possible de connaître l'ensemble des propriétés d'une annotation en regardant la classe qui définie cette annotation. Par exemple, l’annotation @Block est définie dans la classe \Drupal\Core\Block\Annotation\Block se trouvant dans le fichier /core/lib/Drupal/Core/Block/Annotation/Block.php.

<span class="comment shell-comment token"># Block.php</span>
<span class="keyword token">class</span> <span class="class-name token">Block</span> <span class="keyword token">extends</span> <span class="class-name token">Plugin</span> <span class="punctuation token">{</span>
  <span class="comment token" spellcheck="true">/**
   * The plugin ID.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$id</span><span class="punctuation token">;</span>
  <span class="comment token" spellcheck="true">/**
   * The administrative label of the block.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$admin_label</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * The category in the admin UI where the block will be listed.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$category</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>

  <span class="comment token" spellcheck="true">/**
   * Class used to retrieve derivative definitions of the block.
   *
   * @var string
   */</span>
  <span class="keyword token">public</span> <span class="token variable">$derivative</span> <span class="operator token">=</span> <span class="string token">''</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

On remarquera alors que chaque propriété de la classe correspond à une ligne de l'annotation pouvant être déclarée.

Par Artusamak
Julien Dubois

Drupal 8 : Hooks et événements

Drupal 8 : Hooks et événements
lun, 04/04/2016 - 10:30
Artusamak

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

L’une des forces de Drupal depuis sa création est la facilité d’utilisation de son mécanisme d’extensibilité. Autrement dit, ses hooks. Implémenter un hook est très simple, il suffit de respecter une convention de nommage.

Si l’invocation de ces hooks pouvait se faire facilement dans Drupal 7, la syntaxe évolue quelque peu :

<span class="comment shell-comment token"># Cron.php</span>
<span class="keyword token">class</span> <span class="class-name token">Cron</span> <span class="keyword token">implements</span> <span class="class-name token">CronInterface</span> <span class="punctuation token">{</span>
<span class="keyword token">protected</span> <span class="token variable">$moduleHandler</span><span class="punctuation token">;</span> <span class="comment token" spellcheck="true">// The module handler service.</span>
<span class="comment token" spellcheck="true">/**
  * Invokes any cron handlers implementing hook_cron.
  */</span>
<span class="keyword token">protected</span> <span class="keyword token">function</span> <span class="function token">invokeCronHandlers</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
   <span class="comment token" spellcheck="true">// Iterate through the modules calling their cron handlers (if any):</span>
   <span class="keyword token">foreach</span> <span class="punctuation token">(</span><span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">moduleHandler</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getImplementations</span><span class="punctuation token">(</span><span class="string token">'cron'</span><span class="punctuation token">)</span> <span class="keyword token">as</span> <span class="token variable">$module</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
     <span class="comment token" spellcheck="true">// Do not let an exception thrown by one module disturb another.</span>
     <span class="keyword token">try</span> <span class="punctuation token">{</span>
       <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">moduleHandler</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">invoke</span><span class="punctuation token">(</span><span class="token variable">$module</span><span class="punctuation token">,</span> <span class="string token">'cron'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
     <span class="punctuation token">}</span>
     <span class="keyword token">catch</span> <span class="punctuation token">(</span><span class="class-name token"><span class="punctuation token">\</span>Exception</span> <span class="token variable">$e</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
       <span class="function token">watchdog_exception</span><span class="punctuation token">(</span><span class="string token">'cron'</span><span class="punctuation token">,</span> <span class="token variable">$e</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
     <span class="punctuation token">}</span>
   <span class="punctuation token">}</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>

Les fonctions de module_implements(), module_invoke(), module_invoke_all() et drupal_alter(), entre autres, ont été déplacées (et renommées) dans la classe ModuleHandler. Pour les utiliser il faudra passer par le Service module_handler et appeler la méthode que vous aviez l’habitude d’appeler. Si vous ne vous rappelez plus ce qu'est un Service, je vous invite à lire le billet sur Conteneur de Services et Services.

Tous les hooks ne sont pas morts, ce sont les hooks qui permettent à d’autres modules d’étendre Drupal qui ont été renommés, l’altération d’un formulaire se passe toujours via hook_form_FORM_ID_alter(), les thèmes et modules peuvent toujours implémenter hook_preprocess_HOOK() par exemple.

Dans le chapitre suivant dédié aux annotations nous verrons qu’il y a d’autres mécanismes qui remplacent également les hook_*_info().

Hooks VS Events

Avec l’adoption de Symfony, beaucoup de choix historiques sont remis en cause. Le principe des hooks peut être implémenté en suivant plusieurs design patterns différents, seulement voilà, Symfony vient avec sa propre implémentation grâce à sa classe EventDispatcher. Lorsque l’on souhaite connaître tous les modules qui implémentent un hook, on déclenche (dispatch) un événement. Chaque module qui implémente ce hook se signale en souscrivant (subscribe) à cet événement et retourne les données appropriées.

On retrouve par exemple l’utilisation des événements dans la gestion des routes pour afficher une réponse HTML.

<span class="comment shell-comment token"># core.services.yml</span>
html_response<span class="punctuation token">.</span>subscriber<span class="punctuation token">:</span>
<span class="keyword token">class</span><span class="punctuation token">:</span> Drupal\<span class="package token">Core<span class="punctuation token">\</span>EventSubscriber<span class="punctuation token">\</span>HtmlResponseSubscriber</span>
tags<span class="punctuation token">:</span>
   <span class="operator token">-</span> <span class="punctuation token">{</span> name<span class="punctuation token">:</span> event_subscriber <span class="punctuation token">}</span>
<span class="comment shell-comment token"># HtmlResponseSubscriber.php</span>
<span class="keyword token">class</span> <span class="class-name token">HtmlResponseSubscriber</span> <span class="keyword token">implements</span> <span class="class-name token">EventSubscriberInterface</span> <span class="punctuation token">{</span>
<span class="comment token" spellcheck="true">// The HTML response attachments processor service.</span>
<span class="keyword token">protected</span> <span class="token variable">$htmlResponseAttachmentsProcessor</span><span class="punctuation token">;</span>

<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">__construct</span><span class="punctuation token">(</span>AttachmentsResponseProcessorInterface <span class="token variable">$html_response_attachments_processor</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
   <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">htmlResponseAttachmentsProcessor</span> <span class="operator token">=</span> <span class="token variable">$html_response_attachments_processor</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

<span class="keyword token">public</span> <span class="keyword token">static</span> <span class="keyword token">function</span> <span class="function token">getSubscribedEvents</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
   <span class="comment token" spellcheck="true">// Appeler la méthode onRespond lorsque l'événement KernelEvents::RESPONSE survient.</span>
   <span class="token variable">$events</span><span class="punctuation token">[</span>KernelEvents<span class="punctuation token">:</span><span class="punctuation token">:</span><span class="constant token">RESPONSE</span><span class="punctuation token">]</span><span class="punctuation token">[</span><span class="punctuation token">]</span> <span class="operator token">=</span> <span class="punctuation token">[</span><span class="string token">'onRespond'</span><span class="punctuation token">]</span><span class="punctuation token">;</span>
   <span class="keyword token">return</span> <span class="token variable">$events</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

<span class="comment token" spellcheck="true">/**
  * Processes attachments for HtmlResponse responses.
  */</span>
<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">onRespond</span><span class="punctuation token">(</span>FilterResponseEvent <span class="token variable">$event</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
   <span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span><span class="token variable">$event</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">isMasterRequest</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
     <span class="keyword token">return</span><span class="punctuation token">;</span>
   <span class="punctuation token">}</span>

   <span class="token variable">$response</span> <span class="operator token">=</span> <span class="token variable">$event</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getResponse</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
   <span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span><span class="token variable">$response</span> <span class="keyword token">instanceof</span> <span class="class-name token">HtmlResponse</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
     <span class="keyword token">return</span><span class="punctuation token">;</span>
   <span class="punctuation token">}</span>
   <span class="token variable">$event</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">setResponse</span><span class="punctuation token">(</span><span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">htmlResponseAttachmentsProcessor</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">processAttachments</span><span class="punctuation token">(</span><span class="token variable">$response</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>

On déclare un Service qui implémente l’interface EventSubscriberInterface, on y implémente la méthode getSubscribedEvents() pour indiquer à quel(s) événement(s) réagir et quelle fonction appeler lorsque l’événement survient. L’appel à ce Service se fait automagiquement uniquement si on l’a tagué comme event_subscriber.

eventdispatchercomic-fullsize.png

Par cohérence tous les hooks devraient être remplacés par les événements mais pour des questions de temps la conversion a été retardée (voir https://www.drupal.org/node/1972304). Il est fort à parier que les événements prendront le pas sur les hooks dans la prochaine version majeure de Drupal. On peut donc dire que les hooks sont morts, vive les hooks !

Par GoZ
Fabien CLEMENT

Contribuer en tant que développeur à Drupal via Drupal.org

Contribuer en tant que développeur à Drupal via Drupal.org

La contribution sur drupal.org ne se fait pas via 'pull request' comme sur github mais fonctionne encore avec des patchs. Même si quelques modules sont disponibles sur github, ils ne sont censés être que des répliques, les repos principaux et les issues devant se gérer directement sur drupal.org.

Une documentation fixe la charte de bonne utilisation de git, ce qui permet d'aider les contributeurs à apprendre à générer un patch correct et permet au mainteneur de pointer vers cette documentation si besoin.

GoZ
jeu 31/03/2016 - 19:19

Par Artusamak
Julien Dubois

Drupal 8 : L'injection de dépendance

Drupal 8 : L'injection de dépendance
jeu, 31/03/2016 - 09:08
Artusamak

Cet article est extrait de notre formation drupal 8 "de Drupal 7 à Drupal 8" à destination des développeurs. N'hésitez pas à nous contacter pour en savoir plus !

Définition

L’injection de dépendance explicite

Nous avons vu grâce au Conteneur de Services qu’il était possible de réutiliser les objets et d’interchanger leur implémentation. Toute application ayant un minimum de valeur ajoutée, il est probable que tous ces objets aient des liens entre eux. Bien souvent, on parle de dépendance(s). L’Injection de dépendance est donc un gros mot pour désigner une façon de créer les instances des objets et de lier les objets entre eux. L’Injection de dépendance est l’un des nombreux design patterns utilisés dans Drupal.

Si l’on reprend notre exemple de Service utilisé dans le chapitre précédent, nous avons vu un Service simple, prenons maintenant l’exemple d’un Service plus compliqué, qui, pour fonctionner, doit utiliser un autre Service.

L’exemple est celui du Service flood qui permet de limiter le nombre d’actions d’un utilisateur.

Voici la définition du Service, nous allons le détailler juste après.

<span class="comment shell-comment token"># core.services.yml</span>
flood<span class="punctuation token">:</span>
<span class="keyword token">class</span><span class="punctuation token">:</span> Drupal\<span class="package token">Core<span class="punctuation token">\</span>Flood<span class="punctuation token">\</span>DatabaseBackend</span>
arguments<span class="punctuation token">:</span> <span class="punctuation token">[</span><span class="string token">'@database'</span><span class="punctuation token">,</span> <span class="string token">'@request_stack'</span><span class="punctuation token">]</span>
tags<span class="punctuation token">:</span>
   <span class="operator token">-</span> <span class="punctuation token">{</span> name<span class="punctuation token">:</span> backend_overridable <span class="punctuation token">}</span>

Et voici le constructeur ainsi que la méthode register() de la classe Flood\DatabaseBackend.

<span class="comment shell-comment token"># DatabaseBackend.php</span>
<span class="keyword token">class</span> <span class="class-name token">DatabaseBackend</span> <span class="keyword token">implements</span> <span class="class-name token">FloodInterface</span> <span class="punctuation token">{</span>

<span class="comment token" spellcheck="true">/**
* The database connection used to store flood event information.
*
* @var \Drupal\Core\Database\Connection
*/</span>
<span class="keyword token">protected</span> <span class="token variable">$connection</span><span class="punctuation token">;</span>

<span class="comment token" spellcheck="true">/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/</span>
<span class="keyword token">protected</span> <span class="token variable">$requestStack</span><span class="punctuation token">;</span>

<span class="comment token" spellcheck="true">/**
* Construct the DatabaseBackend.
*
* @param \Drupal\Core\Database\Connection $connection
*   The database connection which will be used to store the flood event
*   information.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
*   The request stack used to retrieve the current request.
*/</span>
<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">__construct</span><span class="punctuation token">(</span>Connection <span class="token variable">$connection</span><span class="punctuation token">,</span> RequestStack <span class="token variable">$request_stack</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">connection</span> <span class="operator token">=</span> <span class="token variable">$connection</span><span class="punctuation token">;</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span> <span class="operator token">=</span> <span class="token variable">$request_stack</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

<span class="comment token" spellcheck="true">/**
* Implements Drupal\Core\Flood\FloodInterface::register().
*/</span>
<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">register</span><span class="punctuation token">(</span><span class="token variable">$name</span><span class="punctuation token">,</span> <span class="token variable">$window</span> <span class="operator token">=</span> <span class="number token">3600</span><span class="punctuation token">,</span> <span class="token variable">$identifier</span> <span class="operator token">=</span> <span class="keyword token">NULL</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span><span class="function token">isset</span><span class="punctuation token">(</span><span class="token variable">$identifier</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="token variable">$identifier</span> <span class="operator token">=</span> <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getCurrentRequest</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getClientIp</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
  <span class="punctuation token">}</span>
  <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">connection</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">insert</span><span class="punctuation token">(</span><span class="string token">'flood'</span><span class="punctuation token">)</span>
    <span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">fields</span><span class="punctuation token">(</span><span class="keyword token">array</span><span class="punctuation token">(</span>
      <span class="string token">'event'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="token variable">$name</span><span class="punctuation token">,</span>
      <span class="string token">'identifier'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="token variable">$identifier</span><span class="punctuation token">,</span>
      <span class="string token">'timestamp'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="constant token">REQUEST_TIME</span><span class="punctuation token">,</span>
      <span class="string token">'expiration'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="constant token">REQUEST_TIME</span> <span class="operator token">+</span> <span class="token variable">$window</span><span class="punctuation token">,</span>
    <span class="punctuation token">)</span><span class="punctuation token">)</span>
    <span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">execute</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>

Dans cet exemple la méthode register() du Service flood sauve en base de données l’événement réalisé par un utilisateur (l’utilisateur étant identifié par son IP).

On peut voir que pour sauver l’action de l’utilisateur en base de données nous avons besoin d’utiliser la connexion à la base de données et de récupérer l’IP de l’utilisateur.

Toujours dans l’esprit de garder du code facilement interchangeable, nous ne voulons pas écrire au sein d’une méthode du code qui récupérerait la connexion à la base de données directement, nous voulons que cette connexion nous soit envoyée lorsque nous instancions notre objet.
De cette façon, si la connexion se fait sur une base MySQL ou Cassandra ou est un faux objet retournant des valeurs en dur pour les tests, cela ne fait aucune différence pour nous (et il en va de même pour l’IP de l’utilisateur).

Dans le constructeur de la classe, nous récupérons la connexion à la base de données et la requête de l’utilisateur. Ces deux informations étant indispensables, on peut donc dire qu’il y a deux dépendances sur le Service flood, et ces dépendances sont injectées via le constructeur.

Lors de la définition du Service, on indique les dépendances via une arobase @ suivie du nom de la dépendance dans les arguments. Dans notre cas la base de données (@database) et la requête qui vient d’être effectuée (@request_stack).

<span class="comment shell-comment token"># core.services.yml</span>
flood<span class="punctuation token">:</span>
<span class="keyword token">class</span><span class="punctuation token">:</span> Drupal\<span class="package token">Core<span class="punctuation token">\</span>Flood<span class="punctuation token">\</span>DatabaseBackend</span>
arguments<span class="punctuation token">:</span> <span class="punctuation token">[</span><span class="string token">'@database'</span><span class="punctuation token">,</span> <span class="string token">'@request_stack'</span><span class="punctuation token">]</span>
tags<span class="punctuation token">:</span>
   <span class="operator token">-</span> <span class="punctuation token">{</span> name<span class="punctuation token">:</span> backend_overridable <span class="punctuation token">}</span>

L’Injection de dépendance se fait dans le constructeur de la classe Flood\DatabaseBackend, de cette façon :

<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">__construct</span><span class="punctuation token">(</span>Connection <span class="token variable">$connection</span><span class="punctuation token">,</span> RequestStack <span class="token variable">$request_stack</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">connection</span> <span class="operator token">=</span> <span class="token variable">$connection</span><span class="punctuation token">;</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span> <span class="operator token">=</span> <span class="token variable">$request_stack</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

Les deux arguments du constructeur indiquent que notre classe a besoin de ces deux objets sous peine de ne pas pouvoir fonctionner. On garde la référence à nos deux arguments en les stockant comme attributs de la classe, ce qui permet de les utiliser par la suite au sein de nos méthodes.

<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">register</span><span class="punctuation token">(</span><span class="token variable">$name</span><span class="punctuation token">,</span> <span class="token variable">$window</span> <span class="operator token">=</span> <span class="number token">3600</span><span class="punctuation token">,</span> <span class="token variable">$identifier</span> <span class="operator token">=</span> <span class="keyword token">NULL</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span><span class="function token">isset</span><span class="punctuation token">(</span><span class="token variable">$identifier</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="comment token" spellcheck="true">// Récupération de l'adresse IP du client depuis l'objet "requestStack".</span>
  <span class="token variable">$identifier</span> <span class="operator token">=</span> <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getCurrentRequest</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getClientIp</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
  <span class="punctuation token">}</span>
  <span class="comment token" spellcheck="true">// Utilisation de l'objet "connection" pour requêter la base de données.</span>
  <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">connection</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">insert</span><span class="punctuation token">(</span><span class="string token">'flood'</span><span class="punctuation token">)</span>
    <span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">fields</span><span class="punctuation token">(</span><span class="keyword token">array</span><span class="punctuation token">(</span>
      <span class="string token">'event'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="token variable">$name</span><span class="punctuation token">,</span>
      <span class="string token">'identifier'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="token variable">$identifier</span><span class="punctuation token">,</span>
      <span class="string token">'timestamp'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="constant token">REQUEST_TIME</span><span class="punctuation token">,</span>
      <span class="string token">'expiration'</span> <span class="operator token">=</span><span class="operator token">&gt;</span> <span class="constant token">REQUEST_TIME</span> <span class="operator token">+</span> <span class="token variable">$window</span><span class="punctuation token">,</span>
    <span class="punctuation token">)</span><span class="punctuation token">)</span>
    <span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">execute</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>

Notre exemple nous permet d’illustrer la méthode la plus classique pour injecter des dépendances entre nos Services. Il s’agit d’une Injection de dépendance explicite par constructeur car notre objet ne peut pas fonctionner si l’on ne lui fourni pas ses dépendances. Les dépendances ne pourront pas non plus être modifiées durant la vie de l’objet (le constructeur n’étant appelé qu’une seule fois).

Il existe deux autres façons d’injecter les dépendances vers les objets, on qualifiera ces formes d’injection comme “implicite”.

L’injection de dépendance implicite

L’autre possibilité pour définir une dépendance est de passer les objets utilisés par ce que l’on appelle un setter. Il s’agit d’une méthode d’une classe qui définit (“to set” en anglais) la valeur d’un attribut. Elle est accompagnée de sa méthode inverse, le getter, qui permet de retourner la valeur de l’attribut.

Exemple avec la classe FormBase au sein de laquelle il est possible, entre autres, de définir / récupérer le chemin vers le formulaire.

<span class="comment shell-comment token"># FormBase.php</span>
<span class="keyword token">abstract</span> <span class="keyword token">class</span> <span class="class-name token">FormBase</span> <span class="keyword token">implements</span> <span class="class-name token">FormInterface</span><span class="punctuation token">,</span> ContainerInjectionInterface <span class="punctuation token">{</span>
  <span class="comment token" spellcheck="true">// The request stack.</span>
  <span class="keyword token">protected</span> <span class="token variable">$requestStack</span><span class="punctuation token">;</span> <span class="comment token" spellcheck="true">// \Symfony\Component\HttpFoundation\RequestStack.</span>
  <span class="comment token" spellcheck="true">// Gets the request object.</span>
  <span class="keyword token">protected</span> <span class="keyword token">function</span> <span class="function token">getRequest</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
    <span class="keyword token">if</span> <span class="punctuation token">(</span><span class="operator token">!</span><span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
      <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span> <span class="operator token">=</span> \<span class="package token">Drupal</span><span class="punctuation token">:</span><span class="punctuation token">:</span><span class="function token">service</span><span class="punctuation token">(</span><span class="string token">'request_stack'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
    <span class="punctuation token">}</span>
    <span class="keyword token">return</span> <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">getCurrentRequest</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
  <span class="punctuation token">}</span>
  <span class="comment token" spellcheck="true">// Sets the request stack object to use.</span>
  <span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">setRequestStack</span><span class="punctuation token">(</span>RequestStack <span class="token variable">$request_stack</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
    <span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span> <span class="operator token">=</span> <span class="token variable">$request_stack</span><span class="punctuation token">;</span>
    <span class="keyword token">return</span> <span class="token variable">$this</span><span class="punctuation token">;</span>
  <span class="punctuation token">}</span>
<span class="punctuation token">}</span>

Quel est l’intérêt de cette méthode comparée à l’Injection de dépendance par constructeur ?

Avec cette méthode, il devient possible d’avoir des dépendances optionnelles vers d’autres objets.
Si dans le code de votre classe il est possible de faire appel à un autre objet mais que la classe n’en a pas absolument besoin pour fonctionner, vous pouvez utilisez cette méthode pour injecter votre dépendance. Il est également possible d’utiliser cette méthode si, au cours de la vie de l’objet, la valeur de la dépendance doit changer.
Dans notre exemple, un même formulaire peut être appelé de différents endroits, on utilise donc une injection implicite pour spécifier le chemin d’où est appelé le formulaire.

Il existe une troisième méthode pour injecter des dépendances qui consiste à définir directement la valeur d’un attribut public. Nous ne détaillerons pas cette méthode car c’est une pratique peu recommandée, aucun contrôle sur les données ne pouvant être fait facilement.

L’injection de dépendances appliquée aux Services

Nous l’avons vu dans le chapitre précédent, on peut manipuler les Services via le Conteneur de Services. Dans Drupal 8, pour accéder à un Service, il va falloir passer par le Conteneur de Services.

Les choses se complexifient légèrement car, selon ce que vous implémentez, il ne sera pas possible d’accéder au Conteneur de Services de la même façon.

Cas 1 : Je développe une classe de Service

C’est le cas le plus simple qui est celui que nous avons vu précédemment avec le Service flood, vous implémentez un Service qui a des dépendances obligatoires sur d’autres Services.
Dans ce cas là, pas besoin de manipuler le conteneur directement, Drupal se charge de l’instanciation des objets pour vous, il vous suffit de déclarer le
Service et de stocker les dépendances passées au constructeur.

Déclaration du Service :

<span class="comment shell-comment token"># core.services.yml</span>
flood<span class="punctuation token">:</span>
<span class="keyword token">class</span><span class="punctuation token">:</span> Drupal\<span class="package token">Core<span class="punctuation token">\</span>Flood<span class="punctuation token">\</span>DatabaseBackend</span>
arguments<span class="punctuation token">:</span> <span class="punctuation token">[</span><span class="string token">'@database'</span><span class="punctuation token">,</span> <span class="string token">'@request_stack'</span><span class="punctuation token">]</span>
tags<span class="punctuation token">:</span>
   <span class="operator token">-</span> <span class="punctuation token">{</span> name<span class="punctuation token">:</span> backend_overridable <span class="punctuation token">}</span>

Stockage des dépendances envoyées au constructeur.

<span class="keyword token">public</span> <span class="keyword token">function</span> <span class="function token">__construct</span><span class="punctuation token">(</span>Connection <span class="token variable">$connection</span><span class="punctuation token">,</span> RequestStack <span class="token variable">$request_stack</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">connection</span> <span class="operator token">=</span> <span class="token variable">$connection</span><span class="punctuation token">;</span>
<span class="token variable">$this</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="property token">requestStack</span> <span class="operator token">=</span> <span class="token variable">$request_stack</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

L’utilisation du Conteneur de Services vous est transparente.

Cas 2 : J’ai besoin de passer un Service au constructeur d’une Factory

Autre cas, vous implémentez des plugins, étendez des contrôleurs d’entités ou toute autre classe faisant appel à une Factory nécessitant le Conteneur de Services.
Dans ce cas là, vous aurez à respecter le contrat des interfaces des
Factories qui implémentent l’une des méthodes create() ou createInstance().
Dans la signature de ces méthodes, vous retrouverez la présence d’un argument
$container de type \Symfony\Component\DependencyInjection\ContainerInterface.
Cet argument vous permettra alors de récupérer les
Services à transmettre au constructeur de la classe. (Nous verrons comment savoir quelle Factory appeler dans l’implémentation d’un Service de récupération des couvertures.)

Exemple de l’utilisation du Conteneur de Services.

<span class="comment shell-comment token"># CommentStorage.php</span>
<span class="keyword token">class</span> <span class="class-name token">CommentStorage</span> <span class="keyword token">extends</span> <span class="class-name token">SqlContentEntityStorage</span> <span class="keyword token">implements</span> <span class="class-name token">CommentStorageInterface</span> <span class="punctuation token">{</span>
<span class="keyword token">public</span> <span class="keyword token">static</span> <span class="keyword token">function</span> <span class="function token">createInstance</span><span class="punctuation token">(</span>ContainerInterface <span class="token variable">$container</span><span class="punctuation token">,</span> EntityTypeInterface <span class="token variable">$entity_info</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">return</span> <span class="keyword token">new</span> <span class="class-name token">static</span><span class="punctuation token">(</span>
   <span class="token variable">$entity_info</span><span class="punctuation token">,</span>
   <span class="token variable">$container</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'database'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
   <span class="token variable">$container</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'entity.manager'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
   <span class="token variable">$container</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'current_user'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
   <span class="token variable">$container</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'cache.entity'</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
   <span class="token variable">$container</span><span class="operator token">-</span><span class="operator token">&gt;</span><span class="function token">get</span><span class="punctuation token">(</span><span class="string token">'language_manager'</span><span class="punctuation token">)</span>
<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>

Cas 3 : Le Conteneur de Services ne m’est pas directement transmis

Il se peut que vous vous retrouviez à implémenter une classe qui n’est ni un Service ni l’implémentation d’un Plugin, contrôleur d’Entité, etc. Dans ce cas là vous n’avez aucune méthode appelée par le système à laquelle est transmis le Conteneur de Services. Dans cette situation, la seule façon d’accéder à un Service est de passer par la méthode statique service() de la classe Drupal.

Exemple d’utilisation :

<span class="comment shell-comment token"># MyController.php</span>

<span class="comment token" spellcheck="true">// Récupérer le service tour depuis mon contrôleur.</span>
<span class="token variable">$tour_service</span> <span class="operator token">=</span> \<span class="package token">Drupal</span><span class="punctuation token">:</span><span class="punctuation token">:</span><span class="function token">service</span><span class="punctuation token">(</span><span class="string token">'tour'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>

C’est la solution à utiliser en dernier recours et qu’il faut tenter d’éviter aux maximum pour garder votre application découplée et donc facilement testable, refactorable.

Pages