Problem/Motivation

Per #2671964: ContextHandler cannot validate constraints the ContextHandler cannot properly evaluate plugin constraints as it currently stands. That issue attempts to rectify this problem but cannot for various architectural concerns of those involved. In order to compensate for this, I opted to build an entity generating utility that could be universally useful.

A useful entity generation utility could:

  • Help solve this issue with the ContextHandler validating plugin constraints
  • Simplify contrib utilities like Devel Generate
  • Potentially do cool stuff like generate previews of an entity w/o the need to actually create and save an entity. (just spit-balling here)

Proposed resolution

Write an entity generation utility that accommodates all fieldable content entities and at least opens the door for config entities to be generated as well with additional developer input.

Remaining tasks

Review and iterate.

User interface changes

N/A

API changes

We introduce a new entity generator service and wrote test coverage. This test coverage revealed that the User entity (specifically) had custom value constraints applied to username and timezone which were not being respected by the field's generateSampleItems() methods. In order to solve this, I added or overrode classes specific to those properties and included a custom generateSampleValue() method on them. Once I did this, tests still did not pass at which point I noticed that \Drupal\Core\Field\FieldItemList::generateSampleItems() didn't retrieve the class set by base field definitions, rather it delegated directly to the "type" class (a plugin class specific to the type, so never more specific than say "string"). I updated this to get the field definition's item definition class, which implements the same interface, but could have been overridden by the entity's base field definitions directly. This gave my custom classes the ability to generate reliable sample values that would pass validation.

Data model changes

N/A

CommentFileSizeAuthor
#37 2905527-37.interdiff.txt619 bytesEclipseGc
#37 2905527-37.patch11.3 KBEclipseGc
#35 2905527-35.interdiff.txt3.42 KBEclipseGc
#35 2905527-35.patch11.29 KBEclipseGc
#32 2905527-32.interdiff.txt2.58 KBEclipseGc
#32 2905527-32.patch10.89 KBEclipseGc
#30 2905527-30.interdiff.txt730 bytesEclipseGc
#30 2905527-30.patch10.89 KBEclipseGc
#28 2905527-28.interdiff.txt11.48 KBEclipseGc
#28 2905527-28.patch11.6 KBEclipseGc
#26 2905527-26.interdiff.txt5.25 KBEclipseGc
#26 2905527-26.patch20.69 KBEclipseGc
#24 2905527-24.interdiff.txt10.64 KBEclipseGc
#24 2905527-24.patch16.34 KBEclipseGc
#22 2905527-22.interdiff.txt6.72 KBEclipseGc
#22 2905527-22.patch11.58 KBEclipseGc
#17 2905527-17.interdiff.txt19.38 KBEclipseGc
#17 2905527-17.patch10.38 KBEclipseGc
#13 2905527-13.FAIL_.patch1.38 KBEclipseGc
#13 2905527-13.interdiff.txt7.69 KBEclipseGc
#13 2905527-13.patch21.46 KBEclipseGc
#10 2905527-10.interdiff.txt1.66 KBEclipseGc
#10 2905527-10.patch19.72 KBEclipseGc
#9 2905527-9.interdiff.txt2.26 KBEclipseGc
#9 2905527-9.patch19.73 KBEclipseGc
#8 2905527-8.interdiff.txt18.43 KBEclipseGc
#8 2905527-8.patch19.6 KBEclipseGc
#4 2905527-4.patch11.62 KBEclipseGc
#2 2905527-1.patch11.37 KBEclipseGc
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EclipseGc created an issue. See original summary.

EclipseGc’s picture

Status: Active » Needs review
FileSize
11.37 KB

patch!

As mentioned in the IS, there are some minor changes to the User entity in order to ensure it can reliably generate a user entity. These changes didn't make much sense in isolation because the test I'd have had to write would have been pretty similar to the actual service we introduced and tested in this patch.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 2: 2905527-1.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
11.62 KB

Let's see if this makes testbot happier.

Eclipse

EclipseGc’s picture

Issue tags: +Blocks-Layouts

Tagging this as Blocks-Layouts since it's a blocker in a long chain of dependencies for doing administrative UI for building defaults.

EclipseGc’s picture

Title: Introduce an Entity Generator to easy plugin filtering via context » Introduce an Entity Generator to ease plugin filtering via context
tim.plunkett’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +namespace Drupal\Core\Entity;
    +
    +
    +use Drupal\Core\Config\Entity\ConfigEntityType;
    

    double blank lines

  2. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +class EntityGenerator {
    

    Since this is a service, please provide an interface

  3. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +   * EntityGenerate constructor.
    

    "Constructs a new EntityGenerator."

  4. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
    ...
    +  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    

    I don't think we're using lowerCamelCase in core like this yet

  5. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +   * Generate a fieldable entity.
    ...
    +   * Generate a configuration entity.
    

    Generates

  6. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +   *   The entity type id.
    ...
    +      // Bundle is already set, and id and revision should never be set.
    ...
    +   *   The entity type id.
    

    s/id/ID

  7. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +   * @throws \Exception
    ...
    +      throw new \Exception("Chosen entity type is not fieldable. Use \Drupal\Core\Entity\EntityGenerator::generateConfigurationEntity() instead.");
    ...
    +   * @throws \Exception
    ...
    +      throw new \Exception("Chosen entity type is not a configuration entity. Use \Drupal\Core\Entity\EntityGenerator::generateFieldableEntity() instead.");
    ...
    +      throw new \Exception("Missing generator handler. This entity type cannot be generated.");
    

    This should be a classed exception. And don't end exception methods with a .

  8. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +    $definition = $this->entityTypeManager->getDefinition($entity_type_id);
    +    if (!$definition instanceof ContentEntityType) {
    ...
    +    $definition = $this->entityTypeManager->getDefinition($entity_type_id);
    +    if (!$definition instanceof ConfigEntityType) {
    

    a) Usually we call this local variable $entity_type.
    b) Use something like $entity_type->entityClassImplements(FieldableEntityInterface::class) (or ConfigEntityInterface), because the current checks do not guarantee that.

  9. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,117 @@
    +    /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
    

    There is no $entity in this method. Also, not fieldable

  10. +++ b/core/lib/Drupal/Core/Field/FieldItemList.php
    @@ -259,7 +259,7 @@ public function view($display_options = []) {
    -    $field_type_class = \Drupal::service('plugin.manager.field.field_type')->getPluginClass($field_definition->getType());
    +    $field_type_class = $field_definition->getItemDefinition()->getClass();
    

    This change is not immediately clear to me. Why does this help?

  11. +++ b/core/modules/user/src/Entity/User.php
    @@ -498,6 +498,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    +    $fields['timezone']->getItemDefinition()->setClass('\Drupal\user\TimeZoneItem');
    

    TimeZoneItem::class

  12. +++ b/core/modules/user/src/TimeZoneItem.php
    @@ -0,0 +1,24 @@
    +    $keys = array_keys(system_time_zones());
    +    $count = count($keys);
    +    $key = rand(0, $count);
    +    return $keys[$key];
    

    Can we get a comment explaining this?

  13. +++ b/core/modules/user/src/UserNameItem.php
    @@ -23,4 +24,13 @@ public function isEmpty() {
    +    $values['value'] = substr($values['value'], 0, 60);
    

    Any reason to hardcode 60? Is there a setting on the field definition that would be better to check? A comment would help.

  14. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,88 @@
    + * Tests the EntityGenerator API.
    + *
    + * @group Entity
    + */
    +class EntityGeneratorTest extends KernelTestBase {
    

    @coversDefaultClass would be nice here, and @covers on the individual methods below.

  15. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,88 @@
    +   * Modules to enable.
    +   *
    +   * @var array
    

    {@inheritdoc}

  16. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,88 @@
    +    $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']);
    +    $node_type->save();
    +    $node_type = NodeType::create(['type' => 'page', 'name' => 'Page']);
    +    $node_type->save();
    +    $vocabulary = Vocabulary::create(['name' => 'Tags', 'vid' => 'tags']);
    +    $vocabulary->save();
    

    Super nit, but you can chain the ->save

  17. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,88 @@
    +            $entity = $this->entityGenerator->generateFieldableEntity($entity_type_id, $bundle->id());
    ...
    +          $entity = $this->entityGenerator->generateFieldableEntity($entity_type_id, $entity_type_id);
    

    This tests generateFieldableEntity, needs coverage for the config one.

    And also needs coverage for the thrown Exceptions.

    A coverage report of this would be telling. I don't see any GeneratorHandlers being used, even test ones.
    So far this seems more like a Unit test than a Kernel test?

EclipseGc’s picture

Ok, I worked on this a bit today to fix the issues Tim pointed out. Overall I think the changes clean it up nicely, but in order to test the configuration entity generation, I went ahead and created a NodeType generator just to have one and to facilitate testing. People can easily copy what I did here and take it further if need be. This will at least give some variety for testing purposes.

Eclipse

EclipseGc’s picture

Cleaned up the stuff I added a bit to make it better and more core-worthy.

Eclipse

EclipseGc’s picture

Cleaned up whitespace issues.

Eclipse

tim.plunkett’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,95 @@
    +    $entityType = $this->entityTypeManager->getDefinition($entity_type_id);
    

    Nit: should be $entity_type

  2. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,95 @@
    +      // Bundle is already set, and id and revision should never be set.
    

    s/id/ID

  3. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,95 @@
    +    $definition = $this->entityTypeManager->getDefinition($entity_type_id);
    +    if (!$definition instanceof ConfigEntityType) {
    

    This should also be $entity_type not $definition, and should also use entityClassImplements as the method above does.

  4. +++ b/core/lib/Drupal/Core/Entity/EntityGenerator.php
    @@ -0,0 +1,95 @@
    +      throw new EntityGeneratorMismatchException("Provided entity type \"%s\" is not a configuration entity. Use \Drupal\Core\Entity\EntityGenerator::generateFieldableEntity() instead", $entity_type_id);
    
    +++ b/core/lib/Drupal/Core/Entity/Exception/EntityGeneratorMismatchException.php
    @@ -0,0 +1,28 @@
    +    $message = sprintf($message, $entity_type_id);
    

    I'm not sure I've ever seen it done this way, most of the time I see the sprintf call where the exception is thrown.

    Even in the case of \Drupal\Component\Plugin\Exception\PluginNotFoundException, the sprintf is for the fallback.

    Just struck me as odd.

  5. +++ b/core/lib/Drupal/Core/Entity/EntityGeneratorInterface.php
    @@ -0,0 +1,50 @@
    +   * @param array $values
    +   *   Any default values to use during generation.
    ...
    +  public function generateFieldableEntity($entity_type_id, $bundle, array $values = []);
    ...
    +   * @param array $values
    +   *   Any default values to use during generation.
    ...
    +  public function generateConfigurationEntity($entity_type_id, array $values = []);
    

    The oneline doc should start with (optional)

  6. +++ b/core/lib/Drupal/Core/Entity/Exception/MissingEntityGeneratorHandlerException.php
    @@ -0,0 +1,27 @@
    +  public function __construct($entity_type_id) {
    +    parent::__construct(sprintf("Missing generator handler. The \"%s\" entity type cannot be generated", $entity_type_id));
    

    In this case I would mimic \Drupal\Component\Plugin\Exception\PluginNotFoundException, and allow an optional $message and use this sprintf as a fallback

  7. +++ b/core/lib/Drupal/Core/Entity/GeneratorHandlerInterface.php
    @@ -0,0 +1,28 @@
    +   * @param array $values
    +   *   An array of values to include as part of the entity.
    ...
    +  public function generate(array $values = []);
    

    (optional)

  8. +++ b/core/lib/Drupal/Core/Field/FieldItemList.php
    @@ -259,7 +259,7 @@ public function view($display_options = []) {
    -    $field_type_class = \Drupal::service('plugin.manager.field.field_type')->getPluginClass($field_definition->getType());
    +    $field_type_class = $field_definition->getItemDefinition()->getClass();
    

    I still think this change deserves dedicated test coverage, that doesn't use the EG

The interdiffs look great!

amateescu’s picture

Issue tags: +Entity Field API

From what I see in the patch, this is basically a "Entity::createWithSampleValues()", right? In which case, can't we simply call it that (or something similar) and put it in the storage handler?

EclipseGc’s picture

1-3: OK

4: Was copying stuff I saw here: \Drupal\Core\Entity\Exception\AmbiguousEntityClassException

5-7: OK

8: I chose to place this test into UserEntityTest since we are in fact testing that the user entity will properly validate. I've included a test-only version here which I expect to fail.

Amateescu:

So my reasoning on this is that while yes functionally we're generating an entity with sample values, I chose not to do this on storage handlers because they are already an established thing, and more over (and more importantly) not all entities need a custom generator class. Currently only config entities need that, and ideally we'd be able to describe our entities and their requirements well enough that we can delegate generation to a centralized generator (like I've done for content entities in this patch).

Eclipse

Status: Needs review » Needs work

The last submitted patch, 13: 2905527-13.FAIL_.patch, failed testing. View results

EclipseGc’s picture

Status: Needs work » Needs review

Posted patches in the wrong order heh.

Eclipse

amateescu’s picture

Can't we use config schema to derive sample values for config entities on a best-effort basis? I guess what raises an alarm bell for me is the naming: generator is very broad and.. strong (can't really describe it better, sorry :)), so how about SampleValuesEntityGenerator or something like that?

Potentially do cool stuff like generate previews of an entity w/o the need to actually create and save an entity.

What do you mean by "a preview of an entity"?

Also, I'm wondering how does this help solve the issue linked in the issue summary?

EclipseGc’s picture

Had a long conversation with amateescu in slack. We hashed out some of the details and I've removed a lot of the code that existed in this patch in order to really trim it down. We might want to move tests around a bit, but config generation is gone as a concept, content entity generation moved to the storage container and corresponding interface and base class were updated as necessary.

Eclipse

jibran’s picture

This looks much better now.

Status: Needs review » Needs work

The last submitted patch, 17: 2905527-17.patch, failed testing. View results

amateescu’s picture

Looks much better indeed!

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +90,31 @@ protected function doCreate(array $values) {
    +  public function createWithSampleValues($bundle, array $values = []) {
    

    I'm not sure whether the $bundle parameter should exist or not, so let's hear more opinions on this one :)

  2. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +90,31 @@ protected function doCreate(array $values) {
    +    $bundle_key = $this->entityType->getKey('bundle');
    +    $values[$bundle_key] = $bundle;
    

    You have to take into account that an entity type might not provide a bundle key, in which case the value for the bundle is the ID of the entity type.

  3. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +90,31 @@ protected function doCreate(array $values) {
    +    // Fallback entity generation in case no custom generator is supplied.
    

    There is no fallback anymore ;)

  4. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
    @@ -23,4 +23,21 @@
    +   * Generates an entity with sample field values.
    

    Creates an entity...

  5. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
    @@ -23,4 +23,21 @@
    +   * @throws \Drupal\Core\Entity\Exception\EntityNotFieldableException
    +   *   Thrown if the chosen entity type is not a content entity.
    

    This is not thrown anymore.

  6. +++ b/core/modules/node/src/Entity/NodeType.php
    @@ -19,7 +19,7 @@
    + *     "list_builder" = "Drupal\node\NodeTypeListBuilder"
    

    This change is not needed anymore.

  7. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,77 @@
    + * Tests the EntityGenerator API.
    ...
    +class EntityGeneratorTest extends KernelTestBase {
    ...
    +   * Tests generation of all enabled content entity types.
    ...
    +  public function testGenerateContentEntity() {
    ...
    +        // Generate entities with bundles.
    ...
    +        // Generate entities without bundles.
    

    These strings should be updated to not mention the generator approach anymore.

tim.plunkett’s picture

This looks much cleaner!

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
    @@ -23,4 +23,21 @@
    +   * @throws \Drupal\Core\Entity\Exception\EntityNotFieldableException
    +   *   Thrown if the chosen entity type is not a content entity.
    

    I don't see this thrown anymore, figure it's leftover?

  2. +++ b/core/modules/node/src/Entity/NodeType.php
    @@ -19,7 +19,7 @@
    - *     "list_builder" = "Drupal\node\NodeTypeListBuilder",
    + *     "list_builder" = "Drupal\node\NodeTypeListBuilder"
    

    Leftover change. Trailing commas are okay in annotations.

  3. +++ b/core/modules/user/src/UserNameItem.php
    --- a/core/modules/user/tests/src/Kernel/UserEntityTest.php
    +++ b/core/modules/user/tests/src/Kernel/UserEntityTest.php
    
    +++ b/core/modules/user/tests/src/Kernel/UserEntityTest.php
    @@ -22,6 +22,14 @@ class UserEntityTest extends KernelTestBase {
    +    $this->installEntitySchema('user');
    

    Pretty incredible that this wasn't needed already!

  4. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityGeneratorTest.php
    @@ -0,0 +1,77 @@
    +      if ($definition instanceof ContentEntityType) {
    

    entityClassImplements() please

EDIT: Crosspost! Lots of similar feedback

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
11.58 KB
6.72 KB

Took care of all the feedback that was obvious and actionable.

20.2:

So you can clearly see what I chose to do for this in the new interdiff. I also added back an exception here for requesting a bad bundle. I wanted to keep as much of this code in the method as possible because error checking this stuff requires the caller to understand the entity system pretty in-depth and I'd like the method to be as concise and easy to use as possible. To that end...

20.1:

I REALLY think we want this parameter because 20.2 becomes harder to do properly w/o separate bundle params plus the caller of this method would have to move a bit of the code that figures out the proper bundle key name out of this method and repeat it everywhere this method is called. To that end, I really think we should keep it as is.

Eclipse

amateescu’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +91,41 @@ protected function doCreate(array $values) {
    +  public function createWithSampleValues($bundle, array $values = []) {
    ...
    +    $bundle_key = $this->entityType->getKey('bundle');
    +    $values[$bundle_key] = $bundle;
    ...
    +      $bundle_key
    

    You also need to take into account that an entity type might not have bundles (e.g. User), so the first method argument should be optional and $bundle_key needs to be added to the forbidden keys only if it exists.

    Which makes me wonder, why do we forbid passing an entity ID and revision ID in the $values array?

    Entity types with string IDs will not have an ID generated automatically by the DB layer, so we need to allow passing an initial ID for them.

  2. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +91,41 @@ protected function doCreate(array $values) {
    +    $bundle_type = $this->entityType->getBundleEntityType();
    +    if (!$bundle_type && $bundle != $this->entityTypeId) {
    +      $bundle = $this->entityTypeId;
    +    }
    +    if ($bundle_type) {
    +      $bundle_type = $this->entityManager->getStorage($bundle_type);
    +      $bundle_entity = $bundle_type->load($bundle);
    +      if (!$bundle_entity) {
    +        throw new MissingEntityBundleException($bundle);
    +      }
    +    }
    

    This is too complicated and not really correct because not all bundles are managed through a config entity type. You should use getBundleInfo() method from the entity_type.bundle.info service, and throw a regular EntityStorageException like \Drupal\Core\Entity\ContentEntityStorageBase::doCreate().

  3. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
    @@ -23,4 +23,21 @@
    +   * @throws \Drupal\Core\Entity\Exception\MissingEntityBundleException
    +   *   Thrown if the requested entity bundle does not exist.
    

    As mentioned above, this needs to be a EntityStorageException.

  4. +++ b/core/modules/user/tests/src/Kernel/UserEntityTest.php
    @@ -65,4 +73,22 @@ public function testUserMethods() {
    +    $this->assertFalse((bool) $violations->count());
    
    +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/CreateSampleEntityTest.php
    @@ -0,0 +1,77 @@
    +            $this->assertEquals(0, $violations->count());
    ...
    +          $this->assertEquals(0, $violations->count());
    

    We can use assertCount() here.

  5. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/CreateSampleEntityTest.php
    @@ -0,0 +1,77 @@
    +        // Create sample entities without bundles.
    +        else {
    +          $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues($entity_type_id);
    

    This shows the problem with having $bundle as an explicit argument, it forces the caller to know that an entity type doesn't use bundles and guess that it can use the entity type ID for the bundle name.

    Also, the caller can also be wrong in assuming that an entity doesn't have bundles, since the entity type definition can be altered by any other module to add bundle support for that entity type.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
16.34 KB
10.64 KB

23.1

Which makes me wonder, why do we forbid passing an entity ID and revision ID in the $values array?

We're not forbidding passing, just forbidding generation. There are a host of reasons that this makes sense, but the most obvious is if someone were to call save on these objects, we'd want their ids to be generated appropriately, and for non-serial id entities, an id can absolutely be passed in the $values array.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 24: 2905527-24.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
20.69 KB
5.25 KB

Ok, fixed the tests and code standards issues.

Eclipse

amateescu’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -43,12 +50,15 @@
    -  public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, CacheBackendInterface $cache) {
    +  public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
    
    +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
    @@ -159,11 +161,13 @@ public function getFieldStorageDefinitions() {
    -  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) {
    -    parent::__construct($entity_type, $entity_manager, $cache);
    +  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, EntityTypeBundleInfoInterface $entity_type_bundle_info, LanguageManagerInterface $language_manager) {
    +    parent::__construct($entity_type, $entity_manager, $cache, $entity_type_bundle_info);
    

    These changes will force every overridden content storage handler to require an update, including contrib and custom ones, so I think we're better off using getBundleInfo() from the deprecated entity manager service, since we already have that injected.

  2. +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/CreateSampleEntityTest.php
    @@ -0,0 +1,77 @@
    +  public function testGenerateContentEntity() {
    

    This method still uses the old "generate" naming.

And.. last but not least, we need to change the issue title :)

EclipseGc’s picture

Title: Introduce an Entity Generator to ease plugin filtering via context » Introduce a sample value entity method to ease plugin filtering via context
FileSize
11.6 KB
11.48 KB

Wish I'd known about that method on the entity manager previously. That would have saved some time and test coverage :-D

Eclipse

amateescu’s picture

Status: Needs review » Reviewed & tested by the community

I think it looks perfect now! Except for this small nit:

+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -93,6 +93,13 @@ class SqlContentEntityStorageTest extends UnitTestCase {
+   * The entity type bundle information object.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityTypeBundleInfo;

This leftover from the previous patch should be removed.

EclipseGc’s picture

amateescu’s picture

Really sorry for yet another nitpick review, I found two more things on a second pass :/

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
    @@ -23,4 +23,21 @@
    +   * @throws \Drupal\Core\Entity\Exception\MissingEntityBundleException
    

    This is not the exception that's being thrown anymore.

  2. +++ b/core/modules/user/tests/src/Kernel/UserEntityTest.php
    --- /dev/null
    +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/CreateSampleEntityTest.php
    
    +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/CreateSampleEntityTest.php
    @@ -0,0 +1,77 @@
    +namespace Drupal\KernelTests\Core\Entity\Element;
    

    This is a weird namespace for this test, why is it in the Element subdirectory?

EclipseGc’s picture

Ok, fixed a couple additional things I found from what you pointed out.

Eclipse

amateescu’s picture

Perfect :)

larowlan’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

Updating issue credits to include @tim.plunkett and @amateescu for their reviews.

We need a change record here.

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
    @@ -90,6 +90,40 @@ protected function doCreate(array $values) {
    +      if (!in_array($field_name, $forbidden_keys)) {
    

    nit: these are strings, we should use the third argument

  2. +++ b/core/modules/user/src/TimeZoneItem.php
    @@ -0,0 +1,24 @@
    +    $key = rand(0, count($timezones));
    

    If this yields the maximum, we'll get an error here?

    e.g. if keys are [0, 1, 2]

    count() is 3.

    $timezones[3] does not exist.

    php.net/rand tells me that rand is inclusive of the maximum

  3. +++ b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php
    @@ -0,0 +1,77 @@
    +        // Create sample entities with bundles.
    +        if ($bundle_type = $definition->getBundleEntityType()) {
    +          foreach ($this->entityTypeManager->getStorage($bundle_type)->loadMultiple() as $bundle) {
    +            $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues($bundle->id());
    +            $violations = $entity->validate();
    +            $this->assertCount(0, $violations);
    +          }
    +        }
    +        // Create sample entities without bundles.
    +        else {
    +          $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues();
    +          $violations = $entity->validate();
    

    We don't have any coverage for passing the second argument - $values

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
11.29 KB
3.42 KB

Ok :-D

Eclipse

amateescu’s picture

The nitpick police strikes again:

+++ b/core/modules/user/src/TimeZoneItem.php
@@ -17,7 +17,7 @@ class TimeZoneItem extends StringItem {
+    $key = rand(0, count($timezones)-1);

Missing spaces before and after '-' here :D

And we still need the change record requested in #34.

EclipseGc’s picture

Change record: [#2909464]

Nit's addressed :-D

Eclipse

amateescu’s picture

Status: Needs review » Reviewed & tested by the community

Cool, let's ship it :)

larowlan’s picture

updating issue credits, adding myself for catching the off by one error

larowlan’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +8.5.0 release notes

Committed as dff09a9 and pushed to 8.5.x.

Published the change record.

Tagging for 8.5.x release notes.

  • larowlan committed dff09a9 on 8.5.x
    Issue #2905527 by EclipseGc, amateescu, tim.plunkett, larowlan:...
EclipseGc’s picture

AWESOME!

Wim Leers’s picture

Great to have this capability!

+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
@@ -23,4 +23,21 @@
+  public function createWithSampleValues($bundle = FALSE, array $values = []);

Devil's advocate: why is this not a BC break?

larowlan’s picture

Devil's advocate: why is this not a BC break?

Two months ago, I said the same thing, but then I was pointed to this bit in our BC policy.

Interfaces within non-experimental, non-test modules not tagged with either @api or @internal
Interfaces that are not tagged with either @api or @internal can be safely used as type hints. No methods will be changed or removed from these interface in a breaking way.
However, we reserve the ability to add methods to these interfaces in minor releases to support new features. When implementing the interface, module authors are encouraged to either:

Where there is a 1-1 relationship between a class and an interface, inherit from the class. Where a base class or trait is provided, use those. This way your class should inherit new methods from the class or trait when they're added.
Where the interface is implemented directly for other reasons, be prepared to add support for new methods in minor releases

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

xjm’s picture

I don't think there's anything here that site owners must know when upgrading, so switching to the highlights tag.