Skip to content

Commit

Permalink
Add field:info command (#4928)
Browse files Browse the repository at this point in the history
* Add field:info command

* Move formatting code to trait

* Fix dependency injection

* Add tests.

* change name back. try to avoid conflict

* phpcs

Co-authored-by: Moshe Weitzman <weitzman@tejasa.com>
  • Loading branch information
DieterHolvoet and weitzman committed Dec 15, 2021
1 parent cb2434e commit 5794cdd
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 2 deletions.
45 changes: 45 additions & 0 deletions src/Drupal/Commands/core/AskBundleTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Drush\Drupal\Commands\core;

use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\Console\Input\InputInterface;

/**
* @property InputInterface $input
* @property EntityTypeBundleInfoInterface $entityTypeBundleInfo
* @property EntityTypeManagerInterface $entityTypeManager
*/
trait AskBundleTrait
{
protected function askBundle(): ?string
{
$entityTypeId = $this->input->getArgument('entityType');
$entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId);
$bundleEntityType = $entityTypeDefinition->getBundleEntityType();
$bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entityTypeId);
$choices = [];

if (empty($bundleInfo)) {
if ($bundleEntityType) {
throw new \InvalidArgumentException(
t('Entity type with id \':entityType\' does not have any bundles.', [':entityType' => $entityTypeId])
);
}

return null;
}

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;
}
}
59 changes: 59 additions & 0 deletions src/Drupal/Commands/core/FieldDefinitionRowsOfFieldsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Drush\Drupal\Commands\core;

use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;

trait FieldDefinitionRowsOfFieldsTrait
{
public function renderArray($key, $value, FormatterOptions $options)
{
if (is_array($value)) {
return implode(', ', $value);
}

return $value;
}

public function renderBoolean($key, $value, FormatterOptions $options)
{
if (is_bool($value)) {
return $value ? '✔' : '';
}

return $value;
}

protected function getRowsOfFieldsByFieldDefinitions(array $fieldDefinitions): RowsOfFields
{
$rows = [];

foreach ($fieldDefinitions as $field) {
$storage = $field->getFieldStorageDefinition();
$handlerSettings = $field->getSetting('handler_settings');

$rows[$field->getName()] = [
'label' => $field->getLabel(),
'description' => $field->getDescription(),
'field_name' => $field->getName(),
'field_type' => $field->getType(),
'required' => $field->isRequired(),
'translatable' => $field->isTranslatable(),
'cardinality' => $storage->getCardinality(),
'default_value' => empty($field->getDefaultValueLiteral()) ? null : $field->getDefaultValueLiteral(),
'default_value_callback' => $field->getDefaultValueCallback(),
'allowed_values' => $storage->getSetting('allowed_values'),
'allowed_values_function' => $storage->getSetting('allowed_values_function'),
'handler' => $field->getSetting('handler'),
'target_bundles' => $handlerSettings['target_bundles'] ?? null,
];
}

$result = new RowsOfFields($rows);
$result->addRendererFunction([$this, 'renderArray']);
$result->addRendererFunction([$this, 'renderBoolean']);

return $result;
}
}
87 changes: 87 additions & 0 deletions src/Drupal/Commands/core/FieldInfoCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Drush\Drupal\Commands\core;

use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Core\Entity\EntityTypeBundleInfo;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drush\Commands\DrushCommands;

class FieldInfoCommands extends DrushCommands
{
use AskBundleTrait;
use FieldDefinitionRowsOfFieldsTrait;
use ValidateEntityTypeTrait;

/** @var EntityTypeManagerInterface */
protected $entityTypeManager;
/** @var EntityTypeBundleInfo */
protected $entityTypeBundleInfo;

public function __construct(
EntityTypeManagerInterface $entityTypeManager,
EntityTypeBundleInfo $entityTypeBundleInfo
) {
$this->entityTypeManager = $entityTypeManager;
$this->entityTypeBundleInfo = $entityTypeBundleInfo;
}

/**
* List all configurable fields of an entity bundle
*
* @command field:info
* @aliases field-info,fi
*
* @param string $entityType
* The machine name of the entity type
* @param string $bundle
* The machine name of the bundle
*
* @option show-machine-names
* Show machine names instead of labels in option lists.
*
* @default-fields field_name,required,field_type,cardinality
* @field-labels
* label: Label
* description: Description
* field_name: Field name
* field_type: Field type
* required: Required
* translatable: Translatable
* cardinality: Cardinality
* default_value: Default value
* default_value_callback: Default value callback
* allowed_values: Allowed values
* allowed_values_function: Allowed values function
* handler: Selection handler
* target_bundles: Target bundles
* @filter-default-field field_name
* @table-style default
*
* @usage drush field-info taxonomy_term tag
* List all fields.
* @usage drush field:info
* List all fields and fill in the remaining information through prompts.
*
* @version 11.0
*/
public function info(string $entityType, ?string $bundle = null, array $options = [
'format' => 'table',
]): RowsOfFields
{
$this->validateEntityType($entityType);

$this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle());
$this->validateBundle($entityType, $bundle);

$fieldDefinitions = $this->entityTypeManager
->getStorage('field_config')
->loadByProperties([
'entity_type' => $entityType,
'bundle' => $bundle,
]);

return $this->getRowsOfFieldsByFieldDefinitions($fieldDefinitions);
}
}
46 changes: 46 additions & 0 deletions src/Drupal/Commands/core/ValidateEntityTypeTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Drush\Drupal\Commands\core;

use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
* @property EntityTypeManagerInterface $entityTypeManager
*/
trait ValidateEntityTypeTrait
{
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,
])
);
}
}
}
7 changes: 7 additions & 0 deletions src/Drupal/Commands/core/drush.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ services:
- [ setContentTranslationManager, [ '@?content_translation.manager' ] ]
tags:
- { name: drush.command }
field.info.commands:
class: \Drush\Drupal\Commands\core\FieldInfoCommands
arguments:
- '@entity_type.manager'
- '@entity_type.bundle.info'
tags:
- { name: drush.command }
link.hooks:
class: \Drush\Drupal\Commands\core\LinkHooks
arguments:
Expand Down
23 changes: 21 additions & 2 deletions tests/functional/FieldCreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Unish;

use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Webmozart\PathUtil\Path;

/**
Expand Down Expand Up @@ -35,12 +36,12 @@ public function testFieldCreate()
$this->assertStringNotContainsString('bundle', $this->getErrorOutputRaw());

// New field storage
$this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test2', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1'], null, null, self::EXIT_ERROR);
$this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test2', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED], null, null, self::EXIT_ERROR);
$this->assertStringContainsString('The target-type option is required.', $this->getErrorOutputRaw());
/// @todo --target-bundle not yet validated.
// $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1', 'target-type' => 'unish_article', 'target-bundle' => 'NO-EXIST']);
// $this->assertStringContainsString('TODO', $this->getErrorOutputRaw());
$this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1', 'target-type' => 'unish_article', 'target-bundle' => 'beta']);
$this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']);
$this->assertStringContainsString("Successfully created field 'field_test3' on unish_article type with bundle 'alpha'", $this->getErrorOutputRaw());
$this->assertStringContainsString("http://dev/admin/structure/unish_article_types/manage/alpha/fields/unish_article.alpha.field_test3", $this->getSimplifiedErrorOutput());
$php = "return Drupal::entityTypeManager()->getStorage('field_config')->load('unish_article.alpha.field_test3')->getSettings()";
Expand All @@ -57,4 +58,22 @@ public function testFieldCreate()
$this->drush('field:create', ['unish_article', 'beta'], ['existing-field-name' => 'field_test3', 'field-label' => 'Body', 'field-widget' => 'text_textarea_with_summary'], null, null, self::EXIT_ERROR);
$this->assertStringContainsString('Field with name \'field_test3\' already exists on bundle \'beta\'', $this->getErrorOutputRaw());
}

public function testFieldInfo()
{
$this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test4', '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_test4' on unish_article type with bundle 'alpha'", $this->getSimplifiedErrorOutput());

$this->drush('field:info', ['unish_article'], [], null, null, self::EXIT_ERROR);
$this->assertStringContainsString('The bundle argument is required.', $this->getSimplifiedErrorOutput());
$this->drush('field:info', ['unish_article', 'alpha'], ['format' => 'json', 'fields' => '*']);
$json = $this->getOutputFromJSON('field_test4');
$this->assertSame('field_test4', $json['field_name']);
$this->assertTrue($json['required']);
$this->assertSame('entity_reference', $json['field_type']);
$this->assertSame('baz', $json['description']);
$this->assertSame(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, $json['cardinality']);
$this->assertFalse($json['translatable']);
$this->assertArrayHasKey('beta', $json['target_bundles']);
}
}

0 comments on commit 5794cdd

Please sign in to comment.