Url::fromUri('mailto:contact@ows.fr')
Url::fromRoute('entity.node.canonical', ['node' => 12])
Url::fromUri('https://aider.curie.fr/don/~mon-don/')
Url::fromUri('base://sites/default/files/2016-04/xxx.pdf')->toString()
file_create_url(public://2016-05/20140303_V2.pdf)
Url::fromRoute('<current>')->toString() // URL en cours https://www.truc.com/trac?bidule=troc ?
$base_url = \Drupal::request()->getHost(); // www.truc.com
$host = \Drupal::request()->getSchemeAndHttpHost(); // https://www.truc.com
\Drupal::request()->getUri() // URL en cours absolue
Url::fromUri(\Drupal::request()->getUri(), ['query' => ['display' => 'map']]) // Route en cour en ajoutant un paramètre
Url::fromRoute('<front>')->toString() // URL front
Url::fromRoute('<none>', NULL, ['fragment' => 'content']) // Pas de lien, juste une ancre
\Drupal::service('path.matcher')->isFrontPage(); // Est-ce la home ?
\Drupal::service('path.current')->getPath(); // La page actuelle ? (sans la langue)
\Drupal::routeMatch()->getRouteName(); // La route en cours ?
\Drupal::service('router.admin_context')->isAdminRoute($route); // Route admin ?
\Drupal::theme()->getActiveTheme()->getPath(); // Chemin du thème
\Drupal::routeMatch()->getParameter('node'); // Node en cours
// route:<none> dans un menu pour ne pas avoir de lien
// Redirection.
$response = new RedirectResponse(Url::fromRoute('view.recherche.recherche', $route_parameters)->toString());
$response->send();
// Construction d'une route : view.recherche.recherche : [nom_module].[id_vue].[id_display]
// A la place de l().
Link::createFromRoute($this->t('[TEXT]'), 'entity.view.edit_display_form', ['view' => $view_id, 'display_id' => $view_display_id], ['query' => ['destination' => \Drupal::service('path.current')->getPath()]])->toString()
// Avoir une URL propre à partir d'une string
$label = \Drupal::service('pathauto.alias_cleaner')->cleanString($label);
https://cryptic.zone/blog/drupal-8-cheatsheet-developers
$entity->entity; // Pour avoir l'entité enfant directement, si elle existe
$entity->field_XXX; // Pour avoir le field directement
// En fait, tout ce qui est dans values et properties est accessible directement (grâce à __get())
$entity->id(); // Son ID.
$entity->uuid(); // Son UUID.
$entity->getTitle(); // Son titre.
$entity->label(); // Le titre pour un terme plutôt.
$entity->toLink('...'); // Son lien.
$entity->toLink('...')->toRenderable(); // Pour éviter d'utiliser le thème link.
$entity->getEntityType()->id(); // Le nom machine du type de l'entité.
$entity->bundle(); // Son bundle.
$entity->isEmpty(); // Voir si le champ est rempli.
$entity->created->getString(); // La valeur d'un champ.
$entity->field_text->value; // La valeur d'un champ dans le cas d'un texte.
$entity->field_XXX->view([
'label' => 'hidden',
'type' => 'entity_reference_entity_view',
'settings' => ['view_mode' => 'cours_visuel'],
]); // Vue d'un champ.
$entity->getFileUri(); // L'URI d'un File. toLink ne fonctionne pas car cette classe n'a pas de canonical dans son annotation.
// Date.
$entity->field_date[0]->value // Date type 31/05/2017.
foreach ($entity->field_date[0] as $value) {
$value->getDateTime()->format('d m Y'); // Pour formater comme on veut.
$value->getDateTime()->getTimestamp(); // Pour avoir le timestamp.
}
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = \Drupal::entityTypeManager();
// Block, Node, Taxonomy, User...
$entities = $entity_type_manager->getStorage($entity_type)->loadMultiple($ids);
$entity = $entity_type_manager->getStorage($entity_type)->load($id);
$items = $entity_type_manager->getViewBuilder($entity_type)->viewMultiple($entities, $view_mode);
----
// Si on veut afficher un champ dans la langue courrante.
$entity_traduite = \Drupal::service('entity.repository')->getTranslationFromContext($entity);
Database::getConnection()->schema()->dropTable('id_entity');
$target_entity_type = 'media';
$labels = ['Grand carré', 'Petit carré'];
foreach ($labels as $label) {
$machine_name = strtolower($label);
$machine_name = \Drupal::transliteration()->transliterate($machine_name, 'fr');
$machine_name = preg_replace('/[^a-z0-9_]+/', '_', $machine_name);
\Drupal\Core\Entity\Entity\EntityViewMode::create(['targetEntityType' => $target_entity_type, 'id' => $target_entity_type . '.' . $machine_name, 'label' => $label])->save();
}
\Drupal\image\Entity\ImageStyle::create(['label' => 'XXX'])->save();
$field_XXX = $node->get('field_XXX')->getIterator()->current();
// Récupérer la clé sélectionnée.
$value = $node->field_XXX->target_id / value
// Récupérer la valeur sélectionnée.
$field_XXX->getPossibleOptions()[$value]
$field_XXX->getDataDefinition()->getLabel()
// Ne pas oublier de purger.
field_purge_batch(10);
// Si on veut contextualiser par la route.
// Faire une recherche dans le core pour avoir d'autres exemples de contexte.
$mon_entite->addCacheContexts(['route']);
- Toutes les fonctions commençant par entity_XXX sont dépréciées !
- Tout est entité et pour savoir qui elles sont, il suffit de regarder dans /config/sync et de conditionner sur chaque ligne dans le YAML en question. D'ailleurs, le machine name est considéré comme l'ID de l'entité (peut être utilisé dans un load() par exemple).
\Drupal::currentUser()->hasPermission('configure variables site for admin');
\Drupal::service('date.formatter')->format(TIMESTAMP, 'jour_et_mois'),
$this->field_date->date->format('d m'),
Avoir un élément '#type' => 'date'
avec '#date_date_format' => 'd/m/Y'
et le patch de Franck.
$nids = \Drupal::entityQuery('node')
->condition('type', 'publication')
->condition('status', NODE_PUBLISHED)
->condition('promote', NODE_PROMOTED)
// Choisir un chemin "long" quand c'est un champ à plusieurs valeurs genre date range : field_date.value1 ou field_date.value2
->condition('field_public', $tids, 'IN')
->condition('field_articles', $nid)
->condition('field_date_debut_fin', gmdate('Y-m-d'), '<=')
->sort('created', 'DESC')
->range(0, 10)
->pager(5)
->execute();
if (!empty($nids)) {
// Voir plus haut.
}
$query = \Drupal::entityQuery('node')
->condition('status', NodeInterface::PUBLISHED);
$actu = $query->andConditionGroup()
->condition('type', 'XXX')
->condition('created', [XXX], 'BETWEEN');
$filter = $query->orConditionGroup();
$filter->condition($actu);
$filter->condition('field_XXX', [gmdate('Y-m', $past_month), gmdate('Y-m', $future_month)], 'BETWEEN');
$query->condition($filter);
return $query->execute();
https://kgaut.net/blog/2017/drupal-8-les-entityquery-par-lexemple.html
- Pour afficher la requête, faire exprès de faire une erreur.
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); // Pour connaître le parent
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('thematique'); // Charge tous les termes d'un vocabulaire
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('categorie', 0, NULL, TRUE); // Pareil mais charge les entités
Avoir le parent d'un terme (en attente de validation de ce patch)
- Tout est bloc
- Le titre est un bloc
- On peut mettre un même bloc dans différentes régions
- Les blocs sont traduisibles
$search_block = \Drupal::entityTypeManager()
->getViewBuilder('block')
->view($block);
---------------------
$search_block = \Drupal::entityTypeManager()
->getViewBuilder('block')
->lazyBuilder('[$build['#id']]', 'full');
--------------------- best way
return [
'#type' => 'view',
'#name' => 'recherche',
'#display_id' => 'tous_les_objets',
];
// Ca évite d'avoir à réellement créer une instance du block sans vraiment vouloir la placer et niveau cache c'est un p'tit peu mieux.
- Render API overview
- core/lib/Drupal/Core/Render/Element/ : c'est là que tu as tous les types d'élements à rendre
- Ce que l'on peut mettre dans #attached (ne pas oublier de remplir xxx.libraries.yml)
- Adding stylesheets (CSS) and JavaScript (JS)
- invisible
- hidden
- visually-hidden
// foreach...
$items[] = [
'#markup' => 'TOTO',
'#wrapper_attributes' => ['class' => ['TOTO']],
];
$build['months'] = [
'#theme' => 'item_list',
'#items' => $items,
'#attributes' => [
'class' => 'actu-nav',
],
];
'left' => [
'#theme' => 'image',
'#uri' => theme_get_setting('logo')['path'],
'#uri' => drupal_get_path('theme', 'XXX') . '/images/XXX.png',
'#width' => 80,
'#height' => 80,
],
[
'#type' => 'link',
'#title' => $this->t('Ton lien'),
// '#title' => ['#markup' => [HTML]], // dans le cas où on veut mettre du HTML dans le lien
'#url' => Url::fromUri('XXX'),
],
[
'#type' => 'html_tag',
'#tag' => 'h2',
'#value' => $this->t(''),
],
\Drupal::formBuilder()->getForm('Drupal\XXX\Form\YYYForm');
\Drupal::formBuilder()->getForm('Drupal\XXX\Form\YYYForm', 'param1', 'param2');
Mieux vaut utiliser le type managed_file plutôt que file, il gère tout seul l'upload.
Lire l'annotation de MailManagerInterface.
- Sur le lien, rajouter class="use-ajax"
- Créer une route vers un controller
- Créer un controller
- AjaxResponse()
- AppendCommand()
- ReplaceCommand()
$defaults = menu_ui_get_menu_link_defaults($node);
$menu_tree_parameters = new MenuTreeParameters();
$menu_tree_parameters->setRoot($defaults['id'])->excludeRoot();
$menu = \Drupal::menuTree()->load('', $menu_tree_parameters);
// -- Mieux :
// Lien en cours.
/** @var \Drupal\Core\Menu\MenuActiveTrail $menu_active_trail */
$menu_active_trail = \Drupal::service('menu.active_trail');
/** @var \Drupal\Core\Menu\MenuLinkInterface $current_menu */
$current_menu = $menu_active_trail->getActiveLink();
$current_id = $current_menu->getPluginId();
// Toute l'arborescence du menu du lien en cours.
$menu_tree = \Drupal::menuTree();
$menu_name = $current_menu->getMenuName();
// Je commence l'arborescence directement sur le node en cours.
$parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);
$parameters->setRoot($current_menu->getPluginId())->excludeRoot();
$tree = $menu_tree->load($menu_name, $parameters);
// Transform the tree using the manipulators you want.
$manipulators = [
['callable' => 'menu.default_tree_manipulators:checkNodeAccess'],
// Only show links that are accessible for the current user.
['callable' => 'menu.default_tree_manipulators:checkAccess'],
// Use the default sorting of menu links.
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
];
$main_menu = $menu_tree->transform($tree, $manipulators);
if (!empty($defaults['entity_id'])) {
foreach ($menu as $value) {
$items[] = [
'#type' => 'link',
'#title' => $value->link->getTitle(),
'#url' => Url::fromRoute($value->link->getRouteName(), $value->link->getRouteParameters()),
];
}
$build['cens_standard_en_savoir_plus'] = [
'#theme' => 'item_list',
'#items' => $items,
];
}
// Sans node.
$parameters = new MenuTreeParameters();
$menu = \Drupal::menuTree()->load('entree-menu-4', $parameters);
// Créer les menus au préalable.
// A mettre dans un hook_update_N
// Lancer l'import avant pour avoir les menus de créée.
$menus = [
'footer-profils-fr' => [
'Grand public et donateurs' => [
['title' => "La recherche"],
],
'Patients et proches' => [
['title' => "Actualités"],
],
],
];
foreach ($menus as $menu_name => $menu) {
foreach ($menu as $level_one_title => $item) {
$first_level = MenuLinkContent::create([
'title' => $level_one_title,
'link' => ['uri' => 'internal:/node'],
'menu_name' => $menu_name,
'expanded' => TRUE,
'weight' => $counter++,
]);
$first_level->save();
foreach ($item as $level_two) {
MenuLinkContent::create([
'title' => $level_two['title'],
'link' => ['uri' => 'internal:/node'],
'parent' => $first_level->getPluginId(),
'menu_name' => $menu_name,
'weight' => $counter++,
])->save();
}
}
}
$filter = Drupal::service('plugin.manager.filter')->getInstance('[id_plugin_filter]');
$text = $filter->prepare($text);
$text = $filter->process($text);
$request = \Drupal::request();
$route_match = \Drupal::routeMatch();
$title = \Drupal::service('title_resolver')->getTitle($request, $route_match->getRouteObject());
S'il est lié à EntityReferenceFormatterBase alors, on utilise foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) {
sinon on utilise foreach ($items as $delta => $item) {
dans viewElements
use Drupal\search_api_solr\SearchApiSolrException;
try {
/** @var \Drupal\search_api\IndexInterface $index */
$index = Index::load($index_id);
if ($index === NULL) {
$links = [$this->t("XXX")];
}
/** @var \Drupal\search_api\Query\QueryInterface $query */
$query = $index->query();
/** @var \Drupal\search_api\Query\ConditionGroupInterface $conditions */
$conditions = $query->createConditionGroup('OR');
$conditions->addCondition('title', $text, 'like');
$conditions->addCondition('name', $text, 'like');
$query->addConditionGroup($conditions);
/** @var \Drupal\search_api\Query\ResultSetInterface $result */
$result = $query->execute();
if ($result->getResultCount() === 0) {
$links = [$this->t('Pas de résultat pour cette recherche')];
}
}
catch (SearchApiSolrException $exception) {
watchdog_exception(__METHOD__, $exception);
}
$query->setOption('solr_param_mm', '75%');
$solarium_query->addFilterQuery([
'key' => 'drm_dates_evenement',
'query' => 'drm_dates_evenement:' . gmdate('Y-m-d'),
]);
$route_parameters["filters_X"] = $solarium_query->createFilterQuery(['key' => "filters_X"])->setQuery("filters_X:$profileid");
$solarium_query->addFilterQueries($route_parameters);
$solarium_query->addSort('its_field_statistic', 'DESC');
// service : $container->get('search_api.query_helper')
$results = $this->QueryHelper->getAllResults();
if (current($results)->getResultCount() === 0) {
return [];
}
/**
* Implements hook_search_api_solr_field_mapping_alter().
*/
function XXX_search_api_solr_field_mapping_alter(IndexInterface $index, array &$fields) {
if (isset($fields['pays'])) {
$fields['pays'] = 'ss_pays';
}
}
// Configuration.
$this->configuration['fields']
// Le bundle.
$item->getDatasource()->getItemBundle($item->getOriginalObject())
// Drupal\Core\Entity\Plugin\DataType\EntityAdapter
$item->getOriginalObject()
// Drupal\media_entity\Entity\Media
$item->getOriginalObject()->getValue()
Ajouter l'autocomplete à un formulaire custom :
'#type' => 'textfield',
'#title' => $this->t('Votre recherche'),
'#attributes' => [
'placeholder' => $this->t('Search'),
'data-autocomplete-path' => '/search_api_autocomplete/recherche?display=recherche&&filter=text',
'data-search-api-autocomplete-search' => "recherche",
'class' => ['form-autocomplete', 'ui-autocomplete-input'],
],
'#attached' => [
'library' => [
'search_api_autocomplete/search_api_autocomplete',
],
],
'drupalSettings' => [
'search_api_autocomplete' => [
'recherche' => [
'delay' => 'true',
'auto_submit' => 'true',
'min_length' => 3,
],
],
],
Regarder facets_system_breadcrumb_alter
/** @var \Drupal\facets\FacetSource\FacetSourcePluginManager $facet_source_manager */
$facet_source_manager = \Drupal::service('plugin.manager.facets.facet_source');
/** @var \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager */
$facet_manager = \Drupal::service('facets.manager');
foreach ($facet_source_manager->getDefinitions() as $definition) {
if ($definition['id'] === 'search_api:XXX') {
foreach ($facet_manager->getFacetsByFacetSourceId($definition['id']) as $facet) {
if ($facet->getFieldIdentifier() === 'field_XXX' && !empty($facet->getActiveItems()) && $facet->getActiveItems()[0] === 'XXX') {
$solarium_query->addSort('its_field_XXX', 'ASC');
}
}
}
}
https://drupal-media.gitbooks.io/drupal8-guide/content/index.html
ENTITY MEDIA DOCUMENT
Création/modification de l'entité :
create
__construct
buildConfigurationForm
création du bundle :
create
__construct
thumbnail
Instanciation du média :
providedFields : retourne le nom des différentes propriétés (tableau)
getField : retourne les différentes propriétés (mime, size...)
Upload :
defaultConfiguration : configuration par défaut à l'ajout du widget sur EB
buildConfigurationForm : ça se passe dans l'ajout du widget, sert à afficher les deux champs location/extensions
getForm : affiche le form quand on clique sur l'onglet document
submit : gestion de la soumission du form au dessus
TEST:
$e = entity_load('media', 4);
d($e->getType());
d($e->getType()->providedFields());
d($e->getType()->getField($e, 'size'));
d($e->getType()->getField($e, 'mime'));
- https://api.drupal.org/api/drupal/core%21core.api.php/group/container/
- Services and dependency injection in Drupal 8
- Surcharger un service
Par ordre de priorité.
\Drupal::XXX()
- Meilleure lisibilité
- Typehint accessible pour IDE
\Drupal::service()
- En dernier recours si \Drupal::XXX n'existe pas
- Pour trouver le nom du service, c'est marqué dans la méthode \Drupal::XXX() ou regarder xxx.services.yml
Les deux font la même chose en gros.
Les classes de type "entity type" n'ont pas d'injection de dépendance.
- On injecte les services via $container->get('XXX') (aka ContainerInterface)
- Cela permet d'injecter ce que l'on veut via les tests
- Dans .install car tout n'est pas encore initialisé (YAML config/YAML de module)
- Plugin -> ContainerFactoryPluginInterface
- Bloc (à mettre sur le bon renderrable array)
'#cache' => [
'contexts' => [
'url',
'languages',
],
'tags' => $entity->getCacheTags(), // dans le cas où le bloc est sur une entité et dépend d'elle
'tags' => ['node_list'], // dans le cas où le bloc est dynamique par rapport à un CRUD d'un node
'tags' => ['menu_link_content_list'], // dans le cas où le bloc est dynamique par rapport à un CRUD d'un menu
],
- Pour les extrafields, pas besoin grâce à EntityViewBuilder::getBuildDefaults qui en génère par défaut sauf dans le cas d'une contextualisation type url, cookie...
- {{ content.field_url.0['#url'] }} : récupérer l'information avec un '#'
- {{ content.field_reference_pays }} : afficher le markup d'un champ
- {% if node.field_statut.value %} : valeur d'un champ
- {{ devel_breakpoint() }} pour mieux debuguer
- Encore en 8.4, on ne voit pas les suggestions template d'une vue mais ça fonctionne quand même
- Include de template
- composer update : met à jour tous les paquets du composer.json
- composer update drupal/media_entity_document : met à jour juste ce module
- composer search [cequetuveux] : pour trouver un paquet rapidement
- composer validate : pour voir si notre composer.json est correct
- composer update drupal/search_api_solr --with-dependencies solarium/solarium
../vendor/bin/drupal list
../vendor/bin/drupal generate:module --module="[X]" --machine-name="[X]" --module-path="/modules/custom" --description="[X]" --package="[X]" --core=8.x -n
../vendor/bin/drupal generate:plugin:block --module="[X]" --class="[X]Block" --label="[X] : [X]" --plugin-id="[X]" --theme-region="[X]" -n
- ATTENTION : il faut modifier le yml pour avoir un ID
../vendor/bin/drupal generate:controller --module="[X]" --class="[X]" --routes='"title":"[X]", "name":"[X]", "method":"[X]", "path":"/[X]/[Y]"' --services="[X]" -n
../vendor/bin/drupal generate:form --module="[X]" --class="[X]Form" --form-id="[X]" -n
- Supprimer la route si pas besoin
../vendor/bin/drupal generate:service --module="[X]" --class="[X]" --name="[MODULE_NAME].[X]" --path-service="/modules/custom/[MODULE_NAME]/src/" -n
- Supprimer les arguments dans la route si pas besoin
composer up --with-dependencies drupal/console
pour le mettre à jour en rc12 car drush bloque les autres versions dû à une dépendance avec symfony-console-completion, qui dépend de alchemy/zippy en 0.3 alors que console-core veur zippy 0.4
Les commandes de génération se trouvent dans src/Generator et src/Command/Generate.
- dis => pm-uninstall
- cc all => cache-rebuild
- drush config-get --include-overridden system.performance cache.page.max_age
- drush cex pour exporter
- drush cim pour importer (équivalent de drush updb && dfra)
- drush cim --preview=diff (pour voir les diff avant d'importer)
- drush config-merge ... (pour voir les différences entre la config locale et celle pullée)
- uuidgen pour générer un UUID quand on override une configuration
- taxonomy_term_load(1)->getFieldDefinitions()['name']->getConfig('foobar') pour savoir quoi mettre dans un override yml
- Pour afficher un kint() dans /batch, faire un kint() dans batchPage()
- Changer la version d'un hook_update_n : SELECT * FROM key_value WHERE collection LIKE 'system.schema' and name='facets'
- Variables
- .info
- On ne peut pas faire d'inline, le mettre dans un .js