Planète Kgaut

Par Kgaut
Adhérent
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
Adhérent
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
Adhérent
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
Adhérent
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
Adhérent
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.

 

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Migrate - exemples de migrations "réelles"

Ce site à été construit à l'origine sous Drupal 6, migré sous drupal 7 en 2012, Je suis en train de préparer la migration de ce site vers Drupal 8 via la Migrate API.

Vu que je galère pas mal à trouver des exemples à jours, complets et qui fonctionne, je me suis dis que ce serait intéressant de partager mon expérience, le plus simple étant de partager directement mon module de migration, je l'ai donc mis directement sur github afin que vous puissiez piocher directement dedans pour voir comment je migre l'intégralité du contenu de ce site vers une installation de Drupal 8.

J'ai tenté de faire un readme assez complet, mais les recettes YAML ne sont pas encore super bien documentées, c'est à faire... Pour l'ensemble, c'est du glané à droite à gauche, sur internet mais aussi dans les exemples du module migrate_plus.

En vrac ce que vous pourrez trouver :

  • Explications de base sur la mise en oeuvre de migrate
  • Migration de termes de taxonomie (les vocabulaires étant été recréés préalablement)
  • Migration de noeuds (pareil, les types de contenus et les champs sont recréés manuellement)
  • Migration de « managed files » (fichiers liés à des noeuds (comme l'image d'introduction de ce post)
  • Migration et traitement (process) de zone  « wysiwyg » avec fichiers intégrés : j'en ai profité pour « ranger » l'ensemble des fichiers uploadés dans ckeditor.
  • Migration d'alias d'url (module path)
  • Migration de redirections (module redirect)
  • Migration de flag (les noeuds de ce blog ont des "likes" qui sont des flags.
  • Migration de commentaires
  • Migration d'un champ "images" multiple, avec le plugin iterator

Sera fait dans les prochains jours :

  • Documentation des fichiers YAML, Sources et Process.
  • Migration de paragraphs, si j'ai la foi.

Comme je l'ai dis, je me suis beaucoup inspiré du sous-module migrate_example de migrate_plus, c'est pourquoi il reste encore pas mal de commentaire en anglais faisant référence à des types de contenu « beer », qu'il faut que j'adapte.

Remarque : Je ne suis pas un expert de migrate, l'ensemble est probablement améliorable, mais comme disait un sage : « le mieux est l'ennemi du bien », je cherche avant tout à avoir quelque chose qui fonctionne. Ceci étant dit, n'hésitez-pas à me faire part de vos remarques, commentaires ou questions via les commentaires ou bien les issues sur Github ! Je suis preneur de toute idée d'amélioration.

Le tout est disponible sur Github : https://github.com/kgaut/kgaut_migrate.

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Entité - Champ de base « Date »

    $fields['date_naissance']  = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Date de naissance'))
      ->setRequired(TRUE)
      ->setDefaultValue(NULL)
      ->setSetting('datetime_type', 'date')
      ->setDisplayOptions('form', array(
        'type' => 'date',
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

 

Pour un champ DateTime : Drupal 8 - Entité - Champ de base datetime

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Ajouter des propriétés à un type d'entité existant

Drupal vient avec des types d'entité prédéfinis (Node, ou User par exemple). À ces type d'entité il est possible d'ajouter des fields, mais il est aussi possible d'ajouter des propriétés.

Voici comment ajouter des propriétés (prénom et nom) aux utilisateurs.

Dans le fichier monmodule.module :

function monmodule_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'user') {
    $fields = [];

    $fields['firstname'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('Firstname'))
      ->setSettings(array(
        'max_length' => 100,
        'text_processing' => 0,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['lastname'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('Lastname'))
      ->setSettings(array(
        'max_length' => 100,
        'text_processing' => 0,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    return $fields;
  }
}

Dans le fichier monmodule.install :

/**
 * Create user additional properties
 */
function monmodule_update_8001() {
  $entity_manager = \Drupal::entityManager();
  $definition = $entity_manager->getFieldStorageDefinitions('user')['firstname'];
  $entity_manager->onFieldStorageDefinitionCreate($definition);
  $definition = $entity_manager->getFieldStorageDefinitions('user')['lastname'];
  $entity_manager->onFieldStorageDefinitionCreate($definition);
}

function monmodule_uninstall() {
  $entity_manager = \Drupal::entityManager();
  $definition = $entity_manager->getLastInstalledFieldStorageDefinitions('user')['firstname'];
  $entity_manager->onFieldStorageDefinitionDelete($definition);
  $definition = $entity_manager->getLastInstalledFieldStorageDefinitions('user')['lastname'];
  $entity_manager->onFieldStorageDefinitionDelete($definition);
}

 

Par Kgaut
Adhérent
Kevin Gautreau

Docker image pour SolR avec plusieurs cores

Pour un projet en cours sur Drupal 8, j'ai besoin d'un moteur de recherche avec plusieurs indexes. J'utilise évidement le module search_api avec un moteur de recherche SolR, Vu que je suis en mode "développement" je vais utiliser une image docker qui me fournira une installation fonctionnelle de SolR, directement plugable avec Drupal.

Par habitude j'utilise les images de Thiebaud Schmittlin : https://github.com/TehesFR/docker-solr

Une fois docker installé l'image se lance avec la commande suivante :

docker run -p 8080:8983 tehes/docker-solr:4.10

Cela lancera une image SolR 4.10 sur le port 8080, si on souhaite utiliser une autre version du moteur il suffit d'adapter le tag :

docker run -p 8080:8983 tehes/docker-solr:6.6

Et si besoin de plusieurs indexes ?

Par contre dans mon cas, j'avais besoin de plusieurs cores pour mes différents indexes, j'ai donc adapté son image pour en créer une nouvelle : https://github.com/kgaut/docker-solr-multicore

C'est du « quick'n'Dirty » Je me suis basé uniquement sur la version 6.6 de SolR, la seule modification par rapport au DockerFile original est l'ajout d'une variable NB_CORES qui est par défaut à 4. L'image créé donc 4 core permettant d'avoir 4 indexes.

J'ai mis mon image sur Docker hub : https://hub.docker.com/r/kgaut/docker-solr-multicore/

Pour lancer l'image :

docker run -p 8080:8983 kgaut/docker-solr-multicore

4 cores et pourquoi pas 8 ?

Si jamais vous avez besoin de changer le nombre de cores, vous pouvez modifier cette image de la façon suivante :

git clone git@github.com:kgaut/docker-solr-multicore.git my-docker-solr-multicore
cd my-docker-solr-multicore

Modifiez le fichier DockerFile et à  la ligne 11 remplacez "env NB_CONTAINER 4" par "env NB_CONTAINER 8"

Construisez votre image (cette étape peut prendre un peu de temps) :

docker build -t my-docker-solr-multicore .

une fois terminée, lancez l'image :

docker run -p 8080:8983 my-docker-solr-multicore

et bim ! 8 cores :

Améliorations

Je suis loin d'être un expert docker, et si vous connaissez un moyen de passer le nombre de core voulu lors du lancement de l'image, je suis preneur. Même si vu que les cores sont créés au moment du build de l'image, je ne suis pas bien sur que cela soit possible...

Si vous avez d'autres idées d'améliorations, n'hésitez-pas, merci !

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Créer un élément de menu vers une vue

Dans le fichier monmodule.links.menu.yml :

monmodule.menu.cle:
  title: 'Titre de ma mage'
  description: 'Liste des options'
  route_name: view.admin_options.page
  parent: system.admin_content
  weight: 90

Quelques explications :

  • monmodule.menu.cle est la clée du menu item, cette clée doit être unique
  • title : Le label affiché pour le menu
  • description : Sera affichée au rollover
  • route_name : le nom de la route, sous la forme view.NOM_VUE.NOM_AFFICHAGE
  • parent : Le parent (niveau supérieur) de notre élément de menu, ici notre menu item sera présent sous "Contenu" dans le menu d'administration
  • weight : le poids de l'élément de menu, pour gérer l'odre.

Petite astuce pour le nom de la route, rendez-vous sur la page d'édition de votre vue vous retrouverez les élement dans l'url (voici un exemple pour mon cas : http://monsite.dev/admin/structure/views/view/admin_options/edit/page

Si vous souhaitez ajouter le menu item au premier "niveau" d'un menu (donc sans parent), au lieu de "parent", il faut utiliser la clé "menu_name" qui doit référencer le nom machine du menu, voici la structure à adopter :

monmodule.menu.cle:
  title: 'Titre de ma mage'
  description: 'Liste des options'
  route_name: view.admin_options.page
  manu_name: "main"
  weight: 90

Pages