2

I have a custom entity with a field which is defined as follows.

$fields['file'] = BaseFieldDefinition::create('file')
  ->setDescription('Reference to the built-in core File entity type.')
  ->setLabel('File')
  ->setRequired(TRUE)
  ->setSetting('file_extensions', 'pdf rtf doc docx')
  ->setSetting('max_filesize', '20MB')
  ->setSetting('description_field', TRUE)
  ->setDisplayOptions('form', ['type' => 'file'])
  ->setDisplayOptions('view', ['type' => 'file']);

I have no problem getting to the fields of the referenced File entity from instances of my own entity type. So for example, these lines work as expected.

$filename = $my_entity->file->entity->filename->value;
$username = $my_entity->file->entity->uid->entity->name->value;

When I try to use the fields of the referenced File entity with a TableSort, which I want to plug into an entity query on my custom entity type, the code throws an exception.

$header['name'] = [
  'data' => 'File Name',
  'field' => 'file.entity.filename',
  'specifier' => 'file.entity.filename',
];
$header['user'] = [
  'data' => 'Uploaded By',
  'field' => 'file.entity.uid.entity.name',
  'specifier' => 'file.entity.uid.entity.name',
];

// …

$query->tableSort($header);

Uncaught PHP Exception Drupal\Core\Entity\Query\QueryException: "'file' not found"

For the syntax of the $header array I'm largely relying on clues I got from Tablesort with EntityQuery, in which Berdir recommends that we look at the load() and buildHeader() methods of the UserListBuilder class. Unfortunately, that example appears to be using field values which are stored directly in the base table, so I'm falling back on the assumption that, since we're in entity query land, the same syntax described in QueryInterface::condition() would be expected.

I've looked at a number of the open issues, such as Table sort ignores "field", always adds header title as order query parameter and Tablesort cleanup, but I didn't see anything which shed any light on the problem.

4
  • It's definitely supported with the syntax you're using, as this works fine for nodes using e.g. uid.entity.name as the specifier. Have you had any trouble installing this file field? Or maybe changed it after installation? Changed the cardinality, that sort of thing?
    – Clive
    Apr 29, 2022 at 20:50
  • @Clive - No problems installing the field, and no modification have been made to its configuration. I can populate the field perfectly well when creating or updating the entities, and can get to any of the field's properties using the standard entity field access syntax. Should I file a bug report (with a repro case), do you think?
    – Bob Kline
    Apr 29, 2022 at 23:25
  • I'm not sure if it's a bug or just something undocumented, there is a syntax which works for base file fields. I put an answer in but I'll hopefully update it when I have time to find out more
    – Clive
    Apr 30, 2022 at 21:04
  • @Clive - First of all, I'm very impressed that you figured this out. I have updated my bug report to reflect this new information. Short version: it's a bug in the documentation or the software, and I believe the stronger case can be made for making the software conform to the syntax currently documented for any core reference field type. I'm accepting your brilliant answer, even before you've had a chance to update it. 🥇 👍
    – Bob Kline
    May 1, 2022 at 11:28

2 Answers 2

2

There's a bug in core, and for the time being a workaround is:

'specifier' => 'file.target_id.entity.filename',

The reason for this is the entity query relationship syntax started out as join support, it predates the entity reference support in core and as such while $entity->file->target_id->entity is not supported by core, file.target_id.entity is supported by entity query to indicate it should join the entity specified by the file field and the entity id should be what is specified by target_id. It's a bit clunky but at the time, it was what worked.

It should be able to figure out file.entity.filename as well because the main property name for the file field happens to be target_id and so it is able to expand it for you into file.target_id.entity.filename. However, the class in case Drupal\Core\Entity\Query\Sql\Tables is extremely convoluted and this expansion has a bug for the shared table case which will be fixed in https://www.drupal.org/project/drupal/issues/3278083

9
  • So you're suggesting it's the documentation which needs to be fixed, not the code? Wouldn't be the first time. 😅
    – Bob Kline
    May 1, 2022 at 2:26
  • I'm accepting your excellent answer, though I'm not sure I understand what you mean in your explanation. I have countless examples of entity reference conditions in entity queries which rely on a reference field stored in the base table, and they work just fine with the documented syntax.
    – Bob Kline
    May 1, 2022 at 11:33
  • Yeah vanilla entity reference as a base field seems to work as you'd expect. If I was a betting man, without looking properly I'd say it's probably because the base entity reference field only has a single DB column (target_id), so there would be no need for the syntax to support the "sub-fields"; the fieldname__target_id column can be inferred (sub-fields is likely NOT an official term by the way :) ). File fields have 3 DB columns, so queries need to be told explicitly which file__* column in the base table needs to be used, hence the other syntax. Could still be something else though
    – Clive
    May 1, 2022 at 19:29
  • Found it - DefaultTableMapping::getFieldColumnName() is the smoking gun. If a field has only one column, its DB column name is the name of the field. So an entity reference field with an ID of uid, has a DB column named uid. When you query for uid.entity.*, there's a straight match on the DB column name and everything's fine. When a field has more than one column, the DB columns are named fieldname__property_name, so when you query for fieldname.entity, there's no matching fieldname DB column. The extended syntax is there to bridge that gap and let you select the right column
    – Clive
    May 1, 2022 at 20:10
  • Thanks for this excellent spelunking. I'll include a link to this page from the bug report. 👍
    – Bob Kline
    May 2, 2022 at 19:05
1

The failure described above appears to be the result of a bug in the FileItem plugin. Even though the field type plugin claims it is an entity reference (the @FieldType annotation says category = @Translation("Reference")), and in all other respects that is true, the support for the documented entity query syntax is broken.

Until the bug is fixed, the workaround would be to give up the specialization implemented by the FileItem field type and use a plain entity_reference field instead. With that change the TableSort code above works correctly.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.