Followup of #2199483: Provide a default config_prefix based on entity type ID and provider

  • Moves getConfigPrefix of the EntityTypeInterface to a new ConfigEntityTypeInterface
  • Improves documentation
  • Adds a unit test for all methods in ConfigEntityType to ensure getConfigPrefix logic is tested.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Task because nothing is broken and we can continue to implement everything on EntityType and EntityTypeInterface. However doing this increasely will bleed content logic into config and vica versa.
Issue priority Critical because it blocks #2430219: Implement a key value store to optimise config entity lookups and #2432791: Skip Config::save schema validation of config data for trusted data which add methods to ConfigEntityType which should not on EntityInterface.
Prioritized changes The main goal of this issue is unblock a performance issue. Also I would argue that this should be permitted on the reducing fragility provision because it will make API addition to Config entity types much simpler to enforce during the 8.x cycle.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

alexpott’s picture

Title: Unit test ConfigEntityType class » Move getConfigPrefix() to ConfigEntityTypeInterface
Issue summary: View changes
FileSize
59.31 KB

Re-scoped issue

alexpott’s picture

Issue tags: +LSDHACK
alexpott’s picture

FileSize
14.36 KB

Borked patch

sun’s picture

+++ b/core/lib/Drupal/Core/Config/ConfigManager.php
@@ -63,7 +64,7 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
   public function getEntityTypeIdByName($name) {
     $entities = array_filter($this->entityManager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($name) {
-      return ($config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0;
+      return ($entity_type instanceof ConfigEntityTypeInterface && $config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0;
     });

It looks like this would be easier with #2194783: Rename EntityTypeInterface::isSubclassOf() to ::entityClassImplements() ?

Shall we postpone this issue on that one?

xjm’s picture

Can we fix the naming and docs while we're at it, or does that need to be a separate issue?

mauzeh’s picture

In #2226437: Create ContentEntityTypeInterface I created a ContentEntityTypeInterface and made it extend the EntityTypeInterface.

Shouldn't ConfigEntityTypeInterface also extend EntityTypeInterface?

Berdir’s picture

Status: Needs review » Needs work

Good point, yes it should.

mauzeh’s picture

alright, working on it, will post a new patch. This one needs a reroll so will do that too.

mauzeh’s picture

Status: Needs work » Needs review
FileSize
12.61 KB

Status: Needs review » Needs work

The last submitted patch, 9: 2204697.9.patch, failed testing.

mauzeh’s picture

Status: Needs work » Needs review
FileSize
8.11 KB

woops, forgot a use statement... this one should pass.

Status: Needs review » Needs work

The last submitted patch, 11: 2204697.11.patch, failed testing.

tstoeckler’s picture

Awesome, thanks @mauzeh. I just have some minor points.

  1. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
    @@ -12,10 +12,17 @@
    -   * Returns the config prefix used by the configuration entity type.
    +   * The config prefix set in the configuration entity type annotation.
    

    We have a standard of documenting functions starting with a third person verb so the previous one was actually correct.

  2. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
    @@ -12,10 +12,17 @@
    +   * The default configuration prefix is constructed from the name of the module
    +   * that provides the entity type and the ID of the entity type. If a
    +   * config_prefix annotation is present it will be used in place of the entity
    +   * type ID.
    

    Awesome that you added this documentation!

  3. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
    @@ -40,11 +47,6 @@ public function getConfigPrefix() {
    -    // Ensure that all configuration entities are prefixed by the module that
    -    // provides the configuration entity type. This ensures that default
    -    // configuration will be created as expected during module install and
    -    // dependencies can be calculated without the modules that provide the
    -    // entity types being installed.
    

    This is now sort of duplicated above, but I think it couldn't hurt to leave this in.

  4. +++ b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php
    @@ -48,6 +48,12 @@ class ConfigEntityMapperTest extends UnitTestCase {
       /**
    +   * The entity used for testing.
    +   *
    +   * @var \Drupal\Core\Config\Entity\ConfigEntityType|\PHPUnit_Framework_MockObject_MockObject
    +   */
    +
    

    I don't see any entity :-) ?! Also it should be "entity *type*" I think.

  5. +++ b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigEntityMapperTest.php
    @@ -93,6 +99,10 @@ public function setUp() {
    +    $this->entityType = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityType')
    +      ->disableOriginalConstructor()
    +      ->getMock();
    

    Minor, but due to this issue you can do

    $this->entityType = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface')

    and therefore avoid the disable...() call.

tstoeckler’s picture

Status: Needs work » Needs review
FileSize
6.51 KB
12.02 KB

Oops, #13.1 was actually documenting a variable not a method, so the change is correct.

Fixes the rest. Also re-includes the actual interface and the test for ConfigEntityType that was accidentally left out of the previous patch.

Status: Needs review » Needs work

The last submitted patch, 14: 2204697-14-config-entity-type-interface.patch, failed testing.

tstoeckler’s picture

Status: Needs work » Needs review
FileSize
6.93 KB
11.89 KB

Ahh, now I know which use statement @mauzeh was talking about in #11.

We also already have ConfigEntityTest in 8.x now, so moving the test methods there. And fixing one method that needed updating after renaming the entity storage classes.

Status: Needs review » Needs work

The last submitted patch, 16: 2204697-16-config-entity-type-interface.patch, failed testing.

tstoeckler’s picture

SQLSTATE[42S02]: Base table or view not found: 1146 Table [error]
'drupaltestbotmysql.user__user_picture' doesn't exist: SELECT t.*
FROM
{user__user_picture} t
WHERE (entity_id IN (:db_condition_placeholder_0)) AND (deleted =
:db_condition_placeholder_1) AND (langcode IN
(:db_condition_placeholder_2, :db_condition_placeholder_3,
:db_condition_placeholder_4))
ORDER BY delta ASC; Array
(
[:db_condition_placeholder_0] => 1
[:db_condition_placeholder_1] => 0
[:db_condition_placeholder_2] => en
[:db_condition_placeholder_3] => und
[:db_condition_placeholder_4] => zxx
)].

Don't see how that would be caused by this. Retesting

tstoeckler’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 16: 2204697-16-config-entity-type-interface.patch, failed testing.

tstoeckler’s picture

FileSize
563 bytes
12.2 KB

tstoeckler-- for the time it took me to figure this out... Let's see.

Because of the missing use statement the array_filter callback in getEntityTypeIdByName() was never returning TRUE. Thus the check whether a config object is in fact a config entity in ConfigInstaller failed and fields were being saved as dumb config objects and thus the tables were never created. Fun! :-)

tstoeckler’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 21: 2204697-20-config-entity-type-interface.patch, failed testing.

tstoeckler’s picture

Status: Needs work » Needs review
FileSize
750 bytes
12.93 KB

...

Berdir’s picture

+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityTypeTest.php
@@ -88,4 +88,77 @@ public function testConfigPrefixLengthValid() {
+  /**
+   * Tests the getBaseTable() method.
+   *
+   * @covers ::getBaseTable()
+   */
+  public function testGetBaseTable() {
+    $entity_type = $this->setUpConfigEntityType();
+    $this->assertFalse($entity_type->getBaseTable());
+  }
+
+  /**
+   * Tests the getRevisionDataTable() method.
+   *
+   * @covers ::getRevisionDataTable()
+   */
+  public function testGetRevisionDataTable() {
+    $entity_type = $this->setUpConfigEntityType();
+    $this->assertFalse($entity_type->getRevisionDataTable());
+  }
+
+  /**
+   * Tests the getRevisionTable() method.
+   *
+   * @covers ::getRevisionTable()
+   */
+  public function testGetRevisionTable() {
+    $entity_type = $this->setUpConfigEntityType();
+    $this->assertFalse($entity_type->getRevisionTable());
+  }
+
+  /**
+   * Tests the getDataTable() method.
+   *
+   * @covers ::getDataTable()
+   */
+  public function testGetDataTable() {
+    $entity_type = $this->setUpConfigEntityType();
+    $this->assertFalse($entity_type->getDataTable());
+  }

Why would we want to add tests for those here?

Berdir’s picture

Status: Needs review » Needs work

Still confused about that...

tstoeckler’s picture

Can you explain your confusion? This just increases the test coverage of the ConfigEntityType class. Nothing else. It's not technically related to this issue but it seemed easy enough to do here so I thought why not?

xjm’s picture

@tstoeckler, because it's not in scope and that makes the patch harder to review/commit. :) Might be good to spin off a separate issue "Increase test coverage for ConfigEntityType" with that part of the patch?

Berdir’s picture

It adds test coverage for methods that have nothing to do with config entities and there's an issue to remove them completely, so why add it here and why add it at all? :)

mgifford’s picture

alexpott’s picture

Status: Needs work » Needs review
Related issues: +#2430219: Implement a key value store to optimise config entity lookups
FileSize
14.31 KB

Resurrecting because not doing this totally ties our had at doing API additions to only config entity types during the 8.x cycle and issues like #2430219: Implement a key value store to optimise config entity lookups need to add methods to config entity types that have no business being on the content entity types.

I removed the unnecessary tests added by #24 which stalled this patch in the first place.

alexpott’s picture

This issue is a normal task so we need to outline how it fits within the allowable Drupal 8 beta criteria. Can someone add Drupal 8 beta phase evaluation template to the issue summary.

If no one else does - will do myself later.

alexpott’s picture

Priority: Normal » Major
Issue summary: View changes
Issue tags: -Needs issue summary update +blocker

Added beta eval. Bumping to major because blocks have a place to add the public methods needed for #2430219: Implement a key value store to optimise config entity lookups

alexpott’s picture

FileSize
416 bytes
14.28 KB

Fixing missing new line.

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me.

It is a small API change, but having this interface is very useful and the only way it breaks if someone is calling getConfigPrefix() unconditionally on all entity types. I assume that outside of core, there are very few reasons to call that method at all.

xjm’s picture

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

This needs a change record. Thanks!

alexpott’s picture

Status: Needs work » Reviewed & tested by the community
xjm’s picture

Issue tags: -Needs change record

Thanks @alexpott.

alexpott’s picture

https://www.drupal.org/node/2181667 needs updating to remove the irrelevant mentions of getConfigPrefix() from the examples.

xjm’s picture

xjm’s picture

Priority: Major » Critical
Status: Reviewed & tested by the community » Fixed
Issue tags: +Needs followup

So, per @alexpott, #2430219: Implement a key value store to optimise config entity lookups is now critical, and this issue is also now blocking #2432791: Skip Config::save schema validation of config data for trusted data, which is also critical. I asked @alexpott if it was a hard blocker, and he pointed out that it's increasingly fragile to have these various config-specific methods on EntityInterface but returning FALSE or NULL or whatnot for content entity types. Therefore, elevating this to critical.

  1. I was going to suggest a followup issue to replace instanceof ConfigEntityType checks, but I see @Berdir is already looking at #2194783: Rename EntityTypeInterface::isSubclassOf() to ::entityClassImplements() which will address a related problem space.
  2. +++ b/core/lib/Drupal/Core/Config/ConfigManager.php
    @@ -102,7 +103,7 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
       public function getEntityTypeIdByName($name) {
         $entities = array_filter($this->entityManager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($name) {
    -      return ($config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0;
    +      return ($entity_type instanceof ConfigEntityTypeInterface && $config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0;
         });
         return key($entities);
       }
    

    Hmm. So if the entity type is an instance of ConfigEntityTypeInterface, but getConfigPrefix() returns something that is false, or the name doesn't begin with the config prefix expected... then what happens?

    This isn't introduced by this patch, but it seems like now this is a set of things that would mean the entity type were malformed if the first were true but not one of the others. I think. Can we get a followup to at least add an inline comment to document this, and maybe investigate whether this line is something that should be refactored?

  3. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
    @@ -15,44 +15,17 @@
    +   * The default configuration prefix is constructed from the name of the module
    +   * that provides the entity type and the ID of the entity type. If a
    +   * config_prefix annotation is present it will be used in place of the entity
    +   * type ID.
    
    +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityTypeInterface.php
    @@ -0,0 +1,64 @@
    +   * - The config prefix, which is returned by getConfigPrefix() and is
    +   *   composed of:
    +   *   - The provider module name (limited to 50 characters by
    +   *     DRUPAL_EXTENSION_NAME_MAX_LENGTH).
    +   *   - The module-specific namespace identifier, which defaults to the
    +   *     configuration entity type ID. Entity type IDs are limited to 32
    +   *     characters by EntityTypeInterface::ID_MAX_LENGTH.
    ...
    +   * Ensures that all configuration entities are prefixed by the module that
    +   * provides the configuration entity type. This ensures that if a
    +   * configuration entity is contained in a extension's default configuration it
    +   * be created during extension installation. Additionally, it allows
    +   * dependencies to be calculated without the modules that provide
    +   * configuration entity types being installed.
    

    So the first and third hunks of documentation are added by this patch, and the first is what we have in HEAD. That means we're explaining what a config prefix is composed of in why in different ways in three different places. We should consolidate that and clarify it.

    Normally, since these added lines of documentation are the majority of non-moved lines in the patch, I'd ask to fix this before it went in. However, @alexpott said that this issue is now blocking two criticals, so I'm okay for that to be done in a followup instead.

    I did fix a small bit of pirate speak on commit:

    diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityTypeInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityTypeInterface.php
    index 4b0fd15..a24c96f 100644
    --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityTypeInterface.php
    +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityTypeInterface.php
    @@ -51,8 +51,8 @@
        *
        * Ensures that all configuration entities are prefixed by the module that
        * provides the configuration entity type. This ensures that if a
    -   * configuration entity is contained in a extension's default configuration it
    -   * be created during extension installation. Additionally, it allows
    +   * configuration entity is contained in a extension's default configuration,
    +   * it will be created during extension installation. Additionally, it allows
        * dependencies to be calculated without the modules that provide
        * configuration entity types being installed.

    (Ensures it be created, arrr.)

Committed and pushed to 8.0.x. Thanks!

  • xjm committed b31bbb0 on 8.0.x
    Issue #2204697 by alexpott, tstoeckler, mauzeh, Berdir: Move...
xjm’s picture

Issue summary: View changes
joachim’s picture

This has broken #2409677: add a method to EntityTypeInterface to determine whether an entity type is content or config. It also affects contrib -- AFAIK, calling getConfigPrefix() was the simplest way to determine if a given entity is a config or content entity.

Berdir’s picture

Well, now the simplest way is instanceof ConfigEntityTypeInterface. Which is way more explicit.

The official way to do that so far was isSubclassOf(), wich I want to deprecate in favor of the instanceof check.

xjm’s picture

@joachim, core also does instanceof ConfigEntityType in a few places, which (per #41) need to be converted to instanceof ConfigEntityTypeInterface. See, for example, EntityManager::loadEntityByConfigTarget().

In #45 @Berdir is referring to #2194783: Rename EntityTypeInterface::isSubclassOf() to ::entityClassImplements().

jibran’s picture

Status: Fixed » Closed (fixed)

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