-
Notifications
You must be signed in to change notification settings - Fork 13
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
Issue #2817751 by bojanz: Create an API for bundle plugins #49
Changes from all commits
459c33b
57f7931
9ac0ff3
f1a244b
f95c543
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace Drupal\entity; | ||
|
||
use Drupal\Core\Field\BaseFieldDefinition; | ||
|
||
/** | ||
* Provides a field definition class for bundle fields. | ||
* | ||
* Core currently doesn't provide one, the hook_entity_bundle_field_info() | ||
* example uses BaseFieldDefinition, which is wrong. Tracked in #2346347. | ||
* | ||
* Note that this class implements both FieldStorageDefinitionInterface and | ||
* FieldDefinitionInterface. This is a simplification for DX reasons, | ||
* allowing code to return just the bundle definitions instead of having to | ||
* return both storage definitions and bundle definitions. | ||
*/ | ||
class BundleFieldDefinition extends BaseFieldDefinition { | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isBaseField() { | ||
return FALSE; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?php | ||
|
||
namespace Drupal\entity\BundlePlugin; | ||
|
||
use Drupal\Component\Plugin\PluginManagerInterface; | ||
use Drupal\Core\Entity\EntityTypeInterface; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
class BundlePluginHandler implements BundlePluginHandlerInterface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Class comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't planning on adding one. I don't see a point in a comment that doesn't add any new information, such as "Default implementation of the BundlePluginHandlerInterface." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
|
||
/** | ||
* The entity type. | ||
* | ||
* @var \Drupal\Core\Entity\EntityTypeInterface | ||
*/ | ||
protected $entityType; | ||
|
||
/** | ||
* The bundle plugin manager. | ||
* | ||
* @var \Drupal\Component\Plugin\PluginManagerInterface | ||
*/ | ||
protected $pluginManager; | ||
|
||
/** | ||
* Constructs a new BundlePluginHandler object. | ||
* | ||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | ||
* The entity type. | ||
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager | ||
* The bundle plugin manager. | ||
*/ | ||
public function __construct(EntityTypeInterface $entity_type, PluginManagerInterface $plugin_manager) { | ||
$this->entityType = $entity_type; | ||
$this->pluginManager = $plugin_manager; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { | ||
return new static( | ||
$entity_type, | ||
$container->get('plugin.manager.' . $entity_type->get('bundle_plugin_type')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we catch the exception here and maybe provide a helpful error message "you need to implement a plugin manager for your bundle type called '....'" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is overengineering. The backtrace will show what is going on |
||
); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getBundleInfo() { | ||
$bundles = []; | ||
foreach ($this->pluginManager->getDefinitions() as $plugin_id => $definition) { | ||
$bundles[$plugin_id] = [ | ||
'label' => $definition['label'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be feasible to add something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sounds like a nice idea! |
||
'description' => isset($definition['description']) ? $definition['description'] : '', | ||
'translatable' => $this->entityType->isTranslatable(), | ||
'provider' => $definition['provider'], | ||
]; | ||
} | ||
return $bundles; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getFieldStorageDefinitions() { | ||
$definitions = []; | ||
foreach (array_keys($this->pluginManager->getDefinitions()) as $plugin_id) { | ||
/** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */ | ||
$plugin = $this->pluginManager->createInstance($plugin_id); | ||
$definitions += $plugin->buildFieldDefinitions(); | ||
} | ||
// Ensure the presence of required keys which aren't set by the plugin. | ||
foreach ($definitions as $field_name => $definition) { | ||
$definition->setName($field_name); | ||
$definition->setTargetEntityTypeId($this->entityType->id()); | ||
$definitions[$field_name] = $definition; | ||
} | ||
|
||
return $definitions; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getFieldDefinitions($bundle) { | ||
/** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */ | ||
$plugin = $this->pluginManager->createInstance($bundle); | ||
$definitions = $plugin->buildFieldDefinitions(); | ||
// Ensure the presence of required keys which aren't set by the plugin. | ||
foreach ($definitions as $field_name => $definition) { | ||
$definition->setName($field_name); | ||
$definition->setTargetEntityTypeId($this->entityType->id()); | ||
$definition->setTargetBundle($bundle); | ||
$definitions[$field_name] = $definition; | ||
} | ||
|
||
return $definitions; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
namespace Drupal\entity\BundlePlugin; | ||
|
||
use Drupal\Core\Entity\EntityHandlerInterface; | ||
|
||
/** | ||
* Handles plugin-provided bundles. | ||
*/ | ||
interface BundlePluginHandlerInterface extends EntityHandlerInterface { | ||
|
||
/** | ||
* Gets the bundle info. | ||
* | ||
* @return array | ||
* An array of bundle information keyed by the bundle name. | ||
* The format expected by hook_entity_bundle_info(). | ||
*/ | ||
public function getBundleInfo(); | ||
|
||
/** | ||
* Gets the field storage definitions. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Let's add documentation about the return type. |
||
*/ | ||
public function getFieldStorageDefinitions(); | ||
|
||
/** | ||
* Gets the field definitions for a specific bundle. | ||
* | ||
* @param string $bundle | ||
* The bundle name. | ||
* | ||
* @return \Drupal\entity\BundleFieldDefinition[] | ||
* An array of bundle field definitions, keyed by field name. | ||
*/ | ||
public function getFieldDefinitions($bundle); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
namespace Drupal\entity\BundlePlugin; | ||
|
||
use Drupal\Core\Entity\EntityBundleListenerInterface; | ||
use Drupal\Core\Entity\EntityTypeInterface; | ||
use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
use Drupal\Core\Field\FieldDefinitionListenerInterface; | ||
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface; | ||
|
||
class BundlePluginInstaller implements BundlePluginInstallerInterface { | ||
|
||
/** | ||
* The entity type manager. | ||
* | ||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||
*/ | ||
protected $entityTypeManager; | ||
|
||
/** | ||
* The entity bundle listener. | ||
* | ||
* @var \Drupal\Core\Entity\EntityBundleListenerInterface | ||
*/ | ||
protected $entityBundleListener; | ||
|
||
/** | ||
* The field storage definition listener. | ||
* | ||
* @var \Drupal\Core\Field\FieldStorageDefinitionListenerInterface | ||
*/ | ||
protected $fieldStorageDefinitionListener; | ||
|
||
/** | ||
* The field definition listener. | ||
* | ||
* @var \Drupal\Core\Field\FieldDefinitionListenerInterface | ||
*/ | ||
protected $fieldDefinitionListener; | ||
|
||
/** | ||
* Constructs a new BundlePluginInstaller object. | ||
* | ||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
* The entity type manager. | ||
* @param \Drupal\Core\Entity\EntityBundleListenerInterface $entity_bundle_listener | ||
* The entity bundle listener. | ||
* @param \Drupal\Core\Field\FieldStorageDefinitionListenerInterface $field_storage_definition_listener | ||
* The field storage definition listener. | ||
* @param \Drupal\Core\Field\FieldDefinitionListenerInterface $field_definition_listener | ||
* The field definition listener. | ||
*/ | ||
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityBundleListenerInterface $entity_bundle_listener, FieldStorageDefinitionListenerInterface $field_storage_definition_listener, FieldDefinitionListenerInterface $field_definition_listener) { | ||
$this->entityTypeManager = $entity_type_manager; | ||
$this->entityBundleListener = $entity_bundle_listener; | ||
$this->fieldStorageDefinitionListener = $field_storage_definition_listener; | ||
$this->fieldDefinitionListener = $field_definition_listener; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function installBundles(EntityTypeInterface $entity_type, array $modules) { | ||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin'); | ||
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) { | ||
return in_array($bundle_info['provider'], $modules, TRUE); | ||
}); | ||
foreach (array_keys($bundles) as $bundle) { | ||
$this->entityBundleListener->onBundleCreate($bundle, $entity_type->id()); | ||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) { | ||
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionCreate($definition); | ||
$this->fieldDefinitionListener->onFieldDefinitionCreate($definition); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules) { | ||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin'); | ||
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) { | ||
return in_array($bundle_info['provider'], $modules, TRUE); | ||
}); | ||
foreach (array_keys($bundles) as $bundle) { | ||
$this->entityBundleListener->onBundleDelete($bundle, $entity_type->id()); | ||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) { | ||
$this->fieldDefinitionListener->onFieldDefinitionDelete($definition); | ||
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($definition); | ||
} | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
namespace Drupal\entity\BundlePlugin; | ||
|
||
use Drupal\Core\Entity\EntityTypeInterface; | ||
|
||
/** | ||
* Installs and uninstalls bundle plugins. | ||
* | ||
* Ensures that the fields provided by the bundle plugins are created/deleted. | ||
*/ | ||
interface BundlePluginInstallerInterface { | ||
|
||
/** | ||
* Installs the bundle plugins provided by the specified modules. | ||
* | ||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | ||
* The entity type. | ||
* @param array $modules | ||
* The modules. | ||
*/ | ||
public function installBundles(EntityTypeInterface $entity_type, array $modules); | ||
|
||
/** | ||
* Uninstalls the bundle plugins provided by the specified modules. | ||
* | ||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | ||
* The entity type. | ||
* @param array $modules | ||
* The modules. | ||
*/ | ||
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules); | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should static cache this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It's used when rebuilding the cached bundle or field data, it's not in any hot path. Plus, the function is not doing any I/O, it's just filtering an array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just and uber nit pick. I thought
hook_modules_installed
andhook_entity_bundle_info
can be called on same request.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, let's skip that then.