Skip to content
Snippets Groups Projects
Commit ef795fe6 authored by Mateu Aguiló Bosch's avatar Mateu Aguiló Bosch
Browse files

Issue #3204147 by e0ipso: Create a Typed Entity UI submodule to help

parent 0f8c7992
No related branches found
No related tags found
1 merge request!5Issue #3204147: Create a Typed Entity UI submodule to help
Showing
with 434 additions and 1 deletion
......@@ -3,4 +3,4 @@ description: An example implementation of the typed entity strategy.
type: module
dependencies:
- typed_entity
core: 8.x
core_version_requirement: ^8 || ^9
.php-class-info {
background-color: rgba(20, 127, 255, 0.3);
padding: 0.02rem 0.5em;
border-radius: 3px;
}
.php-class-info code.className {
background-color: rgba(0, 0, 0, 0.2);
padding: 2px 6px;
border-radius: 3px;
}
<?php
namespace Drupal\typed_entity_ui\Controller;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\typed_entity\RepositoryManager;
use Drupal\typed_entity\TypedRepositories\TypedEntityRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ExploreDetails extends ControllerBase {
/**
* The entity type manager to manage entity type plugin definitions.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity type bundle service to discover & retrieve entity type bundles.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* The repository manager.
*
* @var \Drupal\typed_entity\RepositoryManager
*/
protected $repositoryManager;
/**
* Constructs a new EntityBundlePicker form.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
* The entity type bundle info service for discovering entity type bundles.
* @param \Drupal\typed_entity\RepositoryManager $repository_manager
* The repository manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_info, RepositoryManager $repository_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->bundleInfo = $bundle_info;
$this->repositoryManager = $repository_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$container->get(RepositoryManager::class)
);
}
/**
* Set the page title.
*
* @param string $typed_entity_id
* The typed entity ID.
*
* @return \Drupal\Component\Render\MarkupInterface
* The title.
*/
public function title(string $typed_entity_id): MarkupInterface {
[$entity_type_label, $bundle_label] = $this->getLabels($typed_entity_id);
if ($bundle_label) {
return $this->t('Explore typed entity: %bundle (%type)', ['%type' => $entity_type_label, '%bundle' => $bundle_label]);
}
return $this->t('Explore typed entity: %type', ['%type' => $entity_type_label]);
}
/**
* Handles the request.
*
* @param string $typed_entity_id
* The typed entity ID.
*
* @return array
* The render array.
*/
public function __invoke(string $typed_entity_id): array {
[$entity_type_id, $bundle] = explode('.', $typed_entity_id);
$repository = $this->repositoryManager->repository($entity_type_id, $bundle ?? '');
if (!$repository instanceof TypedEntityRepositoryInterface) {
return $this->getNotFoundOutput($typed_entity_id);
}
$reflection_repository = new \ReflectionClass($repository);
return [
[
'#type' => 'html_tag',
'#tag' => 'h2',
'#value' => $this->t('Typed Entity Repository'),
],
[
'#theme' => 'php_class_info',
'#reflection' => $reflection_repository,
],
];
}
/**
* Extract the entity type and bundle labels from the typed entity ID.
*
* @param string $typed_entity_id
* The ID.
*
* @return array
* The labels.
*/
protected function getLabels(string $typed_entity_id): array {
[$entity_type_id, $bundle] = explode('.', $typed_entity_id);
try {
$entity_type_label = $this->entityTypeManager->getDefinition($entity_type_id)->getLabel();
}
catch (PluginNotFoundException $exception) {
return [];
}
if ($bundle) {
$bundle_label = $this->bundleInfo->getBundleInfo($entity_type_id)[$bundle]['label'] ?? '';
return [$entity_type_label, $bundle_label];
}
return [$entity_type_label];
}
/**
* Output when there is no typed entity repository associated.
*
* @param string $typed_entity_id
* The typed entity ID.
*
* @return array
* The render array.
*/
protected function getNotFoundOutput(string $typed_entity_id): array {
[$entity_type_label, $bundle_label] = $this->getLabels($typed_entity_id);
return [
[
'#type' => 'html_tag',
'#tag' => 'h2',
'#value' => $this->t('Not found'),
],
[
'#type' => 'html_tag',
'#tag' => 'p',
'#value' => $this->t('Unable to find a repository for %type (%bundle).', [
'%type' => $entity_type_label,
'%bundle' => $bundle_label ?? $entity_type_label,
]),
],
[
'#type' => 'html_tag',
'#tag' => 'p',
'#value' => $this->t('See the <a href=:docs>documentation</a> to learn how to associate a typed entity repository to type of entity.', [
':docs' => 'https://www.drupal.org/typed_entity',
]),
],
];
}
}
<?php
namespace Drupal\typed_entity_ui\Form;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ExploreForm extends FormBase {
/**
* The entity type manager to manage entity type plugin definitions.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity type bundle service to discover & retrieve entity type bundles.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* Constructs a new Explore form form.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundleInfo
* The entity type bundle info service for discovering entity type bundles.
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager, EntityTypeBundleInfoInterface $bundleInfo) {
$this->entityTypeManager = $entityTypeManager;
$this->bundleInfo = $bundleInfo;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'typed_entity_ui_explore';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$content_entity_types = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
return $entity_type instanceof ContentEntityTypeInterface;
});
$entity_types = array_reduce($content_entity_types, function ($carry, EntityTypeInterface $entity_type) {
$carry[$entity_type->id()] = $entity_type->getLabel();
return $carry;
}, []);
$form['entity_type_id'] = [
'#title' => $this->t('Entity Type'),
'#type' => 'select',
'#options' => $entity_types,
'#empty_option' => $this->t('- Select -'),
'#required' => TRUE,
'#ajax' => [
'callback' => '::bundleCallback',
'wrapper' => 'bundle-wrapper',
],
];
// Disable caching on this form.
$form_state->setCached(FALSE);
$form['actions'] = [
'#type' => 'actions',
];
// Add a submit button that handles the submission of the form.
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
$form['bundle_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'bundle-wrapper'],
];
if ($entity_type_id = $form_state->getValue('entity_type_id')) {
$has_bundles = (bool) $this->entityTypeManager
->getDefinition($entity_type_id)->getBundleEntityType();
if ($has_bundles) {
$bundles = [];
$bundle_info = $this->bundleInfo->getBundleInfo($entity_type_id);
foreach ($bundle_info as $bundle_id => $info) {
$bundles[$bundle_id] = $info['translatable']
? $this->t($info['label'])
: $info['label'];
}
// Add a color element to the bundle_wrapper container with the bundles
// for a given entity type.
$form['bundle_wrapper']['bundle'] = [
'#type' => 'select',
'#empty_option' => $this->t('- Select -'),
'#title' => $this->t('Bundle'),
'#options' => $bundles,
];
}
}
return $form;
}
/**
* Implements callback for Ajax event on entity type selection.
*
* @param array $form
* From render array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current state of form.
*
* @return array
* Color selection section of the form.
*/
public function bundleCallback(array &$form, FormStateInterface $form_state) {
return $form['bundle_wrapper'];
}
/**
* Implements a form submit handler.
*
* @param array $form
* The render array of the currently built form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Object describing the current state of the form.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$typed_entity_id = implode('.', array_filter([$form_state->getValue('entity_type_id'), $form_state->getValue('bundle')]));
$form_state->setRedirect(
'typed_entity_ui.details',
['typed_entity_id' => $typed_entity_id]
);
}
}
{{ attach_library('typed_entity_ui/php_class_info') }}
<div{{ attributes }}>
<h3>{{ 'Class'|t }}</h3>
<code class="className">{{ name }}</code>
<h4>{{ 'Documentation'|t }}</h4>
<code>{{ doc_comment }}</code>
{% if parent %}
<h4>{{ 'Parent'|t }}</h4>
<code class="className">{{ parent }}</code>
{% endif %}
{{ interfaces }}
</div>
name: Typed Entity UI
description: Adds a helper UI to the Typed Entity module to understand class hierarchy.
type: module
core_version_requirement: ^8 || ^9
php_class_info:
css:
theme:
css/typed_entity_ui.php_class_info.css: {}
typed_entity_ui.explore:
title: 'Explore Typed Entity'
route_name: typed_entity_ui.explore
description: 'Explore the classes associated with each entity type and bundle pair.'
parent: system.admin_config_development
<?php
/**
* @file
* Module implementation file.
*/
/**
* Implements hook_theme().
*/
function typed_entity_ui_theme($existing, $type, $theme, $path) {
return [
'php_class_info' => [
'variables' => [
'reflection' => NULL,
],
],
];
}
/**
* Prepares variables for environment indicator element templates.
*
* Default template: environment-indicator.html.twig.
*
* @param array $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #description, #required, #attributes.
*/
function template_preprocess_php_class_info(array &$variables) {
$reflection = $variables['reflection'];
assert($reflection instanceof ReflectionClass);
$variables['name'] = $reflection->getName();
$variables['parent'] = $reflection->getParentClass() ? $reflection->getParentClass()
->getName() : '';
$variables['interfaces'] = [
'#title' => t('Interfaces'),
'#theme' => 'item_list',
'#items' => array_map(function (string $interface) {
return [
'#type' => 'html_tag',
'#tag' => 'code',
'#value' => $interface,
'#attributes' => ['class' => ['className']],
];
}, array_keys($reflection->getInterfaces())),
];
$variables['doc_comment'] = $reflection->getDocComment();
$variables['attributes'] = ['class' => ['php-class-info']];
}
explore typed entity classes:
title: 'Explore typed entity classes'
typed_entity_ui.explore:
path: '/admin/config/development/typed-entity'
defaults:
_form: '\Drupal\typed_entity_ui\Form\ExploreForm'
_title: 'Explore'
requirements:
_permission: 'explore typed entity classes'
typed_entity_ui.details:
path: '/admin/config/development/typed-entity/{typed_entity_id}'
defaults:
_controller: '\Drupal\typed_entity_ui\Controller\ExploreDetails'
_title_callback: '\Drupal\typed_entity_ui\Controller\ExploreDetails::title'
requirements:
_permission: 'explore typed entity classes'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment