drupal d8
parameters:
session.storage.options:
cookie_domain: '.nerocro.ows'
<?php
// Base de données.
$databases['default']['default'] = [
'host' => 'localhost',
'driver' => 'mysql',
'database' => '[DATABASE]',
'username' => '[USERNAME]',
'password' => '[PASSWORD]',
];
// URL en laquelle on a confiance.
$settings['trusted_host_patterns'][] = '^[XXX]\.nerocro\.fr$';
// Dossier des fichiers privés.
$settings['file_private_path'] = DRUPAL_ROOT . '/../drupal_private_files';
// Ne plus avoir de cache de rendu.
$settings['container_yamls'][] = __DIR__ . '/inc/development.services.yml';
$settings['cache']['bins']['render'] = 'cache.backend.null';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
// Pas de cache.
$config['system.performance']['cache']['page']['max_age'] = 0;
// Partage de session.
// $settings['container_yamls'][] = __DIR__ . '/inc/cookie.services.yml';
// Désactivation du cron.
$config['automated_cron.settings']['interval'] = 0;
// Afficher les erreurs.
$config['system.logging']['error_level'] = 'verbose';
// E-mail administrateur.
$config['system.site']['mail'] = 'nerocro@XXX.fr';
// Pas d'agrégation des fichiers statiques.
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
// Config split.
$config['config_split.config_split.devel']['status'] = TRUE;
$config['config_split.config_split.stage_file_proxy']['status'] = TRUE;
$config['config_split.config_split.webprofiler']['status'] = FALSE;
// JSON API.
$settings['campus_jsonapi_remotes'] = ['127.0.0.1'];
// Solr.
$config['search_api.server.solr_5']['backend_config']['connector_config']['host'] = 'localhost';
$config['search_api.server.solr_5']['backend_config']['connector_config']['port'] = '8983';
$config['search_api.server.solr_5']['backend_config']['connector_config']['core'] = 'CORENAME';
// $config['search_api.server.solr_5']['backend_config']['connector_config']['auth']['username'] = '';
// $config['search_api.server.solr_5']['backend_config']['connector_config']['auth']['password'] = '';
// Évite de modifier la production si je lance une indexation en local.
$config['search_api.index.default']['read_only'] = TRUE;
// Thème admin.
// $config['system.theme']['admin'] = 'seven';
$config['node.settings']['use_admin_theme'] = TRUE;
// Views.
$config['views.settings']['ui']['show']['master_display'] = TRUE;
$config['views.settings']['ui']['show']['advanced_column'] = TRUE;
$config['views.settings']['ui']['show']['display_embed'] = TRUE;
$config['views.settings']['ui']['show']['preview_information'] = TRUE;
$config['views.settings']['ui']['show']['performance_statistics'] = FALSE;
$config['views.settings']['ui']['show']['additional_queries'] = TRUE;
$config['views.settings']['ui']['show']['sql_query']['where'] = 'below';
$config['views.settings']['ui']['show']['sql_query']['enabled'] = TRUE;
$config['views.settings']['ui']['always_live_preview'] = TRUE;
$config['views.settings']['ui']['exposed_filter_any_label'] = 'new_any';
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
\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.
}
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.
$entity->addCacheContexts(['route']);
// Pour connaître le parent.
$term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
// Charge tous les termes d'un vocabulaire.
$tree = $this->entityTypeManager->getStorage('taxonomy_term')->loadTree('thematique');
// Pareil mais charge les entités.
$tree = $this->entityTypeManager->getStorage('taxonomy_term')->loadTree('categorie', 0, NULL, TRUE);
\Drupal::currentUser()->hasPermission('configure variables site for admin');
$query = $this->entityTypeManager->getStorage('node')->getQuery()
->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
/** @var \Drupal\Core\Block\BlockManagerInterface $block_manager */
$block_manager = \Drupal::service('plugin.manager.block');
/** @var \Drupal\Core\Block\BlockPluginInterface $block */
$block = $block_manager->createInstance('BLOCK_ID');
$render = $block->build();
$block = $this->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.
$this->dateFormatter->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.
\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.
$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');
}
}
}
}
Par ordre de priorité.
\Drupal::XXX()
\Drupal::service()
Les deux font la même chose en gros.
Les classes de type "entity type" n'ont pas d'injection de dépendance.
'#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
],
../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
../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
../vendor/bin/drupal generate:service --module="[X]" --class="[X]" --name="[MODULE_NAME].[X]" --path-service="/modules/custom/[MODULE_NAME]/src/" -n
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.4Les commandes de génération se trouvent dans src/Generator et src/Command/Generate.