more migration

This commit is contained in:
2026-07-12 18:55:08 +02:00
parent dfefc56268
commit 47a4f04c65
17 changed files with 699 additions and 165 deletions

View File

@@ -0,0 +1,14 @@
uuid: 143a87e9-a828-419c-8f07-117346183d44
langcode: fr
status: true
dependencies: { }
id: projet_artiste
label: 'Projet - Artiste'
enabled: true
first_field: field_artiste_s_invite_s
second_field: field_projet_s_lie_s
add_direction: append
bundles:
node:
- personne
- projet

View File

@@ -0,0 +1,14 @@
uuid: 73bfbc5b-f77c-47d8-9725-aac43032587d
langcode: fr
status: true
dependencies: { }
id: projet_curateur
label: 'Projet - Curateur'
enabled: true
first_field: field_invite_accompagne_curate_p
second_field: field_projet_s_lie_s
add_direction: append
bundles:
node:
- personne
- projet

View File

@@ -0,0 +1,14 @@
uuid: 6d877a10-29bf-41ad-8ed9-46a333e96e18
langcode: fr
status: true
dependencies: { }
id: projet_partenaire
label: 'Projet - Partenaire'
enabled: true
first_field: field_partenaire_s
second_field: field_projet_s_lie_s
add_direction: append
bundles:
node:
- partenaire
- projet

View File

@@ -11,4 +11,6 @@ add_direction: append
bundles:
node:
- evenement
- partenaire
- personne
- projet

View File

@@ -6,13 +6,38 @@ dependencies:
- field.field.node.partenaire.body
- field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- image.style.thumbnail
- node.type.partenaire
module:
- field_group
- image
- link
- path
- text
third_party_settings:
field_group:
group_details:
children:
- field_logo
- field_lien
- status
- path
- langcode
label: Details
region: content
parent_name: ''
weight: 3
format_type: details_sidebar
format_settings:
classes: ''
show_empty_fields: false
id: ''
label_as_html: false
open: true
description: ''
required_fields: true
weight: -100
id: node.partenaire.default
targetEntityType: node
bundle: partenaire
@@ -20,7 +45,7 @@ mode: default
content:
body:
type: text_textarea_with_summary
weight: 121
weight: 1
region: content
settings:
rows: 9
@@ -28,15 +53,9 @@ content:
placeholder: ''
show_summary: false
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_lien:
type: link_default
weight: 122
weight: 3
region: content
settings:
placeholder_url: ''
@@ -44,57 +63,15 @@ content:
third_party_settings: { }
field_logo:
type: image_image
weight: 123
weight: 2
region: content
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
langcode:
type: language_select
weight: 2
region: content
settings:
include_locked: true
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
weight: 120
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
field_projet_s_lie_s:
type: entity_reference_autocomplete
weight: 5
weight: 2
region: content
settings:
match_operator: CONTAINS
@@ -102,4 +79,37 @@ content:
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }
langcode:
type: language_select
weight: 7
region: content
settings:
include_locked: true
third_party_settings: { }
path:
type: path
weight: 6
region: content
settings: { }
third_party_settings: { }
status:
type: boolean_checkbox
weight: 5
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
promote: true
sticky: true
uid: true
url_redirects: true

View File

@@ -5,11 +5,35 @@ dependencies:
config:
- field.field.node.personne.body
- field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_type_de_personne
- node.type.personne
module:
- field_group
- path
- text
third_party_settings:
field_group:
group_details:
children:
- field_type_de_personne
- status
- path
- langcode
label: Details
region: content
parent_name: ''
weight: 4
format_type: details_sidebar
format_settings:
classes: ''
show_empty_fields: false
id: ''
label_as_html: false
open: true
description: ''
required_fields: true
weight: -100
id: node.personne.default
targetEntityType: node
bundle: personne
@@ -17,7 +41,7 @@ mode: default
content:
body:
type: text_textarea_with_summary
weight: 121
weight: 2
region: content
settings:
rows: 9
@@ -25,23 +49,27 @@ content:
placeholder: ''
show_summary: false
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_prenom:
type: string_textfield
weight: 122
weight: 1
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
field_projet_s_lie_s:
type: entity_reference_autocomplete
weight: 3
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
field_type_de_personne:
type: entity_reference_autocomplete
weight: 123
weight: 11
region: content
settings:
match_operator: CONTAINS
@@ -51,54 +79,35 @@ content:
third_party_settings: { }
langcode:
type: language_select
weight: 2
weight: 14
region: content
settings:
include_locked: true
third_party_settings: { }
path:
type: path
weight: 30
weight: 13
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
weight: 120
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
weight: 12
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: -5
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }
hidden:
created: true
promote: true
sticky: true
uid: true
url_redirects: true

View File

@@ -6,6 +6,7 @@ dependencies:
- field.field.node.partenaire.body
- field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- node.type.partenaire
module:
- image
@@ -47,6 +48,14 @@ content:
third_party_settings: { }
weight: 103
region: content
field_projet_s_lie_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 108
region: content
links:
settings: { }
third_party_settings: { }

View File

@@ -7,6 +7,7 @@ dependencies:
- field.field.node.partenaire.body
- field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- node.type.partenaire
module:
- text
@@ -32,5 +33,6 @@ content:
hidden:
field_lien: true
field_logo: true
field_projet_s_lie_s: true
langcode: true
search_api_excerpt: true

View File

@@ -5,6 +5,7 @@ dependencies:
config:
- field.field.node.personne.body
- field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_type_de_personne
- node.type.personne
module:
@@ -30,6 +31,14 @@ content:
third_party_settings: { }
weight: 102
region: content
field_projet_s_lie_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 108
region: content
field_type_de_personne:
type: entity_reference_label
label: above

View File

@@ -6,6 +6,7 @@ dependencies:
- core.entity_view_mode.node.teaser
- field.field.node.personne.body
- field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_type_de_personne
- node.type.personne
module:
@@ -31,6 +32,7 @@ content:
region: content
hidden:
field_prenom: true
field_projet_s_lie_s: true
field_type_de_personne: true
langcode: true
search_api_excerpt: true

View File

@@ -0,0 +1,29 @@
uuid: 47db4d6d-cf31-4049-b817-22c99a51e797
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_projet_s_lie_s
- node.type.partenaire
- node.type.projet
id: node.partenaire.field_projet_s_lie_s
field_name: field_projet_s_lie_s
entity_type: node
bundle: partenaire
label: 'Projet(s) lié(s)'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
projet: projet
sort:
field: _none
direction: ASC
auto_create: false
auto_create_bundle: projet
field_type: entity_reference

View File

@@ -0,0 +1,29 @@
uuid: d3189e52-d468-4bee-8463-5067aeb810c7
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_projet_s_lie_s
- node.type.personne
- node.type.projet
id: node.personne.field_projet_s_lie_s
field_name: field_projet_s_lie_s
entity_type: node
bundle: personne
label: 'Projet(s) lié(s)'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
projet: projet
sort:
field: _none
direction: ASC
auto_create: false
auto_create_bundle: projet
field_type: entity_reference

View File

@@ -33,6 +33,22 @@ source:
name: date_end
label: 'Date end'
selector: 'Date de fin de projet'
-
name: projet_type
label: 'Type de projet'
selector: 'Type de projet'
-
name: curator
label: Commissaria
selector: 'invité/accompagné/curaté par'
-
name: artistes
label: 'Artistes invité·es'
selector: 'Artistes invité·es'
-
name: partners
label: 'Partenaires associés'
selector: 'Partenaires associés'
ids:
id:
type: integer
@@ -42,15 +58,53 @@ process:
default_value: projet
title: title
field_dates/value:
plugin: format_date
plugin: leshed_date
source: date_start
from_format: d/m/Y
to_format: Y-m-d
field_dates/end_value:
plugin: format_date
plugin: leshed_date
source: date_end
from_format: d/m/Y
to_format: Y-m-d
end_of_year: true
fallback_source: date_start
field_type_de_projet:
-
plugin: skip_on_empty
method: process
source: projet_type
-
plugin: explode
delimiter: ','
-
plugin: callback
callable: trim
-
plugin: entity_generate
entity_type: taxonomy_term
bundle_key: vid
bundle: type_de_projet
value_key: name
field_invite_accompagne_curate_p:
-
plugin: skip_on_empty
method: process
source: curator
-
plugin: personne_generate
type_de_personne: curateur
field_artiste_s_invite_s:
-
plugin: skip_on_empty
method: process
source: artistes
-
plugin: personne_generate
type_de_personne: artiste
field_partenaire_s:
-
plugin: skip_on_empty
method: process
source: partners
-
plugin: partenaire_generate
uid:
plugin: default_value
default_value: 3

View File

@@ -2,6 +2,9 @@ uuid: 34d30e20-1672-4c74-b35d-5f4312f520f2
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_prenom
- field.storage.node.field_type_de_projet
module:
- node
- user
@@ -39,26 +42,6 @@ display:
hide_empty: false
empty_zero: false
hide_alter_empty: true
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
plugin_id: field
label: Titre
exclude: false
alter:
alter_text: false
element_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
type: string
settings:
link_to_entity: true
type:
id: type
table: node_field_data
@@ -124,6 +107,197 @@ display:
multi_type: separator
separator: ', '
field_api_classes: false
field_prenom:
id: field_prenom
table: node__field_prenom
field: field_prenom
relationship: none
group_type: group
admin_label: ''
plugin_id: field
label: ''
exclude: true
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: title
plugin_id: field
label: Titre
exclude: false
alter:
alter_text: true
text: '{{ field_prenom }} {{ title }}'
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
field_type_de_projet:
id: field_type_de_projet
table: node__field_type_de_projet
field: field_type_de_projet
relationship: none
group_type: group
admin_label: ''
plugin_id: field
label: 'Type de projet'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: false
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
name:
id: name
table: users_field_data
@@ -249,13 +423,13 @@ display:
pager:
type: full
options:
pagination_heading_level: h4
items_per_page: 50
tags:
next: 'Suivant '
previous: ' Précédent'
first: '« Premier'
last: 'Dernier »'
pagination_heading_level: h4
exposed_form:
type: basic
options:
@@ -483,15 +657,13 @@ display:
default_row_class: true
columns:
node_bulk_form: node_bulk_form
type: title
field_prenom: title
title: title
type: type
name: name
status: status
changed: changed
edit_node: edit_node
delete_node: delete_node
dropbutton: dropbutton
timestamp: title
operations: operations
default: changed
info:
node_bulk_form:
@@ -499,13 +671,6 @@ display:
separator: ''
empty_column: false
responsive: ''
title:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
type:
sortable: true
default_sort_order: asc
@@ -513,6 +678,20 @@ display:
separator: ''
empty_column: false
responsive: ''
field_prenom:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
title:
sortable: true
default_sort_order: asc
align: ''
separator: '<br/>'
empty_column: false
responsive: ''
name:
sortable: false
default_sort_order: asc
@@ -534,30 +713,7 @@ display:
separator: ''
empty_column: false
responsive: priority-low
edit_node:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
delete_node:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
dropbutton:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
timestamp:
sortable: false
default_sort_order: asc
operations:
align: ''
separator: ''
empty_column: false
@@ -592,7 +748,9 @@ display:
- user
- 'user.node_grants:view'
- user.permissions
tags: { }
tags:
- 'config:field.storage.node.field_prenom'
- 'config:field.storage.node.field_type_de_projet'
page_1:
id: page_1
display_title: Page
@@ -624,4 +782,6 @@ display:
- user
- 'user.node_grants:view'
- user.permissions
tags: { }
tags:
- 'config:field.storage.node.field_prenom'
- 'config:field.storage.node.field_type_de_projet'

View File

@@ -43,14 +43,18 @@ source:
name: projet_type
label: 'Type de projet'
selector: 'Type de projet'
-
name: invites
label: 'Invité/accompagné/curaté par'
selector: 'invité/accompagné/curaté par'
-
name: curator
label: 'Commissaria'
selector: 'invité/accompagné/curaté par'
-
name: artistes
label: 'Artistes invité·es'
selector: 'Artistes invité·es'
-
name: partners
label: 'Partenaires associés'
selector: 'Partenaires associés'
# Under 'ids', we identify source fields populated above which will uniquely
# identify each imported item. The 'type' makes sure the migration map table
# uses the proper schema type for stored the IDs.
@@ -91,9 +95,25 @@ process:
-
plugin: skip_on_empty
method: process
source: invites
source: curator
-
plugin: personne_generate
type_de_personne: curateur
field_artiste_s_invite_s:
-
plugin: skip_on_empty
method: process
source: artistes
-
plugin: personne_generate
type_de_personne: artiste
field_partenaire_s:
-
plugin: skip_on_empty
method: process
source: partners
-
plugin: partenaire_generate
uid:
plugin: default_value
default_value: 3

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Finds or creates 'partenaire' nodes from a comma-separated list of names.
*
* Input: a string such as "Port Autonome de Rouen, Labelle 69". Names are
* split on commas and trimmed. Empty values and placeholders ("/") are
* skipped. An existing node with the same title is reused, otherwise it is
* created. Returns an array of node IDs.
*/
#[MigrateProcess(id: 'partenaire_generate', handle_multiples: TRUE)]
final class PartenaireGenerate extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* Placeholder values that must not create a partenaire node.
*/
private const IGNORED_VALUES = ['/', '?', '-'];
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly EntityTypeManagerInterface $entityTypeManager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): array {
if (!is_string($value) || trim($value) === '') {
return [];
}
$names = array_filter(array_map('trim', explode(',', $value)));
$nids = [];
foreach ($names as $name) {
if (in_array($name, self::IGNORED_VALUES, TRUE)) {
continue;
}
$nids[] = $this->getOrCreatePartenaire($name);
}
return $nids;
}
/**
* Returns the nid of the matching partenaire node, creating it if needed.
*/
private function getOrCreatePartenaire(string $title): int {
$storage = $this->entityTypeManager->getStorage('node');
$ids = $storage->getQuery()
->condition('type', 'partenaire')
->condition('title', $title)
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($ids) {
return (int) reset($ids);
}
$node = $storage->create([
'type' => 'partenaire',
'title' => $title,
'status' => 1,
'uid' => $this->configuration['author_uid'] ?? 3,
]);
$node->save();
return (int) $node->id();
}
}

View File

@@ -20,6 +20,12 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* commas. For each name, the first word is used as 'field_prenom' and the
* rest as the node title. An existing node with the same title and prenom
* is reused, otherwise it is created. Returns an array of node IDs.
*
* When 'type_de_personne' is set in the configuration, a taxonomy term with
* that name is found or created (without duplicates) in the
* 'type_de_personne' vocabulary, and referenced from the node's
* 'field_type_de_personne' field. The tag is also added to matching
* existing nodes that do not yet have it.
*/
#[MigrateProcess(id: 'personne_generate', handle_multiples: TRUE)]
final class PersonneGenerate extends ProcessPluginBase implements ContainerFactoryPluginInterface {
@@ -92,6 +98,9 @@ final class PersonneGenerate extends ProcessPluginBase implements ContainerFacto
private function getOrCreatePersonne(string $nom, string $prenom): int {
$storage = $this->entityTypeManager->getStorage('node');
$type_name = $this->configuration['type_de_personne'] ?? NULL;
$tid = $type_name ? $this->getOrCreateType($type_name) : NULL;
$query = $storage->getQuery()
->condition('type', 'personne')
->condition('title', $nom)
@@ -105,19 +114,67 @@ final class PersonneGenerate extends ProcessPluginBase implements ContainerFacto
}
$ids = $query->execute();
if ($ids) {
return (int) reset($ids);
$nid = (int) reset($ids);
if ($tid !== NULL) {
$this->addTypeToPersonne($nid, $tid);
}
return $nid;
}
$node = $storage->create([
$values = [
'type' => 'personne',
'title' => $nom,
'field_prenom' => $prenom === '' ? NULL : $prenom,
'status' => 1,
'uid' => $this->configuration['author_uid'] ?? 3,
]);
];
if ($tid !== NULL) {
$values['field_type_de_personne'] = $tid;
}
$node = $storage->create($values);
$node->save();
return (int) $node->id();
}
/**
* Adds a type_de_personne term to an existing node if not already set.
*/
private function addTypeToPersonne(int $nid, int $tid): void {
$node = $this->entityTypeManager->getStorage('node')->load($nid);
if ($node === NULL) {
return;
}
$existing = array_column($node->get('field_type_de_personne')->getValue(), 'target_id');
if (!in_array((string) $tid, array_map('strval', $existing), TRUE)) {
$node->get('field_type_de_personne')->appendItem(['target_id' => $tid]);
$node->save();
}
}
/**
* Returns the tid of the matching type_de_personne term, creating it if needed.
*/
private function getOrCreateType(string $name): int {
$storage = $this->entityTypeManager->getStorage('taxonomy_term');
$ids = $storage->getQuery()
->condition('vid', 'type_de_personne')
->condition('name', $name)
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($ids) {
return (int) reset($ids);
}
$term = $storage->create([
'vid' => 'type_de_personne',
'name' => $name,
]);
$term->save();
return (int) $term->id();
}
}