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

Issue #3206921 by e0ipso: Allow typed repositories to be determined by a deriver

parent e5b3521b
No related branches found
No related tags found
No related merge requests found
......@@ -3,7 +3,7 @@
namespace Drupal\typed_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryInterface;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryBase;
/**
* Defines typed_entity_repository annotation object.
......@@ -55,12 +55,10 @@ class TypedRepository extends Plugin {
* {@inheritdoc}
*/
public function getId() {
return implode(
TypedRepositoryInterface::SEPARATOR,
array_filter([
$this->definition['entity_type_id'] ?? NULL,
$this->definition['bundle'] ?? NULL,
])
return $this->definition['id']
?? TypedRepositoryBase::generatePluginId(
$this->definition['entity_type_id'] ?? '',
$this->definition['bundle'] ?? '',
);
}
......
......
......@@ -6,8 +6,8 @@ use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryBase;
use Drupal\typed_entity\WrappedEntities\WrappedEntityInterface;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryInterface;
use Drupal\typed_entity\WrappedEntities\WrappedEntityInterface;
/**
* Repository to wrap entities and negotiate specific repositories.
......@@ -28,6 +28,13 @@ class RepositoryManager implements EntityWrapperInterface {
*/
private $pluginManager;
/**
* Caches the deriver base IDs.
*
* @var string[]|null
*/
protected $deriverBaseIds;
/**
* RepositoryManager constructor.
*
......@@ -44,6 +51,9 @@ class RepositoryManager implements EntityWrapperInterface {
/**
* Get a repository.
*
* If more than one deriver declares the same pair of entity_type and bundle,
* the first one found is returned.
*
* @param string $repository_id
* The repository identifier.
*
......@@ -54,12 +64,84 @@ class RepositoryManager implements EntityWrapperInterface {
try {
$instance = $this->pluginManager->createInstance($repository_id, []);
}
catch (PluginException $exception) {
$instance = array_reduce(
$this->extractDeriverBaseIds(),
function ($carry, $base_id) use ($repository_id) {
return $this->deriverPluginReducer($carry, $base_id, $repository_id);
}
);
}
if ($instance) {
return $instance;
}
// If we could not find a repository, try with one for the entity type.
if (strpos($repository_id, TypedRepositoryInterface::ID_PARTS_SEPARATOR) !== FALSE) {
// This removes the last part of the ID, leaving the deriver and entity
// type ID intact. `lorem:ipsum.dolor` -> `lorem:ipsum`.
[$new_repository_id] = explode(TypedRepositoryInterface::ID_PARTS_SEPARATOR, $repository_id, 2);
return $this->get($new_repository_id);
}
return NULL;
}
/**
* Reducer to find the first plugin object based on a deriver base ID.
*
* @param object|null $plugin
* The plugin object.
* @param string $base_id
* The base ID.
* @param string $repository_id
* The repository ID.
*
* @return \Drupal\typed_entity\TypedRepositories\TypedRepositoryInterface|null
* The plugin object.
*/
private function deriverPluginReducer(
?object $plugin,
string $base_id,
string $repository_id
): ?TypedRepositoryInterface {
if ($plugin) {
return $plugin;
}
$plugin_id = sprintf(
'%s%s%s',
$base_id,
TypedRepositoryBase::DERIVATIVE_SEPARATOR,
$repository_id
);
try {
$instance = $this->pluginManager->createInstance($plugin_id, []);
return $instance instanceof TypedRepositoryInterface ? $instance : NULL;
}
catch (PluginException $exception) {
return NULL;
}
return $instance instanceof TypedRepositoryInterface
? $instance
: NULL;
}
/**
* Extracts all the derivers from the list of registered plugins.
*
* @return array
* The deriver base IDs for the typed repository plugin type.
*/
protected function extractDeriverBaseIds(): array {
if (isset($this->deriverBaseIds)) {
return $this->deriverBaseIds;
}
$definitions = $this->pluginManager->getDefinitions();
$ids = array_keys($definitions);
$deriver_plugin_ids = array_filter($ids, function (string $id) {
return strpos($id, TypedRepositoryBase::DERIVATIVE_SEPARATOR) !== FALSE;
});
$deriver_base_ids = array_map(function (string $id) {
[$base] = explode(TypedRepositoryBase::DERIVATIVE_SEPARATOR, $id, 2);
return $base;
}, $deriver_plugin_ids);
$this->deriverBaseIds = array_unique($deriver_base_ids);
return $this->deriverBaseIds;
}
/**
......@@ -87,10 +169,7 @@ class RepositoryManager implements EntityWrapperInterface {
* The repository for the entity.
*/
public function repository(string $entity_type_id, string $bundle = ''): ?TypedRepositoryInterface {
$identifier = implode(
TypedRepositoryBase::SEPARATOR,
array_filter([$entity_type_id, $bundle])
);
$identifier = TypedRepositoryBase::generatePluginId($entity_type_id, $bundle);
return $this->get($identifier);
}
......@@ -99,13 +178,6 @@ class RepositoryManager implements EntityWrapperInterface {
*/
public function wrap(EntityInterface $entity): ?WrappedEntityInterface {
$repository = $this->repositoryFromEntity($entity);
if (!$repository) {
// Maybe there is a repository for all bundles.
$repository = $this->repository($entity->getEntityTypeId());
if (!$repository) {
return NULL;
}
}
return $repository->wrap($entity);
}
......
......
......@@ -129,9 +129,16 @@ class TypedRepositoryBase extends PluginBase implements TypedRepositoryInterface
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
public function id(): string {
return static::generatePluginId($this->entityType->id(), $this->bundle);
}
/**
* {@inheritdoc}
*/
public static function generatePluginId(string $entity_type_id, string $bundle = ''): string {
return implode(
static::SEPARATOR,
array_filter([$this->entityType->id(), $this->bundle])
static::ID_PARTS_SEPARATOR,
array_filter([$entity_type_id, $bundle])
);
}
......
......
......@@ -18,7 +18,20 @@ interface TypedRepositoryInterface extends EntityWrapperInterface {
*
* @var string
*/
const SEPARATOR = ':';
const ID_PARTS_SEPARATOR = '.';
/**
* Generate a plugin ID based on an entity type and bundle.
*
* @param string $entity_type_id
* The entity type.
* @param string $bundle
* The entity bundle.
*
* @return string
* The plugin ID.
*/
public static function generatePluginId(string $entity_type_id, string $bundle = ''): string;
/**
* Gets a query to start finding items.
......
......
......@@ -42,7 +42,7 @@ class TypedEntityRepositoryTest extends KernelTestBase {
]);
$foo->save();
$repository = typed_entity_repository_manager()->get('node:article');
$repository = typed_entity_repository_manager()->get('node.article');
$article_wrapper = $repository->wrap($article);
static::assertInstanceOf(Article::class, $article_wrapper);
......@@ -73,7 +73,7 @@ class TypedEntityRepositoryTest extends KernelTestBase {
* @covers ::wrapMultiple
*/
public function testWrapMultiple() {
$repository = typed_entity_repository_manager()->get('node:article');
$repository = typed_entity_repository_manager()->get('node.article');
$article_wrappers = $repository->wrapMultiple($this->createArticles());
foreach ($article_wrappers as $article_wrapper) {
......@@ -87,8 +87,8 @@ class TypedEntityRepositoryTest extends KernelTestBase {
* @covers ::id
*/
public function testId() {
$repository = typed_entity_repository_manager()->get('node:article');
static::assertSame('node:article', $repository->id());
$repository = typed_entity_repository_manager()->get('node.article');
static::assertSame('node.article', $repository->id());
}
/**
......@@ -97,7 +97,7 @@ class TypedEntityRepositoryTest extends KernelTestBase {
* @covers ::getQuery
*/
public function testGetQuery() {
$repository = typed_entity_repository_manager()->get('node:article');
$repository = typed_entity_repository_manager()->get('node.article');
$query = $repository->getQuery();
$this->createArticles();
......
......
<?php
namespace Drupal\Tests\typed_entity\Unit;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Tests\UnitTestCase;
use Drupal\typed_entity\RepositoryManager;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryBase;
use Drupal\typed_entity\TypedRepositories\TypedRepositoryInterface;
use Drupal\typed_entity\TypedRepositoryPluginManager;
use Prophecy\Argument;
/**
* Tests RepositoryManager.
*
* @coversDefaultClass \Drupal\typed_entity\RepositoryManager
*
* @group typed_entity
*/
class RepositoryManagerTest extends UnitTestCase {
/**
* @covers ::get
* @dataProvider getDataProvider
*/
public function testGet($entity_type_id, $bundle, $times) {
$plugin_manager = $this->prophesize(TypedRepositoryPluginManager::class);
$plugin_ids = [
'foo.bar',
'lorem.ipsum',
'oof',
'base:foo.baz',
'base:rab',
];
$plugin_manager->getDefinitions()->willReturn(array_combine($plugin_ids, $plugin_ids));
$a_plugin = $this->prophesize(TypedRepositoryInterface::class)->reveal();
$plugin_manager->createInstance(Argument::type('string'), Argument::type('array'))
->shouldBeCalledTimes($times)
->will(function ($args) use ($plugin_ids, $a_plugin) {
[$id] = $args;
if (in_array($id, $plugin_ids)) {
return $a_plugin;
}
throw new PluginNotFoundException('typed_entity_repository');
});
$repo_manager = new RepositoryManager(
$this->prophesize(EntityTypeManager::class)->reveal(),
$plugin_manager->reveal()
);
$repository_id = TypedRepositoryBase::generatePluginId($entity_type_id, $bundle);
$instance = $repo_manager->get($repository_id);
static::assertNotNull($instance);
}
/**
* @covers ::get
*/
public function testGetNull() {
$plugin_manager = $this->prophesize(TypedRepositoryPluginManager::class);
$plugin_manager->getDefinitions()->willReturn(['lol:iirc.wrt' => '']);
$plugin_manager->createInstance(Argument::type('string'), Argument::type('array'))
->shouldBeCalledTimes(4)
->will(function () {
throw new PluginNotFoundException('typed_entity_repository');
});
$repo_manager = new RepositoryManager(
$this->prophesize(EntityTypeManager::class)->reveal(),
$plugin_manager->reveal()
);
$repository_id = TypedRepositoryBase::generatePluginId('meh', 'phew');
$instance = $repo_manager->get("something:$repository_id");
static::assertNull($instance);
}
/**
* Data provider for testGet.
*/
public function getDataProvider(): array {
return [
['foo', 'bar', 1],
['oof', 'faa', 3],
['oof', '', 1],
['foo', 'baz', 2],
['rab', 'red', 4],
];
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment