Message d'avertissement

The subscription service is currently unavailable. Please try again later.

Articles de l'utilisateur

Par kgaut
Kevin Gautreau

Drupal 8 - Afficher un webform où l'on veut via le code

Pour récupérer et retourner un webform où l'on veut via le code (que ce soit dans un bloc, un controller...) on peut utiliser les lignes suivantes :

  1. // Ici, « contact » est le nom machine de mon webform
  2. $webform = \Drupal::entityTypeManager()->getStorage('webform')->load('contact');
  3. return $webform->getSubmissionForm();

 

Par kgaut
Kevin Gautreau

Drupal 8 - Domain Access - récupérer le domaine courant

Voici comment récupérer le domaine actif quand on utilise le module Domain Access pour Drupal 8 :

  1. $current_domain = \Drupal::service('domain.negotiator')->getActiveDomain();

Pour récupérer le domaine par défaut :

  1. $default_domain = \Drupal::service('domain.negotiator')->loadDefaultDomain();

 

Par kgaut
Kevin Gautreau

Drupal 8 - Entité - Champ de base « link » (lien / URL)

Pour créer un champ de base de type lien, dans la définition de votre entité :

  1. $fields['mon_lien'] = BaseFieldDefinition::create('link')
  2. ->setLabel(t('Path'))
  3. // valeurs possible : LinkItemInterface::LINK_EXTERNAL ou LinkItemInterface::LINK_GENERIC
  4. ->setSetting('link_type', LinkItemInterface::LINK_GENERIC)
  5. //Activer ou non le titre
  6. ->setSetting('title', DRUPAL_DISABLED)
  7. ->setDisplayOptions('form', [
  8. 'type' => 'link_default',
  9. 'weight' => 0,
  10. ])
  11. ->setDisplayConfigurable('form', TRUE);

 

Par kgaut
Kevin Gautreau

Drupal 8 & Search API - effectuer une requête dans le code

Avec Drupal et Drupal 8 encore plus la recherche passe la plupart du temps par Search API, une interface qui se branche devant plusieurs moteur de base de données (SolR, ElasticSearch, Database...)

Le plus souvent on va faire nos pages de résultats de recherche à l'aide de Views (et du module Search views pour Drupal 7). Mais dans certains cas on va vouloir avoir plus la main sur la recherche et donc aller interroger Search API directement dans le code.

Initialisation de la requête

Chargement de l'index (ici, le nom machine est « contenu »)

  1. // Chargement de l'index « contenu »
  2. $query = Index::load('contenu')->query();

Définition de la requête recherchée

Terme sur lesquels vont être effectués la requête.

  1. // On lancer la recherche sur « hello world »
  2. $query->keys("hello world");

Mode de la requête

  1. // Les différentes possibilités sont
  2. // - « direct » => Requête directe
  3. // - « terms » => Multiple words
  4. // - « phrase » => Single phrase
  5. $parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')->createInstance('direct');
  6.  
  7. // Optionnellement, on peut choisir un opérateur spécifique (OR ou AND)
  8. $parse_mode->setConjunction('OR');
  9.  
  10. // Affectation du mode de la requête
  11. $query->setParseMode($parse_mode);

Définitions des champs sur lesquels rechercher

Évidement c'est optionnel, par défaut la recherche se fera sur l'ensemble des champs « fulltext » contenus dans l'index.

  1. // Recherche uniquement sur le champ « body »
  2. $query->setFulltextFields(['body']);
  3.  
  4. // Recherche uniquement sur les champs « body » et « title »
  5. $query->setFulltextFields(['body', 'title']);

Ajout de conditions supplémentaires

L'objet retourné par Index::load('contenu')->query(); est une query classique drupal, sur laquelle on peut effectuer les traitement classique que l'on peut faire sur n'importe quelle Query drupal8.

À noter ici que le nom des champs doit être celui que l'on renseigne dans l'index, ils peuvent être différents des noms des champs définis dans nos types de contenu.

Quelques exemples en vrac :

Condition sur un champ boolean

  1. // le champ private doit être « TRUE »
  2. $query->addCondition('private', TRUE);

Condition sur un champ « varchar »

  1. // on veut que le contenu retourné soit un article ou un snippet
  2. $query->addCondition('type', ['article', 'snippet'], 'IN');

Condition sur un champ date

À noter le format de date à utiliser : "Y-m-d\TH:i:s\Z"

  1. $btf = \DateTime::createFromFormat('d/m/Y', '21/10/2015');
  2. $date_formatted = $btf->format("Y-m-d\TH:i:s\Z")
  3.  
  4. // Date de création de l'article > date définie
  5. $query->addCondition('created', $date_formatted, '>');
  6.  
  7. // Et l'inverse
  8. $query->addCondition('created', $date_to, ');

Pagination

Gestion de la pagination identique à une requête classique

  1. // Récupération des 20 premiers résultats.
  2. $query->range(0, 20);

Exécution de la requête et récupération des résultats

  1. $results_set = $query->execute();
  2.  
  3. //Nombre de résultats retournés
  4. $nb_results = $results_set->getResultCount()
  5.  
  6. // Récupération des entités
  7. foreach ($results_set->getResultItems() as $item) {
  8. $resultat = $item->getOriginalObject()->getValue();
  9. }

Sources

Pas trouvé grand chose sur le sujet à part cette page dans la documentation sur drupal.org : https://www.drupal.org/docs/8/modules/search-api/developer-documentatio…

Si vous avez d'autres liens pouvant aider, je suis preneur.

Par kgaut
Kevin Gautreau

Drupal - Création d'une table dans une base secondaire

Voici comment créer une table dans une base de donnée autre que celle par défaut.

Code à mettre dans votre MODULE.install

Le principe : création d'un « HOOK_schema fake » et utilisation des HOOK_install et HOOK_uninstall pour créer / supprimer cette table en sélectionnant la bonne base.

Le code est pour drupal 8 mais il peut facilement être adapté pour drupal 7.

  1.  
  2. use \Drupal\Core\Database\Database;
  3.  
  4. function MODULE_schema_autre_db() {
  5. $schema['users'] = [
  6. 'description' => 'Members informations',
  7. 'fields' => [],
  8. 'primary key' => ['id'],
  9. ];
  10.  
  11. $schema['users']['fields']['id'] = [
  12. 'description' => 'ID',
  13. 'type' => 'serial',
  14. 'not null' => TRUE,
  15. 'unsigned' => TRUE,
  16. ];
  17.  
  18. $schema['users']['fields']['uid'] = [
  19. 'description' => 'Drupal ID',
  20. 'type' => 'int',
  21. 'not null' => TRUE,
  22. 'unsigned' => TRUE,
  23. ];
  24.  
  25. $schema['users']['fields']['mail'] = [
  26. 'type' => 'varchar',
  27. 'length' => 255,
  28. ];
  29. return $schema;
  30. }
  31.  
  32.  
  33. function MODULE_install() {
  34. Database::setActiveConnection('NOM_DB');
  35. $schema = MODULE_schema_autre_db();
  36. foreach ($schema as $name => $table) {
  37. Database::getConnection()->schema()->createTable($name, $table);
  38. }
  39. Database::setActiveConnection();
  40. }
  41.  
  42. function MODULE_uninstall() {
  43. Database::setActiveConnection('NOM_DB');
  44. $schema = MODULE_schema_autre_db();
  45. foreach ($schema as $name => $table) {
  46. Database::getConnection()->schema()->dropTable($name);
  47. }
  48. Database::setActiveConnection();
  49. }

 

 

Par kgaut
Kevin Gautreau

Drupal - PSA-2018-001 - Patch de sécurité déployé le 28/03/2018

Il vient d'être annoncé par l'équipe gérant la sécurité du CMS drupal qu'un gros correctif de sécurité pour drupal 7 et drupal 8 sera déployé mercredi 28/03/2018 entre 19h et 20h30 (heures Françaises).

On ne sait pas encore où se situe la faille. On sait seulement qu'elle semble (très) importante et qu'il est recommandé d'appliquer le patch immédiatement.

La dernière fois qu'un cas similaire s'est présenté, une fois le correctif disponible, les sites non patchés ont été très rapidement piratés. En effet : une fois que l'on a accès au patch, il est facile de trouver la faille corrigée et ainsi de l'exploiter sur des sites non protégés.

Si vous êtes développeur ou si vous gérez vos propres sites drupal, prévoyez une permanence mercredi soir pour vous occuper de ça.

Si vous avez un site drupal qui est géré par un tiers, assurez-vous qu'il soit au courant et qu'il s'en occupera au plus tôt.

Encore une fois, les versions 7 et 8 de drupal sont concernées.

Plus d'informations : https://www.drupal.org/psa-2018-001

 

 

Par kgaut
Kevin Gautreau

Drupal 8 - Entitée & Views - Créer une relations inverse

Prenons deux types d'entités custom : « Bière » et « Brasserie » avec une relation 1-n entre les deux dans le sens :

  • Une bière provient d'une et d'une seule brasserie
  • Une brasserie peut proposer N bières

 Ainsi :

drupal-views-relations.png

En drupalisme, on aurait une propriété « entity_reference » au niveau de notre bière qui fera référence à la brasserie.

Dans views, si on fait un listing des bières, pas de soucis pour accéder au contenu de la brasserie depuis la bière, par contre l'inverse n'est pas possible.

Depuis un listing de brasserie, il n'est pas possible d’accéder aux bières de la brasserie.

Pour cela il faut utiliser la classe en charge de views_data, définie dans l'annotation de notre type d'entité brasserie :

drupal-entitee-annotation.png

Et voila le contenu de ce fichier

  1.  
  2. namespace Drupal\mon_module\Entity\ViewsData;
  3.  
  4. use Drupal\views\EntityViewsData;
  5.  
  6. class BrasserieViewsData extends EntityViewsData {
  7.  
  8. /**
  9.   * {@inheritdoc}
  10.   */
  11. public function getViewsData() {
  12. $data = parent::getViewsData();
  13. $data['brasserie']['bieres'] = [
  14. 'title' => t('Bieres'),
  15. 'help' => t('Lie la brasserie aux bières produites'),
  16. 'relationship' => [
  17. 'group' => t('bieres'), // Affiché en information dans la partie « relationship » de views
  18. 'label' => t('Actions de formation'), // Affiché en information dans la partie « relationship » de views
  19. 'base' => 'biere', // Table de base de l'entitée cible
  20. 'field table' => 'biere', // Table contenant le champ de l'entitée cible sur lequel on fera la jointure
  21. 'base field' => 'brasserie',// Champ de l'entité cible Champ sur lequel on fera la jointure
  22. 'relationship field' => 'brasserie_id', // Champ de l'entité source sur lequel on fera la jointure
  23. 'id' => 'standard',
  24. ],
  25. ];
  26. return $data;
  27. }
  28. }

Et voila le travail !

views-relationship.png

Par kgaut
Kevin Gautreau

Drupal 8 - Menu - Ajouter un élément de menu avec des paramètres GET

Voici comment créer un Menu item (élément de menu) avec des paramètres GET.

Pour cela on utilise le fichier MON_MODULE.links.menu.yml (à noter que cela marchera aussi dans les fichiers MON_MODULE.links.action.yml et MON_MODULE.links.task.yml)

  1. formations.element:
  2.   title: 'Mon titre de menu'
  3.   weight : 2
  4.   route_name: view.front_formations.page
  5.   menu_name: menu-formation
  6.   options:
  7.   query:
  8.   label: 'prise-de-vue'
  9.   cat: 2

Ici l'url aura comme « query string » : ?label=prise-de-vue&cat=2

Pour rappel la clé menu_name attend le nom du menu dans lequel on veut placer l'élément de menu que l'on vient de définir.

Par kgaut
Kevin Gautreau

Drupal 8 - Entité - Champ de base « Nombre Décimal »

Le type nombre décimal peut-être pratique pour stocker tout nombre à virgule (un prix par exemple).

Voici comment attacher une propriété « nombre décimal » à un type d'entité personnalisé.

  1. $fields['prix'] = BaseFieldDefinition::create('decimal')
  2. ->setLabel(t('Prix'))
  3. ->setSetting('unsigned', TRUE)
  4. ->setSetting('scale', 2)
  5. ->setSetting('min', 0)
  6. ->setSetting('suffix', '€ TTC')
  7. ->setRequired(TRUE)
  8. ->setDisplayOptions('form', array(
  9. 'type' => 'number',
  10. 'weight' => 5,
  11. ))
  12. ->setDisplayConfigurable('form', TRUE)
  13. ->setDisplayConfigurable('view', TRUE);

 

Par kgaut
Kevin Gautreau

Drupal 8 - Générer un lien de « flagging » dans le code

Voici comment générer un lien pour « flaguer » une entité avec drupal 8 et le module flag :

  1. $type_entite_a_flaguer = 'user';
  2. $id_entitee_a_flaguer = $user->id();
  3. $id_du_flag = 'follow_user';
  4.  
  5. $f = \Drupal::service('flag.link_builder');
  6. $link = $f->build($type_entite_a_flaguer, $id_entitee_a_flaguer, $id_du_flag);

vous pourrez alors utiliser le lien dans un template en faisant par exemple :

  1. {{- link -}}

 

Par kgaut
Kevin Gautreau

Drupal 8 - Afficher un webform où l'on veut via le code

Pour récupérer et retourner un webform où l'on veut via le code (que ce soit dans un bloc, un controller...) on peut utiliser les lignes suivantes :

  1. // Ici, « contact » est le nom machine de mon webform
  2. $webform = \Drupal::entityTypeManager()->getStorage('webform')->load('contact');
  3. return $webform->getSubmissionForm();

 

Par kgaut
Kevin Gautreau

Drupal 8 - Domain Access - récupérer le domaine courant

Voici comment récupérer le domaine actif quand on utilise le module Domain Access pour Drupal 8 :

  1. $current_domain = \Drupal::service('domain.negotiator')->getActiveDomain();

Pour récupérer le domaine par défaut :

  1. $default_domain = \Drupal::service('domain.negotiator')->loadDefaultDomain();

 

Par kgaut
Kevin Gautreau

Drupal 8 - Entité - Champ de base « link » (lien / URL)

Pour créer un champ de base de type lien, dans la définition de votre entité :

  1. $fields['mon_lien'] = BaseFieldDefinition::create('link')
  2. ->setLabel(t('Path'))
  3. // valeurs possible : LinkItemInterface::LINK_EXTERNAL ou LinkItemInterface::LINK_GENERIC
  4. ->setSetting('link_type', LinkItemInterface::LINK_GENERIC)
  5. //Activer ou non le titre
  6. ->setSetting('title', DRUPAL_DISABLED)
  7. ->setDisplayOptions('form', [
  8. 'type' => 'link_default',
  9. 'weight' => 0,
  10. ])
  11. ->setDisplayConfigurable('form', TRUE);

 

Par kgaut
Kevin Gautreau

Drupal 8 & Search API - effectuer une requête dans le code

Avec Drupal et Drupal 8 encore plus la recherche passe la plupart du temps par Search API, une interface qui se branche devant plusieurs moteur de base de données (SolR, ElasticSearch, Database...)

Le plus souvent on va faire nos pages de résultats de recherche à l'aide de Views (et du module Search views pour Drupal 7). Mais dans certains cas on va vouloir avoir plus la main sur la recherche et donc aller interroger Search API directement dans le code.

Initialisation de la requête

Chargement de l'index (ici, le nom machine est « contenu »)

  1. // Chargement de l'index « contenu »
  2. $query = Index::load('contenu')->query();

Définition de la requête recherchée

Terme sur lesquels vont être effectués la requête.

  1. // On lancer la recherche sur « hello world »
  2. $query->keys("hello world");

Mode de la requête

  1. // Les différentes possibilités sont
  2. // - « direct » => Requête directe
  3. // - « terms » => Multiple words
  4. // - « phrase » => Single phrase
  5. $parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')->createInstance('direct');
  6.  
  7. // Optionnellement, on peut choisir un opérateur spécifique (OR ou AND)
  8. $parse_mode->setConjunction('OR');
  9.  
  10. // Affectation du mode de la requête
  11. $query->setParseMode($parse_mode);

Définitions des champs sur lesquels rechercher

Évidement c'est optionnel, par défaut la recherche se fera sur l'ensemble des champs « fulltext » contenus dans l'index.

  1. // Recherche uniquement sur le champ « body »
  2. $query->setFulltextFields(['body']);
  3.  
  4. // Recherche uniquement sur les champs « body » et « title »
  5. $query->setFulltextFields(['body', 'title']);

Ajout de conditions supplémentaires

L'objet retourné par Index::load('contenu')->query(); est une query classique drupal, sur laquelle on peut effectuer les traitement classique que l'on peut faire sur n'importe quelle Query drupal8.

À noter ici que le nom des champs doit être celui que l'on renseigne dans l'index, ils peuvent être différents des noms des champs définis dans nos types de contenu.

Quelques exemples en vrac :

Condition sur un champ boolean

  1. // le champ private doit être « TRUE »
  2. $query->addCondition('private', TRUE);

Condition sur un champ « varchar »

  1. // on veut que le contenu retourné soit un article ou un snippet
  2. $query->addCondition('type', ['article', 'snippet'], 'IN');

Condition sur un champ date

À noter le format de date à utiliser : "Y-m-d\TH:i:s\Z"

  1. $btf = \DateTime::createFromFormat('d/m/Y', '21/10/2015');
  2. $date_formatted = $btf->format("Y-m-d\TH:i:s\Z")
  3.  
  4. // Date de création de l'article > date définie
  5. $query->addCondition('created', $date_formatted, '>');
  6.  
  7. // Et l'inverse
  8. $query->addCondition('created', $date_to, ');

Pagination

Gestion de la pagination identique à une requête classique

  1. // Récupération des 20 premiers résultats.
  2. $query->range(0, 20);

Exécution de la requête et récupération des résultats

  1. $results_set = $query->execute();
  2.  
  3. //Nombre de résultats retournés
  4. $nb_results = $results_set->getResultCount()
  5.  
  6. // Récupération des entités
  7. foreach ($results_set->getResultItems() as $item) {
  8. $resultat = $item->getOriginalObject()->getValue();
  9. }

Sources

Pas trouvé grand chose sur le sujet à part cette page dans la documentation sur drupal.org : https://www.drupal.org/docs/8/modules/search-api/developer-documentatio…

Si vous avez d'autres liens pouvant aider, je suis preneur.

Par kgaut
Kevin Gautreau

Drupal - Création d'une table dans une base secondaire

Voici comment créer une table dans une base de donnée autre que celle par défaut.

Code à mettre dans votre MODULE.install

Le principe : création d'un « HOOK_schema fake » et utilisation des HOOK_install et HOOK_uninstall pour créer / supprimer cette table en sélectionnant la bonne base.

Le code est pour drupal 8 mais il peut facilement être adapté pour drupal 7.

  1.  
  2. use \Drupal\Core\Database\Database;
  3.  
  4. function MODULE_schema_autre_db() {
  5. $schema['users'] = [
  6. 'description' => 'Members informations',
  7. 'fields' => [],
  8. 'primary key' => ['id'],
  9. ];
  10.  
  11. $schema['users']['fields']['id'] = [
  12. 'description' => 'ID',
  13. 'type' => 'serial',
  14. 'not null' => TRUE,
  15. 'unsigned' => TRUE,
  16. ];
  17.  
  18. $schema['users']['fields']['uid'] = [
  19. 'description' => 'Drupal ID',
  20. 'type' => 'int',
  21. 'not null' => TRUE,
  22. 'unsigned' => TRUE,
  23. ];
  24.  
  25. $schema['users']['fields']['mail'] = [
  26. 'type' => 'varchar',
  27. 'length' => 255,
  28. ];
  29. return $schema;
  30. }
  31.  
  32.  
  33. function MODULE_install() {
  34. Database::setActiveConnection('NOM_DB');
  35. $schema = MODULE_schema_autre_db();
  36. foreach ($schema as $name => $table) {
  37. Database::getConnection()->schema()->createTable($name, $table);
  38. }
  39. Database::setActiveConnection();
  40. }
  41.  
  42. function MODULE_uninstall() {
  43. Database::setActiveConnection('NOM_DB');
  44. $schema = MODULE_schema_autre_db();
  45. foreach ($schema as $name => $table) {
  46. Database::getConnection()->schema()->dropTable($name);
  47. }
  48. Database::setActiveConnection();
  49. }

 

 

Par kgaut
Kevin Gautreau

Drupal 8 - Rediriger l'accès aux pages d'un type de contenu en particulier

Ça vient à contresens du paradigme des noeuds, mais parfois on souhaite que les pages de détail d'un noeud d'un type de contenu en particulier ne soient pas accessible.

Avec quelques adaptation, ceci peut aussi fonctionner pour les pages de terme de taxonomie.

Voici comment faire :

1ère étape, déclarer un services dans mon_module.services.yml

  1. services:
  2.   mon_module.node_cp_redirect:
  3.   class: Drupal\mon_module\EventSubscriber\NodeCPRedirect
  4.   tags:
  5.   - { name: event_subscriber }

2ème étape, créer l'event subscriber: src/EventSubscriber/NodeCPRedirect.php dans le dossier de mon module.

Ici je vais chercher à rediriger tous les noeuds de type « communiques_de_presse » vers la vue « view.front_communiques_de_presse.page », mais cela peut-être évidement n'importe quelle route.

  1.  
  2. namespace Drupal\mon_module\EventSubscriber;
  3.  
  4. use Drupal\Core\Url;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. use Symfony\Component\HttpFoundation\RedirectResponse;
  7. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  8. use Symfony\Component\HttpKernel\KernelEvents;
  9.  
  10. class NodeCPRedirect implements EventSubscriberInterface {
  11.  
  12. public static function getSubscribedEvents() {
  13. return [KernelEvents::REQUEST => [['redirectionCommuniquesPresse']]];
  14. }
  15.  
  16. /**
  17.   * Redirection des contenus de type communique_de_presse vers la vue de listing.
  18.   */
  19. public function redirectionCommuniquesPresse(GetResponseEvent $event) {
  20. $request = $event->getRequest();
  21.  
  22. if ($request->attributes->get('_route') !== 'entity.node.canonical') {
  23. return;
  24. }
  25. if ($request->attributes->get('node')->getType() !== 'communique_de_presse') {
  26. return;
  27. }
  28. $response = new RedirectResponse(Url::fromRoute('view.front_communiques_de_presse.page')->toString(), 301);
  29. $event->setResponse($response);
  30. }
  31.  
  32. }

 

Par kgaut
Kevin Gautreau

Drupal 8 - PHPMyAdmin - corriger l'erreur « Syntax error near '`value`»

Parfois on a pas le choix et l'on est obligé de passer par PHPMyAdmin pour récupérer un export de base de données.

Avec un dump de Drupal 8, lors de l'import, on peut tomber sur l'erreur suivante :

Requête SQL :

CREATE TABLE IF NOT EXISTS `key_value` ( `collection` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'A named collection of key and value pairs.' `value` longblob NOT NULL COMMENT 'The value.', PRIMARY KEY (`collection`,`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Generic key-value storage table. See the state system for'

MySQL a répondu: Documentation

#1064 - Erreur de syntaxe près de '`value` longblob NOT NULL COMMENT 'The value.',
  PRIMARY KEY (`collection`,`nam' à la ligne 3

C'est visiblement du à un bug dans une certaine version de PHPMyAdmin.

Il est possible de corriger cette erreur en modifiant manuellement le dump en deux étapes.

La première, remplacez le bloc suivant :

  1. CREATE TABLE IF NOT EXISTS `key_value` (
  2. `collection` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'A named collection of key and value pairs.'
  3. `value` longblob NOT NULL COMMENT 'The value.',
  4. PRIMARY KEY (`collection`,`name`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Generic key-value storage table. See the state system for';

par

  1. CREATE TABLE IF NOT EXISTS `key_value` (
  2. `collection` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'A named collection of key and value pairs.',
  3. `name` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'The key of the key-value pair. As key is a SQL reserved keyword, name was chosen instead.',
  4. `value` longblob NOT NULL COMMENT 'The value.',
  5. PRIMARY KEY (`collection`,`name`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Generic key-value storage table. See the state system for';

Seconde étape, supprimez le bloc suivant :

  1. ALTER TABLE `key_value`
  2. ADD `name` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.';

Une fois cela fait, vous pourrez alors importer le dump sans soucis.

Plus d'informations sur ce problème en anglais : https://www.drupal.org/node/2496331

Par kgaut
Kevin Gautreau

Drupal 8 - Views - Récupérer un formulaire exposé

Pour un besoin spécifique, il fallait que je récupère un formulaire exposé de views pour l'afficher ailleurs.

Il fallait en fait qu'à un endroit j'affiche les filtres exposés et à un autre le tri exposé.

J'ai donc créé un bloc, qui récupère ce formulaire, masque un champ.

Voici donc comment récupérer un formulaire exposé :

  1. $view_name = 'ma_vue';
  2. $view_display = 'page';
  3.  
  4. $view = \Drupal\views\Views::getView($view_name);
  5. $view->initHandlers();
  6. $view->setDisplay($view_display);
  7. $form_state = new \Drupal\Core\Form\FormState();
  8. $form_state->setFormState([
  9. 'view' => $view,
  10. 'display' => $view->display_handler->display,
  11. 'exposed_form_plugin' => $view->display_handler->getPlugin('exposed_form'),
  12. 'method' => 'get',
  13. 'rerender' => TRUE,
  14. 'no_redirect' => FALSE,
  15. 'always_process' => TRUE,
  16. ]);
  17.  
  18. // Ici j'ajoute une info pour connaitre le contexte si le formulaire est altéré
  19. // afin de distiguer cette instance de formulaire de la « normale ».
  20. $form_state->addBuildInfo('exposed_block', TRUE);
  21.  
  22. $form = \Drupal::formBuilder()->buildForm('Drupal\views\Form\ViewsExposedForm', $form_state);
  23.  
  24. // Je masque le champs qui ne m'intéresse pas
  25. $form['sort_by']['#access'] = FALSE;

Dans le cadre d'un HOOK_form_alter je peux récupérer l'information "exposed_block" de la façon suivante :

<span class="re0">$form_state</span><span class="sy0">-></span><span class="me1">getBuildInfo</span><span class="br0">(</span><span class="br0">)</span><span class="br0">[</span><span class="st_h">'exposed_block'</span><span class="br0">]</span>

 

 

Par kgaut
Kevin Gautreau

Drupal 8 - Views - Changer le titre d'une vue en fonction du nombre de résultats

Utile dans le cadre d'une vue affichant des résultats d'une recherche via Search API :

  1. function MONMODULE_views_post_render(\Drupal\views\ViewExecutable $view) {
  2. if($view->storage->id() === 'NOM_MACHINE_VUE' && $view->current_display === 'NOM_MACHINE_AFFICHAGE') {
  3. $view->setTitle($view->getTitle() . ' (' . count($view->result) . ')');
  4. }
  5. }

 

Par kgaut
Kevin Gautreau

Drupal - afficher en live le contenu du watchdog avec drush

Parfois on se retrouve avec un gros White Screen Of Death sur son site, et impossible de se connecter.

Dans ce cas là il est pratique de pouvoir afficher en live le contenu du watchdog, c'est possible avec Drush et la commande watchdog-show :

drush @alias watchdog-show --tail --full --count=50

--tail affichera les nouvelles entrées du watchdog en direct

--full pour afficher le détail des erreurs

--count=50 permettra d'afficher les 50 lignes les plus récentes du watchdog.

2017-10-31-16-13-09-Screenshot-01.jpg

(Deux fois dans la journée que je recherche cette commande, au moins je saurai maintenant ou la retrouver...)

Par kgaut
Kevin Gautreau

Drupal 8 - Formulaire - « ajouter un élément » et « supprimer un élément »

Voici comment dans un formulaire custom D8, avoir un ensemble de champ "multiples" dans un formulaire de config par exemple.

drupal8-formulaire-add-more.jpg

Dans mon exemple je n'ai que le champ « titre », mais c'est simplement pour alléger le snippet.

Le code en question :

config('monmodule.homepage');
    $pomoted_items = $config->get('promoted', []);
    $number_promoted = $form_state->getValue('number_promoted', count($pomoted_items));
    $removed = $form_state->getValue('removed_promoted', []);

    $form['number_promoted'] = [
      '#type' => 'value',
      '#value' => $number_promoted,
    ];
    $form['removed'] = [
      '#type' => 'value',
      '#value' => $removed,
    ];
    $form['promoted'] = [
      '#type' => 'fieldset',
      '#title' => t("À la une"),
      '#tree' => TRUE,
    ];
    for ($i = 1; $i  'fieldset',
        '#title' => t("À la une #@i", ['@i' => $i]),
      ];
      $form['promoted'][$i]['title'] = [
        '#type' => 'textfield',
        '#title' => $this->t("Titre"),
        '#default_value' => $item['title'],
      ];
      $form['promoted'][$i]['remove_' . $i] = array(
        '#type' => 'submit',
        '#value' => t("Supprimer l'élément #@i", ['@i' => $i]),
        '#submit' => array('::removeItem'),
        '#attributes' => [
          'class' => ['button--danger'],
          'data-toRemove' => $i,
        ]
      );
    }
    $form['promoted']['add_item'] = array(
      '#type' => 'submit',
      '#value' => t('Ajouter un autre élément'),
      '#submit' => array('::addPromotedItem'),
    );

    return parent::buildForm($form, $form_state);
  }

  public function addPromotedItem(array &$form, FormStateInterface $form_state) {
    $form_state->setValue('number_promoted', $form_state->getValue('number_promoted') + 1);
    $form_state->setRebuild();
  }

  public function removeItem(array &$form, FormStateInterface $form_state) {
    $removed = $form_state->getValue('removed_promoted', []);
    $removed[] = $form_state->getTriggeringElement()['#attributes']['data-toRemove'];
    $form_state->setValue('removed_promoted', $removed);
    $form_state->setRebuild();
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $parsed = [];
    $promoted = $form_state->getValue('promoted');
    foreach ($promoted as $promoted_item) {
      if(is_array($promoted_item)) {
        $parsed[] = $promoted_item;
      }
    }
    $this->config('monmodule.homepage')->set('promoted', $parsed)->save();
  }
}

 

 
Par kgaut
Kevin Gautreau

Pushover - Envoyer des notifications push depuis Drupal 8

Pour d'autres raisons j'ai utilisé le service de Pushover, qui permet d'envoyer des notifications push vers son téléphone mobile. Dans le principe, vous installer l'application sur votre téléphone / tablette (android ou apple) et via une api vous pourrez envoyer une notifications vers un, ou l'ensemble de vos devices.

J'ai aussi créé un module pour intégrer le service et ainsi pouvoir envoyer une notification push depuis son site Drupal 8. Quelle utilité ? À vous de voir, je l'ai personnellement utilisé pour recevoir une notification push en cas de nouveau commentaire ou bien d'alerte critique sur le site.

Le module est sur Github : https://github.com/kgaut/drupal-pushover, on peut l'installer via composer :

# Ajout du dépot
composer config repositories.drupal-pushover vcs https://github.com/kgaut/drupal-pushover
# Téléchargement du module
composer require drupal/pushover

Une fois installé on le configure via le formulaire dans Configuration / Services Web / Pushover Configuration

pushover-drupal-configuration.jpg

Le code pour déclencher une notification est le suivant :

\Drupal::service('pushover.sender')->sendNotification($title, $message, $url = NULL, $url_title = NULL);

Un exemple d'intégration pour envoyer une notification lors d'un nouveau commentaire :

/**
 * Implements hook_HOOK_insert().
 *
 * Send Pushover notification on new comment.
 *
 * @param \Drupal\comment\Entity\Comment $comment
 */
function kgaut_comment_insert(\Drupal\comment\Entity\Comment $comment) {
  $node = $comment->getCommentedEntity();
  $author = $comment->getAuthorName();
  $title = t('Nouveau commentaire sur Kgaut.NET');
  $description = t('@name à posté un nouveau commentaire sur « @post_title »', [
    '@name' => $author,
    '@post_title' => $node->label(),
  ]);
  $url = $node->url('canonical',['absolute' => TRUE]);
  \Drupal::service('pushover.sender')->sendNotification($title, $description, $url, t('Voir le post'));
}

Et voila le résultat :

pushover-notification-small.png

Bon évidement, on doit aussi pouvoir trouver des usages utiles :)

Ça n'est pas du drupal, mais j'ai aussi ajouté une notification quand quelqu'un se connecte en ssh sur mon serveur, je partagerai peut-être le script en question un de ces 4 !

Par kgaut
Kevin Gautreau

Drupalcamp Lannion : le programme est annoncé !

La sélection des sujets est terminée, le programme des deux jours de conférence du drupalcamp à Lannion des 27-28 et 29 octobre est maintenant connu !

Au programme : du drupal 8, du cache, du templating, des tests, du composer, de la config, des bonnes pratiques...

Deux salles, deux ambiances : une salle plus dédiées pour les débutants et les sujets plus larges et une salle avec sujets « barbus ».

D'autres salles seront aussi disponible pour des BOF (petites discussions improvisées ou suites informelles de sessions).

Personnellement je donnerai une présentation sur comment gérer son site drupal 8 avec composer et git le samedi matin.

Pour rappel, le 3e jour sera dédié aux ateliers et sprints.

Merci à toutes les personnes qui nous ont proposé des sujets !

Pour rappel, l'accès aux conférence est libre et gratuit, mais l'association propose des « packs weekend » comprenant les repas du midi, le café, le traditionnel sac de goodies et l’accès aux soirées communautaires, proposé à un prix plus que raisonnable (15€ pour les trois jours). Je ne peux que vous encourager à soutenir l'association et prendre votre pack : https://lannion2017.drupalcamp.bzh/reservations

Enfin, toujours pour proposer un tarif aussi accessible, il reste des packs de sponsoring disponible : https://lannion2017.drupalcamp.bzh/sponsors/forfaits

Par kgaut
Kevin Gautreau

Drupal 8 - Accèder à $_GET et $_POST

Récupérer $_GET['test'] :

<span>$</span>test<span> </span><span>=</span><span> \Drupal</span><span>::</span><span>request()->query->get</span><span>(</span><span>'</span>test<span>'</span><span>);</span>

Récupérer $_POST['test'] :

<span>$</span>test<span> </span><span>=</span><span> \Drupal</span><span>::</span><span>request</span><span>()-></span><span>request</span><span>-></span><span>get</span><span>(</span><span>'</span>test<span>'</span><span>);</span>

Récupérer un tableau associatif avec toutes les variables $_GET :

<span>$variables_get = \Drupal::request()->query-></span>all<span>(</span><span>);</span>

Récupérer un tableau associatif avec toutes les variables $_POST :

<span>$variables_post</span><span> </span><span>=</span><span> \Drupal</span><span>::request()->request-></span>all<span>(</span><span>);</span>

 

Par kgaut
Kevin Gautreau

Drupal 8 - Les EntityQuery par l'exemple

Remplacement des Entity Field Query en drupal 7, les Entity Query permettent d'effectuer des requêtes sur nos types d'entités, (custom ou non) selon leurs propriétés ou leurs champs (fields).

Elles sont une bonne solution pour faire des queries même avancées, sans avoir à faire des jointures à en perdre la tête.

Un exemple simple

Récupération et chargement de l'ensemble des utilisateurs activés.

//On commence par donner le type d'entité que l'on souhaite « requêter »
$query = \Drupal::entityQuery('user'); 
//On ajoute une condition, ici « status = 1 » pour ne récupérer que les utilisateurs actifs
$query->condition('status', 1); 
//On lance la requête et récupère les ids des utilisateurs.
$users_ids = $query->execute(); 
//on charge les utilisateurs en question
$users = User::loadMultiple($users_ids);

Que peut-on peut requêter ?

Sous drupal 8, tout est entité (ou presque), on peut ainsi faire des requêtes sur :

  • Les noeuds
  • Les blocs
  • Les utilisateurs
  • Les menus
  • Les éléments de menus
  • Nos types d'entités custom
  • ...

Bref, beaucoup de chose !

La déclaration du type d'entité se fait lors de la génération de la requête :

Pour requêter les utilisateurs
$query = \Drupal::entityQuery('user');

Pour requêter les noeuds
$query = \Drupal::entityQuery('node');

Les conditions

$query->condition(champ_ou_propriété, valeur, opérateur);

Le champ sur lequel on fait une condition peut -être soit une propriété (nid, changed, created, title, name...) ou bien un champ « field » (field_tags, field_image...)

Par défaut l'opérateur est : = (égal)

$query->condition('uid', 1);

mais on peut évidement le spécifier :

$query->condition('uid', $user->id(), '');

On peut utiliser le IN, afin de, par exemple récupérer et charger tous les users en fonction d'un field "interest" qui fait référence à des termes de taxonomies :

$interests = [127, 128, 27];
$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec un "LIKE", afin de tester le même département :

$query->condition('field_adresse_zipcode', substr($code_postal, 0, 2).'%', 'LIKE');

Conditions multiples

Aussi on peut ajouter plusieurs conditions, ici je fais un test sur la date de naissance, qu'elle soit bien comprise entre $min_date & $max_date :

$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$query->condition('field_birthday', $min_date->format('Y-m-d'), 'condition('field_birthday', $max_date->format('Y-m-d'), '>');
$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec des conditions Or

On peut utiliser des conditions logiques « OR », ici je veux récuperer les utilisateurs qui ont les mêmes centres d'intérêts (field_interests) OU qui sont manuellement mis en avant (field_pinned)

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);
$condition_or->condition('field_interests', $interests, 'IN');

$query->condition($condition_or);

$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec des conditions OR et AND

On peut faire des conditions un peu plus balaises, avec ici donc : soit les utilisateurs mis en avant (field_pinned) OU (qui ont les même centres d'intérêts (field_interest) ET qui sont nés après $max_date ET avant $min_date :

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);

$condition_and = $query->andConditionGroup();

$condition_and->condition('field_interests', $interests, 'IN');
$condition_and->condition('field_birthday', $min_date->format('Y-m-d'), 'condition('field_birthday', $max_date->format('Y-m-d'), '>');

$condition_or->condition($condition_and);

$query->condition($condition_or);

$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Gestion de la pagination et du nombre de résultats

$query->range(0, 20);

Gestion du tri

$query->sort('created');

ou en précisant le sens :

$query->sort('field_birthday', 'ASC');

 

Par kgaut
Kevin Gautreau

Drupal 8 - Définir l'item de menu actif en fonction du type de noeud

Sur ce site, j'ai trois types de contenu :

  • Article
  • Snippet
  • Réalisation

Pour chacun de ces trois types de contenu, j'ai une vue (View) de listing qui correspond (cf le menu ci-dessus).

Par défaut quand on est sur la vue de listing des snippets par exemple, on a bien l'élément de menu "Snippets" qui est marqué comme actif, par contre quand on va sur le détail d'un snippet, on perd cette information. Nous allons voir comment sélectionner le menu item qui doit être actif en fonction du type de contenu sur lequel on se trouve.

1ère étape : surcharger le service MenuActiveTrail

dans mon module « kgaut » je créé le fichier KgautMenuActiveTrail.php dans le dossier src/Menu avec le contenu suivant :

routeMatch->getParameter('node');

    if ($node instanceof NodeInterface) {
      $bundle = $node->bundle();
      switch ($bundle) {
        case 'article':
          //Ici je demande à recherche le menu item qui utilise la route correspondant à mon type de contenu
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_articles.page', [], $menu_name);
          break;

        case 'snippet':
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_snippets.page', [], $menu_name);
          break;

        case 'realisation':
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_realisations.page', [], $menu_name);
          break;
      }

      if (isset($links) && $links) {
        $found = reset($links);
      }
    }
    return $found;
  }
}

 

C'est bien beau, mais maintenant il faut signaler à Drupal que l'on a surchargé un de ses services. Pour cela il faut créer le fichier src/MonModuleServiceProvider.php (attention c'est normé !) Dans mon cas j'ai donc créé le fichier KgautServiceProvider.php dans le dossier src de mon module kgaut (oui je suis très égocentrique).

En voici le contenu :

getDefinition('menu.active_trail')->setClass('Drupal\kgaut\Menu\KgautMenuActiveTrail');
  }

}

Un petit vidage de cache plus tard, et on est bon !

Par kgaut
Kevin Gautreau

Drupal Camp Lannion 2017 - 27-28-29 Octobre

L'association Drupal France et l'équipe de bénévoles, organise comme chaque année un Drupal Camp, pour 2017, c'est en Bretagne, à Lannion du 27 au 29 octobre !

Drupal Camp, c'est quoi ? C'est l’évènement français incontournable pour tous les utilisateurs du CMS : développeurs, chefs de projets, Webmasters, Référenceurs... 2 Jours de conférences et un jours de sprints.

Les journées de vendredi et samedi sont dédiées aux conférences et discussion. La journée du dimanche est consacrée aux ateliers et sprints (contributions). Un atelier est déjà prévu : un sprint de traduction de drupal en breton. https://lannion2017.drupalcamp.bzh/brezhoneg.

L'accès aux conférence est libre et gratuit, mais l'association propose des « packs weekend » comprenant les repas du midi, le café, le traditionnel sac de goodies et l’accès aux soirées communautaires, proposé à un prix plus que raisonable (15€ pour les trois jours) je ne peux que vous encourager à soutenir l'association et prendre votre pack : https://lannion2017.drupalcamp.bzh/reservations

Afin de proposer un tarif aussi accessible, nous faisons appels à des partenaires pour nous aider financièrement dans l'organisation de cet évènement : https://lannion2017.drupalcamp.bzh/sponsors/forfaits

Enfin, comme chaque année, c'est vous qui faites le contenu des drupal camp ! N'hésitez-pas à proposer un sujet de conférence, même si vous n'êtes pas un expert Drupal, vous aurez toujours quelque chose à apprendre aux participants : https://lannion2017.drupalcamp.bzh/programme/guide-aux-conferenciers.

Espérant vous voir nombreux à Lannion fin octobre, n'hésitez-pas à me faire un petit coucou une fois là bas ! (Malheureusement, Jean-Michel, mon suricate ne devrait pas faire le déplacement...)

Par kgaut
Kevin Gautreau

Message de service : Migration du site sous Drupal 8

j'ai enfin terminé la migration de ce site vers Drupal 8 !

À l'origine un drupal 6, j'avais migré le site en 2012 vers Drupal 7, il était maintenant temps de tester la Migrate API pour tester la migration de contenu vers un site en drupal 8.

J'ai donc créé un module pour migrer l'ensemble des contenus de l'ancien site (articles, réalisations, snippets, commentaires, flag, redirections, fichiers...) Ayant eu du mal à trouver des ressources à jour et fonctionnant correctement, j'ai mis l'ensemble du module sur github, espérant que cela serve à d'autres : https://github.com/kgaut/kgaut_migrate.

Au niveau du front, comme vous le voyez, je suis resté simple (je suis développeur back après tout) j'ai créé un thème basé sur bartik, que j'ai légèrement personnalisé.

Je suis en train de faire le tour mais tout à l'air de fonctionner correctement. Si jamais vous remarquez quelque chose qui ne correspond pas à ce que vous attendez, n'hésitez-pas à me le signaler.

 

 

Pages