From 2e43e513024d8d7e6df79ddf78ae23b458959174 Mon Sep 17 00:00:00 2001 From: Dieter Holvoet Date: Sun, 12 Dec 2021 17:10:19 +0100 Subject: [PATCH 1/6] Add field:delete command --- .../Commands/core/FieldCreateCommands.php | 75 +--------- .../Commands/core/FieldDeleteCommands.php | 135 ++++++++++++++++++ src/Drupal/Commands/core/drush.services.yml | 7 + 3 files changed, 144 insertions(+), 73 deletions(-) create mode 100644 src/Drupal/Commands/core/FieldDeleteCommands.php diff --git a/src/Drupal/Commands/core/FieldCreateCommands.php b/src/Drupal/Commands/core/FieldCreateCommands.php index b9a441cd1c..f026368f2d 100644 --- a/src/Drupal/Commands/core/FieldCreateCommands.php +++ b/src/Drupal/Commands/core/FieldCreateCommands.php @@ -26,7 +26,9 @@ class FieldCreateCommands extends DrushCommands implements CustomEventAwareInterface { + use AskBundleTrait; use CustomEventAwareTrait; + use ValidateEntityTypeTrait; /** @var FieldTypePluginManagerInterface */ protected $fieldTypePluginManager; @@ -216,41 +218,6 @@ public function create(string $entityType, ?string $bundle = null, array $option $this->logResult($field); } - protected function validateEntityType(string $entityTypeId): void - { - if (!$this->entityTypeManager->hasDefinition($entityTypeId)) { - throw new \InvalidArgumentException( - t("Entity type with id ':entityType' does not exist.", [':entityType' => $entityTypeId]) - ); - } - } - - protected function validateBundle(string $entityTypeId, string $bundle): void - { - if (!$entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId)) { - return; - } - - $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); - - if ($bundleEntityType === null && $bundle === $entityTypeId) { - return; - } - - $bundleDefinition = $this->entityTypeManager - ->getStorage($bundleEntityType) - ->load($bundle); - - if (!$bundleDefinition) { - throw new \InvalidArgumentException( - t("Bundle ':bundle' does not exist on entity type with id ':entityType'.", [ - ':bundle' => $bundle, - ':entityType' => $entityTypeId, - ]) - ); - } - } - protected function askExistingFieldName(): ?string { $entityType = $this->input->getArgument('entityType'); @@ -361,44 +328,6 @@ protected function askTranslatable(): bool return $this->io()->confirm('Translatable', false); } - protected function askBundle(): ?string - { - $entityTypeId = $this->input->getArgument('entityType'); - $entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId); - $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); - $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entityTypeId); - $choices = []; - - // If the entity type has one fixed bundle (eg. user), return it. - if ($bundleEntityType === null && count($bundleInfo) === 1) { - return key($bundleInfo); - } - - // If the entity type doesn't have bundles, return null - // TODO Find an example - if ($bundleEntityType === null && count($bundleInfo) === 0) { - return null; - } - - // If the entity type can have multiple bundles but it doesn't have any, throw an error - if ($bundleEntityType !== null && count($bundleInfo) === 0) { - throw new \InvalidArgumentException( - t("Entity type with id ':entityType' does not have any bundles.", [':entityType' => $entityTypeId]) - ); - } - - foreach ($bundleInfo as $bundle => $data) { - $label = $this->input->getOption('show-machine-names') ? $bundle : $data['label']; - $choices[$bundle] = $label; - } - - if (!$this->input->isInteractive() || !$answer = $this->io()->choice('Bundle', $choices)) { - throw new \InvalidArgumentException(t('The bundle argument is required.')); - } - - return $answer; - } - protected function askCardinality(): int { $fieldType = $this->input->getOption('field-type'); diff --git a/src/Drupal/Commands/core/FieldDeleteCommands.php b/src/Drupal/Commands/core/FieldDeleteCommands.php new file mode 100644 index 0000000000..6186ab8c2f --- /dev/null +++ b/src/Drupal/Commands/core/FieldDeleteCommands.php @@ -0,0 +1,135 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + } + + /** + * Delete a field + * + * @command field:delete + * @aliases field-delete,fd + * + * @param string $entityType + * The machine name of the entity type + * @param string $bundle + * The machine name of the bundle + * + * @option field-name + * The machine name of the field + * + * @option show-machine-names + * Show machine names instead of labels in option lists. + * + * @usage drush field:delete + * Delete a field by answering the prompts. + * @usage drush field-delete taxonomy_term tag + * Delete a field and fill in the remaining information through prompts. + * @usage drush field-delete taxonomy_term tag --field-name=field_tag_label + * Delete a field in a completely non-interactive way. + * + * @version 11.0 + */ + public function delete(string $entityType, ?string $bundle = null, array $options = [ + 'field-name' => InputOption::VALUE_REQUIRED, + 'show-machine-names' => InputOption::VALUE_OPTIONAL, + ]): void + { + $this->validateEntityType($entityType); + + $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); + $this->validateBundle($entityType, $bundle); + + $fieldName = $this->input->getOption('field-name') ?? $this->askExisting($entityType, $bundle); + $this->input->setOption('field-name', $fieldName); + + /** @var FieldConfig[] $results */ + $results = $this->entityTypeManager + ->getStorage('field_config') + ->loadByProperties([ + 'field_name' => $fieldName, + 'entity_type' => $entityType, + 'bundle' => $bundle, + ]); + + $this->deleteFieldConfig(reset($results)); + + // Fields are purged on cron. However field module prevents disabling modules + // when field types they provided are used in a field until it is fully + // purged. In the case that a field has minimal or no content, a single call + // to field_purge_batch() will remove it from the system. Call this with a + // low batch limit to avoid administrators having to wait for cron runs when + // removing fields that meet this criteria. + field_purge_batch(10); + } + + protected function askExisting(string $entityType, string $bundle): string + { + $choices = []; + /** @var FieldConfigInterface[] $fieldConfigs */ + $fieldConfigs = $this->entityTypeManager + ->getStorage('field_config') + ->loadByProperties([ + 'entity_type' => $entityType, + 'bundle' => $bundle, + ]); + + foreach ($fieldConfigs as $fieldConfig) { + $label = $this->input->getOption('show-machine-names') + ? $fieldConfig->get('field_name') + : $fieldConfig->get('label'); + + $choices[$fieldConfig->get('field_name')] = $label; + } + + return $this->io()->choice('Choose a field to delete', $choices); + } + + protected function deleteFieldConfig(FieldConfigInterface $fieldConfig): void + { + $fieldStorage = $fieldConfig->getFieldStorageDefinition(); + $bundles = $this->entityTypeBundleInfo->getBundleInfo($fieldConfig->getTargetEntityTypeId()); + $bundleLabel = $bundles[$fieldConfig->getTargetBundle()]['label']; + + if ($fieldStorage && !$fieldStorage->isLocked()) { + $fieldConfig->delete(); + + // If there is only one bundle left for this field storage, it will be + // deleted too, notify the user about dependencies. + if (count($fieldStorage->getBundles()) <= 1) { + $fieldStorage->delete(); + } + + $message = 'The field :field has been deleted from the :type content type.'; + } else { + $message = 'There was a problem removing the :field from the :type content type.'; + } + + $this->logger()->success( + t($message, [':field' => $fieldConfig->label(), ':type' => $bundleLabel]) + ); + } +} diff --git a/src/Drupal/Commands/core/drush.services.yml b/src/Drupal/Commands/core/drush.services.yml index e975013360..2aaac6c711 100644 --- a/src/Drupal/Commands/core/drush.services.yml +++ b/src/Drupal/Commands/core/drush.services.yml @@ -42,6 +42,13 @@ services: - '@entity_type.bundle.info' tags: - { name: drush.command } + field.delete.commands: + class: \Drush\Drupal\Commands\core\FieldDeleteCommands + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + tags: + - { name: drush.command } link.hooks: class: \Drush\Drupal\Commands\core\LinkHooks arguments: From 12e7564d3b555e6840639258503b788cd1a4c436 Mon Sep 17 00:00:00 2001 From: Dieter Holvoet Date: Sun, 12 Dec 2021 20:09:11 +0100 Subject: [PATCH 2/6] Add validation for the field-name option --- src/Drupal/Commands/core/FieldDeleteCommands.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Drupal/Commands/core/FieldDeleteCommands.php b/src/Drupal/Commands/core/FieldDeleteCommands.php index 6186ab8c2f..d818acb8a4 100644 --- a/src/Drupal/Commands/core/FieldDeleteCommands.php +++ b/src/Drupal/Commands/core/FieldDeleteCommands.php @@ -66,6 +66,12 @@ public function delete(string $entityType, ?string $bundle = null, array $option $fieldName = $this->input->getOption('field-name') ?? $this->askExisting($entityType, $bundle); $this->input->setOption('field-name', $fieldName); + if ($fieldName === '') { + throw new \InvalidArgumentException(dt('The %optionName option is required.', [ + '%optionName' => 'field-name', + ])); + } + /** @var FieldConfig[] $results */ $results = $this->entityTypeManager ->getStorage('field_config') From 09f0ffc1a633d1e46785783771c4cc8b0d730657 Mon Sep 17 00:00:00 2001 From: Dieter Holvoet Date: Sun, 12 Dec 2021 20:43:13 +0100 Subject: [PATCH 3/6] Validate whether field-name option is a valid field --- src/Drupal/Commands/core/FieldDeleteCommands.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Drupal/Commands/core/FieldDeleteCommands.php b/src/Drupal/Commands/core/FieldDeleteCommands.php index d818acb8a4..2eb89cb681 100644 --- a/src/Drupal/Commands/core/FieldDeleteCommands.php +++ b/src/Drupal/Commands/core/FieldDeleteCommands.php @@ -81,6 +81,15 @@ public function delete(string $entityType, ?string $bundle = null, array $option 'bundle' => $bundle, ]); + if ($results === []) { + throw new \InvalidArgumentException( + t("Field with name ':fieldName' does not exist on bundle ':bundle'.", [ + ':fieldName' => $fieldName, + ':bundle' => $bundle, + ]) + ); + } + $this->deleteFieldConfig(reset($results)); // Fields are purged on cron. However field module prevents disabling modules From ec31865b52e5db1cb4a41bf99263cc06919f0de9 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Wed, 15 Dec 2021 23:29:09 -0500 Subject: [PATCH 4/6] Add tests. --- .../Commands/core/FieldDeleteCommands.php | 4 ++-- tests/functional/FieldCreateTest.php | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Drupal/Commands/core/FieldDeleteCommands.php b/src/Drupal/Commands/core/FieldDeleteCommands.php index 2eb89cb681..1941fc89e9 100644 --- a/src/Drupal/Commands/core/FieldDeleteCommands.php +++ b/src/Drupal/Commands/core/FieldDeleteCommands.php @@ -49,7 +49,7 @@ public function __construct( * @usage drush field-delete taxonomy_term tag * Delete a field and fill in the remaining information through prompts. * @usage drush field-delete taxonomy_term tag --field-name=field_tag_label - * Delete a field in a completely non-interactive way. + * Delete a field in a non-interactive way. * * @version 11.0 */ @@ -138,7 +138,7 @@ protected function deleteFieldConfig(FieldConfigInterface $fieldConfig): void $fieldStorage->delete(); } - $message = 'The field :field has been deleted from the :type content type.'; + $message = 'The field :field has been deleted from the :type bundle.'; } else { $message = 'There was a problem removing the :field from the :type content type.'; } diff --git a/tests/functional/FieldCreateTest.php b/tests/functional/FieldCreateTest.php index ca676305af..584295abff 100644 --- a/tests/functional/FieldCreateTest.php +++ b/tests/functional/FieldCreateTest.php @@ -31,7 +31,7 @@ public function testFieldCreate() $this->drush('field:create', ['foo'], [], null, null, self::EXIT_ERROR); $this->assertStringContainsString('Entity type with id \'foo\' does not exist.', $this->getErrorOutputRaw()); $this->drush('field:create', ['user'], [], null, null, self::EXIT_ERROR); - $this->assertStringNotContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); + $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); $this->drush('field:create', ['user', 'user'], [], null, null, self::EXIT_ERROR); $this->assertStringNotContainsString('bundle', $this->getErrorOutputRaw()); @@ -76,4 +76,20 @@ public function testFieldInfo() $this->assertFalse($json['translatable']); $this->assertArrayHasKey('beta', $json['target_bundles']); } -} + + public function testFieldDelete() + { + $this->drush('field:delete', ['user'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); + $this->drush('field:delete', ['user', 'user'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The field-name option is required.', $this->getErrorOutputRaw()); + + $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test5', 'field-description' => 'baz', 'field-type' => 'entity_reference', 'is-required' => true, 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']); + $this->assertStringContainsString("Successfully created field 'field_test5' on unish_article type with bundle 'alpha'", $this->getSimplifiedErrorOutput()); + + $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_testZZZZZ'], null, null, self::EXIT_ERROR); + $this->assertStringContainsString("Field with name 'field_testZZZZZ' does not exist on bundle 'alpha'", $this->getErrorOutputRaw()); + $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_test5']); + $this->assertStringContainsString(" The field Test has been deleted from the Alpha bundle.", $this->getErrorOutputRaw()); + } +} \ No newline at end of file From 420af5e33deab8812373f004fc13f7197482464f Mon Sep 17 00:00:00 2001 From: Dieter Holvoet Date: Thu, 16 Dec 2021 09:52:18 +0100 Subject: [PATCH 5/6] Make entity type argument optional --- ...Trait.php => EntityTypeBundleAskTrait.php} | 20 ++++++++++++++++++- ...hp => EntityTypeBundleValidationTrait.php} | 2 +- .../Commands/core/FieldCreateCommands.php | 7 ++++--- .../Commands/core/FieldDeleteCommands.php | 16 ++++++++++++--- .../Commands/core/FieldInfoCommands.php | 7 ++++--- 5 files changed, 41 insertions(+), 11 deletions(-) rename src/Drupal/Commands/core/{AskBundleTrait.php => EntityTypeBundleAskTrait.php} (68%) rename src/Drupal/Commands/core/{ValidateEntityTypeTrait.php => EntityTypeBundleValidationTrait.php} (97%) diff --git a/src/Drupal/Commands/core/AskBundleTrait.php b/src/Drupal/Commands/core/EntityTypeBundleAskTrait.php similarity index 68% rename from src/Drupal/Commands/core/AskBundleTrait.php rename to src/Drupal/Commands/core/EntityTypeBundleAskTrait.php index 1e692474fd..3a2700ada6 100644 --- a/src/Drupal/Commands/core/AskBundleTrait.php +++ b/src/Drupal/Commands/core/EntityTypeBundleAskTrait.php @@ -11,8 +11,26 @@ * @property EntityTypeBundleInfoInterface $entityTypeBundleInfo * @property EntityTypeManagerInterface $entityTypeManager */ -trait AskBundleTrait +trait EntityTypeBundleAskTrait { + protected function askEntityType(): ?string + { + $entityTypeDefinitions = $this->entityTypeManager->getDefinitions(); + $choices = []; + + foreach ($entityTypeDefinitions as $entityTypeDefinition) { + $choices[$entityTypeDefinition->id()] = $this->input->getOption('show-machine-names') + ? $entityTypeDefinition->id() + : $entityTypeDefinition->getLabel(); + } + + if (!$answer = $this->io()->choice('Entity type', $choices)) { + throw new \InvalidArgumentException(t('The entityType argument is required.')); + } + + return $answer; + } + protected function askBundle(): ?string { $entityTypeId = $this->input->getArgument('entityType'); diff --git a/src/Drupal/Commands/core/ValidateEntityTypeTrait.php b/src/Drupal/Commands/core/EntityTypeBundleValidationTrait.php similarity index 97% rename from src/Drupal/Commands/core/ValidateEntityTypeTrait.php rename to src/Drupal/Commands/core/EntityTypeBundleValidationTrait.php index 7c968efc30..d50c4b5b18 100644 --- a/src/Drupal/Commands/core/ValidateEntityTypeTrait.php +++ b/src/Drupal/Commands/core/EntityTypeBundleValidationTrait.php @@ -7,7 +7,7 @@ /** * @property EntityTypeManagerInterface $entityTypeManager */ -trait ValidateEntityTypeTrait +trait EntityTypeBundleValidationTrait { protected function validateEntityType(string $entityTypeId): void { diff --git a/src/Drupal/Commands/core/FieldCreateCommands.php b/src/Drupal/Commands/core/FieldCreateCommands.php index f026368f2d..38bad23c33 100644 --- a/src/Drupal/Commands/core/FieldCreateCommands.php +++ b/src/Drupal/Commands/core/FieldCreateCommands.php @@ -26,9 +26,9 @@ class FieldCreateCommands extends DrushCommands implements CustomEventAwareInterface { - use AskBundleTrait; + use EntityTypeBundleAskTrait; use CustomEventAwareTrait; - use ValidateEntityTypeTrait; + use EntityTypeBundleValidationTrait; /** @var FieldTypePluginManagerInterface */ protected $fieldTypePluginManager; @@ -120,7 +120,7 @@ public function setContentTranslationManager(ContentTranslationManagerInterface * @see \Drupal\field_ui\Form\FieldConfigEditForm * @see \Drupal\field_ui\Form\FieldStorageConfigEditForm */ - public function create(string $entityType, ?string $bundle = null, array $options = [ + public function create(?string $entityType = null, ?string $bundle = null, array $options = [ 'field-name' => InputOption::VALUE_REQUIRED, 'field-label' => InputOption::VALUE_REQUIRED, 'field-description' => InputOption::VALUE_OPTIONAL, @@ -136,6 +136,7 @@ public function create(string $entityType, ?string $bundle = null, array $option 'existing' => false, ]): void { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); $this->validateEntityType($entityType); $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); diff --git a/src/Drupal/Commands/core/FieldDeleteCommands.php b/src/Drupal/Commands/core/FieldDeleteCommands.php index 1941fc89e9..888f74a82b 100644 --- a/src/Drupal/Commands/core/FieldDeleteCommands.php +++ b/src/Drupal/Commands/core/FieldDeleteCommands.php @@ -11,8 +11,8 @@ class FieldDeleteCommands extends DrushCommands { - use AskBundleTrait; - use ValidateEntityTypeTrait; + use EntityTypeBundleAskTrait; + use EntityTypeBundleValidationTrait; /** @var EntityTypeManagerInterface */ protected $entityTypeManager; @@ -52,12 +52,14 @@ public function __construct( * Delete a field in a non-interactive way. * * @version 11.0 + * @see \Drupal\field_ui\Form\FieldConfigDeleteForm */ - public function delete(string $entityType, ?string $bundle = null, array $options = [ + public function delete(?string $entityType = null, ?string $bundle = null, array $options = [ 'field-name' => InputOption::VALUE_REQUIRED, 'show-machine-names' => InputOption::VALUE_OPTIONAL, ]): void { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); $this->validateEntityType($entityType); $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); @@ -120,6 +122,14 @@ protected function askExisting(string $entityType, string $bundle): string $choices[$fieldConfig->get('field_name')] = $label; } + if ($choices === []) { + throw new \InvalidArgumentException( + t("Bundle ':bundle' has no fields.", [ + ':bundle' => $bundle, + ]) + ); + } + return $this->io()->choice('Choose a field to delete', $choices); } diff --git a/src/Drupal/Commands/core/FieldInfoCommands.php b/src/Drupal/Commands/core/FieldInfoCommands.php index 8c962d23e0..5560ffcea7 100644 --- a/src/Drupal/Commands/core/FieldInfoCommands.php +++ b/src/Drupal/Commands/core/FieldInfoCommands.php @@ -10,9 +10,9 @@ class FieldInfoCommands extends DrushCommands { - use AskBundleTrait; + use EntityTypeBundleAskTrait; + use EntityTypeBundleValidationTrait; use FieldDefinitionRowsOfFieldsTrait; - use ValidateEntityTypeTrait; /** @var EntityTypeManagerInterface */ protected $entityTypeManager; @@ -66,10 +66,11 @@ public function __construct( * * @version 11.0 */ - public function info(string $entityType, ?string $bundle = null, array $options = [ + public function info(?string $entityType = null, ?string $bundle = null, array $options = [ 'format' => 'table', ]): RowsOfFields { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); $this->validateEntityType($entityType); $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); From 2baa05f330d9773c6da3669607c5f5c2d2189fae Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 16 Dec 2021 07:33:10 -0500 Subject: [PATCH 6/6] Update tests and fix PHPCS --- tests/functional/FieldCreateTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/FieldCreateTest.php b/tests/functional/FieldCreateTest.php index 584295abff..07e6af8975 100644 --- a/tests/functional/FieldCreateTest.php +++ b/tests/functional/FieldCreateTest.php @@ -27,7 +27,7 @@ public function testFieldCreate() { // Arguments. $this->drush('field:create', [], [], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('Not enough arguments (missing: "entityType")', $this->getErrorOutputRaw()); + $this->assertStringContainsString('The entityType argument is required', $this->getErrorOutputRaw()); $this->drush('field:create', ['foo'], [], null, null, self::EXIT_ERROR); $this->assertStringContainsString('Entity type with id \'foo\' does not exist.', $this->getErrorOutputRaw()); $this->drush('field:create', ['user'], [], null, null, self::EXIT_ERROR); @@ -79,17 +79,17 @@ public function testFieldInfo() public function testFieldDelete() { - $this->drush('field:delete', ['user'], [], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); - $this->drush('field:delete', ['user', 'user'], [], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('The field-name option is required.', $this->getErrorOutputRaw()); - $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test5', 'field-description' => 'baz', 'field-type' => 'entity_reference', 'is-required' => true, 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']); $this->assertStringContainsString("Successfully created field 'field_test5' on unish_article type with bundle 'alpha'", $this->getSimplifiedErrorOutput()); + $this->drush('field:delete', ['unish_article'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); + $this->drush('field:delete', ['unish_article', 'alpha'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The field-name option is required.', $this->getErrorOutputRaw()); + $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_testZZZZZ'], null, null, self::EXIT_ERROR); $this->assertStringContainsString("Field with name 'field_testZZZZZ' does not exist on bundle 'alpha'", $this->getErrorOutputRaw()); $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_test5']); $this->assertStringContainsString(" The field Test has been deleted from the Alpha bundle.", $this->getErrorOutputRaw()); } -} \ No newline at end of file +}