Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bundle classes generator #4903

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Drupal/Commands/core/drush.services.yml
Expand Up @@ -21,6 +21,11 @@ services:
arguments: ['@entity_type.manager']
tags:
- { name: drush.command }
entity.generators:
class: Drush\Drupal\Generators\entity\EntityBundleClassesGenerator
arguments: ['@entity_type.bundle.info', '@entity_type.manager']
tags:
- { name: drush.generator.v2 }
image.commands:
class: \Drush\Drupal\Commands\core\ImageCommands
tags:
Expand Down
67 changes: 67 additions & 0 deletions src/Drupal/Generators/entity/EntityBundleClassesGenerator.php
@@ -0,0 +1,67 @@
<?php
namespace Drush\Drupal\Generators\entity;

use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use DrupalCodeGenerator\Command\ModuleGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Question\ChoiceQuestion;

class EntityBundleClassesGenerator extends ModuleGenerator
{
protected string $name = 'entity:bundle-classes';
protected string $description = 'Generate a bundle class for each content entity.';
protected string $alias = 'ebc';
protected string $templatePath = __DIR__;

protected $bundleInfo;
protected $entityTypeManager;

public function __construct(EntityTypeBundleInfoInterface $bundleInfo, EntityTypeManagerInterface $entityTypeManager)
{
parent::__construct($this->name);
$this->bundleInfo = $bundleInfo;
$this->entityTypeManager = $entityTypeManager;
}

/**
* {@inheritdoc}
*/
protected function generate(array &$vars): void
{
$this->collectDefault($vars);
$vars['bundle_info'] = $this->bundleInfo->getAllBundleInfo();
$definitions = $this->entityTypeManager->getDefinitions();
$vars['entity_types'] = array_filter($definitions, [$this, 'isContentEntity']);
$choices = array_keys($vars['entity_types']);
$question = new ChoiceQuestion('Entity type(s). Use comma to delimit.', $choices, 'node');
$question->setValidator([static::class, 'validateRequired']);
$question->setMultiselect(true);
$vars['entity_type_ids'] = $this->io->askQuestion($question);
$this->addFile($vars['machine_name'] . '.module', 'hook_bundle_info.php')
// @todo When we require 2.1, use https://getcomposer.org/doc/07-runtime.md#installed-versions
// to get path to DCG so we use its templates/_lib/file-docs/module.twig instead of a copy of that file.
->headerTemplate('module.twig')
->appendIfExists()
->headerSize(7);
$vars['use_base_class'] = $this->confirm('Generate a base class? Respond no if you can easily modify the entity class.');
foreach ($vars['entity_type_ids'] as $id) {
$vars['entity_class'] = $vars['parent_class'] = $this->entityTypeManager->getStorage($id)->getEntityClass();
$vars['entity_type_id'] = $id;
if ($vars['use_base_class']) {
$base_class = $vars['base_class'] = $vars['parent_class'] = Utils::camelize($id . 'BundleBase');
$this->addFile("src/Entity/Bundle/$id/${base_class}.php", 'base_bundle_class.php.twig')->vars($vars);
}
foreach ($vars['bundle_info'][$id] as $bundle => $info) {
$bundle_class = $vars['bundle_class'] = Utils::camelize($bundle);
$this->addFile("src/Entity/Bundle/$id/${bundle_class}.php", 'bundle_class.php.twig')->vars($vars);
}
}
$this->logger->warning('Run `drush cache:rebuild` so the bundle classes are recognized.');
}

protected function isContentEntity($definition)
{
return $definition->getGroup() == 'content';
}
}
11 changes: 11 additions & 0 deletions src/Drupal/Generators/entity/base_bundle_class.php.twig
@@ -0,0 +1,11 @@
<?php
namespace Drupal\{{ machine_name }}\Entity\Bundle\{{ entity_type_id }};

use {{ entity_class }};

/**
* A base bundle class for {{ entity_type_id }} entities.
*/
abstract class {{ base_class }} extends {{ entity_class|split('\\')|last }} {

}
13 changes: 13 additions & 0 deletions src/Drupal/Generators/entity/bundle_class.php.twig
@@ -0,0 +1,13 @@
<?php
namespace Drupal\{{ machine_name }}\Entity\Bundle\{{ entity_type_id }};
{% if not use_base_class %}

use {{ parent_class }};
{% endif %}

/**
* A bundle class for {{ entity_type_id }} entities.
*/
class {{ bundle_class }} extends {{ parent_class|split('\\')|last }} {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, we should encourage bundle classes to implement bundle interfaces. A Page bundle class could implement PageInterface. This is very useful, as a bundle class can implement multiple interfaces:

interface PageInterface {
  public function getContent();
}

interface ArchivableInterface {
  public function getArchivedDate();
}

class Page implements PageInterface, ArchivableInterface {
}

also this allows to do some more semantically checks:

if ($node instanceof PageInterface) {...}


}
10 changes: 10 additions & 0 deletions src/Drupal/Generators/entity/hook_bundle_info.php.twig
@@ -0,0 +1,10 @@
/**
* Implements hook_entity_bundle_info_alter().
*/
function drush_empty_module_entity_bundle_info_alter(array &$bundles): void {
{% for id in entity_type_ids %}
{% for bundle in bundle_info[id]|keys %}
$bundles['{{ id }}']['{{ bundle }}']['class'] = \Drupal\{{ machine_name }}\Entity\Bundle\{{ id }}\{{ bundle|camelize }}::class;
{% endfor %}
{% endfor %}
}
6 changes: 6 additions & 0 deletions src/Drupal/Generators/entity/module.twig
@@ -0,0 +1,6 @@
<?php

/**
* @file
* Primary module hooks for {{ machine_name }} module.
*/