From 1dee70f4c68bce451e78335fc36f25ac2a5b027a Mon Sep 17 00:00:00 2001 From: Dieter Holvoet Date: Fri, 2 Jun 2023 14:10:10 +0200 Subject: [PATCH] field-create: Make it easier for field types extending entity reference to add target type/bundles options (#5556) * Move entity reference related logic to hook implementations * Remove ensureOption method * Fix cs --- .../Commands/field/FieldCreateCommands.php | 110 ------------- .../field/FieldEntityReferenceHooks.php | 151 ++++++++++++++++++ src/Drupal/Commands/field/drush.services.yml | 9 +- 3 files changed, 159 insertions(+), 111 deletions(-) create mode 100644 src/Drupal/Commands/field/FieldEntityReferenceHooks.php diff --git a/src/Drupal/Commands/field/FieldCreateCommands.php b/src/Drupal/Commands/field/FieldCreateCommands.php index 61e6de8d65..e355e2420a 100644 --- a/src/Drupal/Commands/field/FieldCreateCommands.php +++ b/src/Drupal/Commands/field/FieldCreateCommands.php @@ -202,10 +202,6 @@ public function create(?string $entityType = null, ?string $bundle = null, array $this->ensureOption('is-translatable', [$this, 'askTranslatable'], false); $this->ensureOption('cardinality', [$this, 'askCardinality'], true); - if ($this->input->getOption('field-type') === 'entity_reference') { - $this->ensureOption('target-type', [$this, 'askReferencedEntityType'], true); - } - $this->createFieldStorage(); } @@ -363,81 +359,6 @@ protected function askCardinality(): int return $limit; } - - protected function askReferencedEntityType(): string - { - $definitions = $this->entityTypeManager->getDefinitions(); - $choices = []; - - foreach ($definitions as $name => $definition) { - $label = $this->input->getOption('show-machine-names') - ? $name - : sprintf('%s: %s', $definition->getGroupLabel()->render(), $definition->getLabel()); - $choices[$name] = $label; - } - - return $this->io()->choice('Referenced entity type', $choices); - } - - protected function askReferencedBundles(FieldDefinitionInterface $fieldDefinition): array - { - $choices = []; - $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo( - $fieldDefinition->getFieldStorageDefinition()->getSetting('target_type') - ); - - if (empty($bundleInfo)) { - return []; - } - - foreach ($bundleInfo as $bundle => $info) { - $label = $this->input->getOption('show-machine-names') ? $bundle : $info['label']; - $choices[$bundle] = $label; - } - - $question = (new ChoiceQuestion('Referenced bundles', $choices)) - ->setMultiselect(true); - - return $this->io()->askQuestion($question) ?: []; - } - - protected function askBundle(): ?string - { - $entityTypeId = $this->input->getArgument('entityType'); - $entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId); - $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); - $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entityTypeId); - $choices = []; - - if ($bundleEntityType && $bundleInfo === []) { - throw new \InvalidArgumentException( - t('Entity type with id \':entityType\' does not have any bundles.', [':entityType' => $entityTypeId]) - ); - } - - if ($fieldName = $this->input->getOption('existing-field-name')) { - $bundleInfo = array_filter($bundleInfo, function (string $bundle) use ($entityTypeId, $fieldName) { - return !$this->entityTypeManager->getStorage('field_config')->load("$entityTypeId.$bundle.$fieldName"); - }, ARRAY_FILTER_USE_KEY); - } - - if (!$bundleEntityType && count($bundleInfo) === 1) { - // eg. User - return $entityTypeId; - } - - foreach ($bundleInfo as $bundle => $data) { - $label = $this->input->getOption('show-machine-names') ? $bundle : $data['label']; - $choices[$bundle] = $label; - } - - if (!$answer = $this->io()->choice('Bundle', $choices)) { - throw new \InvalidArgumentException(t('The bundle argument is required.')); - } - - return $answer; - } - protected function createField(): FieldConfigInterface { $values = [ @@ -460,33 +381,6 @@ protected function createField(): FieldConfigInterface $field = $this->entityTypeManager ->getStorage('field_config') ->create($values); - - if ($this->input->getOption('field-type') === 'entity_reference') { - $targetType = $this->input->getOption('target-type'); - $targetTypeDefinition = $this->entityTypeManager->getDefinition($targetType); - // For the 'target_bundles' setting, a NULL value is equivalent to "allow - // entities from any bundle to be referenced" and an empty array value is - // equivalent to "no entities from any bundle can be referenced". - $targetBundles = null; - - if ($targetTypeDefinition->hasKey('bundle')) { - if ($referencedBundle = $this->input->getOption('target-bundle')) { - $this->validateBundle($targetType, $referencedBundle); - $referencedBundles = [$referencedBundle]; - } else { - $referencedBundles = $this->askReferencedBundles($field); - } - - if (!empty($referencedBundles)) { - $targetBundles = array_combine($referencedBundles, $referencedBundles); - } - } - - $settings = $field->getSetting('handler_settings') ?? []; - $settings['target_bundles'] = $targetBundles; - $field->setSetting('handler_settings', $settings); - } - $field->save(); return $field; @@ -502,10 +396,6 @@ protected function createFieldStorage(): FieldStorageConfigInterface 'translatable' => true, ]; - if ($targetType = $this->input->getOption('target-type')) { - $values['settings']['target_type'] = $targetType; - } - // Command files may customize $values as desired. $handlers = $this->getCustomEventHandlers('field-create-field-storage'); foreach ($handlers as $handler) { diff --git a/src/Drupal/Commands/field/FieldEntityReferenceHooks.php b/src/Drupal/Commands/field/FieldEntityReferenceHooks.php new file mode 100644 index 0000000000..6440cfaa47 --- /dev/null +++ b/src/Drupal/Commands/field/FieldEntityReferenceHooks.php @@ -0,0 +1,151 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + } + + /** + * @hook on-event field-create-field-storage + */ + public function hookFieldStorage(array $values, InputInterface $input): array + { + if ($input->getOption('field-type') === 'entity_reference') { + $values['settings']['target_type'] = $this->getTargetType($input); + } + + return $values; + } + + /** + * @hook on-event field-create-field-config + */ + public function hookFieldConfig(array $values, InputInterface $input): array + { + if ($input->getOption('field-type') === 'entity_reference') { + $values['settings']['handler_settings']['target_bundles'] = $this->getTargetBundles($input); + } + + return $values; + } + + protected function getTargetType(InputInterface $input): string + { + $value = $input->getOption('target-type'); + + if ($value === null && $input->isInteractive()) { + $value = $this->askReferencedEntityType(); + } + + if ($value === null) { + throw new \InvalidArgumentException(dt('The %optionName option is required.', [ + '%optionName' => 'target-type', + ])); + } + + $input->setOption('target-type', $value); + + return $value; + } + + protected function getTargetBundles(InputInterface $input): ?array + { + $targetType = $input->getOption('target-type'); + $targetTypeDefinition = $this->entityTypeManager->getDefinition($targetType); + // For the 'target_bundles' setting, a NULL value is equivalent to "allow + // entities from any bundle to be referenced" and an empty array value is + // equivalent to "no entities from any bundle can be referenced". + $targetBundles = null; + + if ($targetTypeDefinition->hasKey('bundle')) { + if ($referencedBundle = $input->getOption('target-bundle')) { + $this->validateBundle($targetType, $referencedBundle); + $referencedBundles = [$referencedBundle]; + } else { + $referencedBundles = $this->askReferencedBundles($targetType); + } + + if (!empty($referencedBundles)) { + $targetBundles = array_combine($referencedBundles, $referencedBundles); + } + } + + return $targetBundles; + } + + protected function askReferencedEntityType(): string + { + $definitions = $this->entityTypeManager->getDefinitions(); + $choices = []; + + foreach ($definitions as $name => $definition) { + $label = $this->input->getOption('show-machine-names') + ? $name + : sprintf('%s: %s', $definition->getGroupLabel()->render(), $definition->getLabel()); + $choices[$name] = $label; + } + + return $this->io()->choice('Referenced entity type', $choices); + } + + protected function askReferencedBundles(string $targetType): ?array + { + $choices = []; + $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($targetType); + + if (empty($bundleInfo)) { + return []; + } + + foreach ($bundleInfo as $bundle => $info) { + $label = $this->input->getOption('show-machine-names') ? $bundle : $info['label']; + $choices[$bundle] = $label; + } + + $question = (new ChoiceQuestion('Referenced bundles', $choices)) + ->setMultiselect(true); + + return $this->io()->askQuestion($question) ?: null; + } +} diff --git a/src/Drupal/Commands/field/drush.services.yml b/src/Drupal/Commands/field/drush.services.yml index 8c302ca57c..86436babf3 100644 --- a/src/Drupal/Commands/field/drush.services.yml +++ b/src/Drupal/Commands/field/drush.services.yml @@ -42,4 +42,11 @@ services: - '@entity_type.bundle.info' - '@entity_field.manager' tags: - - { name: drush.command } \ No newline at end of file + - { name: drush.command } + field.entity-reference.hooks: + class: \Drush\Drupal\Commands\field\FieldEntityReferenceHooks + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + tags: + - { name: drush.command }