Skip to content

[5.x]: EntryQuery::type() ignores per-field entry-type handle overrides (5.6+), only matches the entry type's global handle #19482

Description

@antcooper

What happened?

Description

I'm migrating a large site from Craft 4 to 5. There were a lot of Matrix fields with blocks that used the same handle. These were renamed on migration to avoid conflicts e.g. copy -> copy2.

There's quite a lot of template code with conditionals that rely on .type() filter specific matrix blocks.

e.g {{ entry.pageContent.type('copy').all() }}

Since 5.6.0, an entry type's name/handle can be overridden per field usage (e.g. when adding an entry type to a Matrix field), via craft\services\Entries::getEntryType() / EntryType::$original. This override is reflected everywhere the field resolves its own entry types — e.g. Entry::getType()->handle inside a Matrix field context, and the CP's entry type chip for that field.

However, craft\elements\db\EntryQuery::type() does not seem to honor this override. For a string value, it resolves the handle via Entries::getEntryTypeByHandle(), which only checks each entry type's real/global handle column.

Result: filtering entry.someMatrixField.type('overriddenHandle') returns nothing, even though the CP and .type.handle both show overriddenHandle as the entry type's handle for that field. The real (global, often auto-renamed-on-upgrade) handle must be used instead.

Steps to reproduce

  1. Create an entry type Foo, real handle foo.
  2. Create a second entry type Bar, real handle bar.
  3. Add entry type Bar to a Matrix field, overriding its per-usage handle to foo (so it displays as foo for that field only).
  4. Create an entry in that Matrix field using entry type Bar.
  5. In a template: {{ entry.myMatrixField.type('foo').all() }} → returns null.
  6. {{ entry.myMatrixField.type('bar').all() }} → returns the expected entries.
  7. Meanwhile {{ entry.type.handle }} on those same entries prints foo, matching the CP and step 5's expectation, not step 6's.

Expected behavior

.type('foo') should match, since that's the handle the field/CP presents as authoritative for that usage.

Actual behavior

.type() only ever matches an entry type's real global handle, returning zero results for the override handle, with no warning that the override isn't query-able.

The upgrade migration auto-renamed colliding block types (e.g. copy, copy2, copy3) and applied the original shared name as a per-field handle override to preserve the old UX in the CP. Existing templates that filtered by the pre-upgrade handle now returns nothing on these fields.

Craft CMS version

5.10.14

PHP version

8.4

Operating system and version

No response

Database type and version

No response

Image driver and version

No response

Installed plugins and versions

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions