Problem/Motivation
If an entity type returns NULL from label(), then the controller title callbacks for the edit and delete pages will result in the following errors as shown in the test-only output:
1) Drupal\Tests\Core\Entity\Controller\EntityControllerTest::testDeleteEditTitleCallbacks with data set #1 ('deleteTitle', null, 'Delete')
TypeError: Drupal\Component\Utility\Html::escape(): Argument #1 ($text) must be of type string, null given, called in /var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php on line 238
/var/www/html/core/lib/Drupal/Component/Utility/Html.php:433
/var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php:238
/var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php:211
/var/www/html/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php:195
/var/www/html/core/lib/Drupal/Component/Utility/ToStringTrait.php:15
/var/www/html/core/tests/Drupal/Tests/Core/Entity/Controller/EntityControllerTest.php:55
2) Drupal\Tests\Core\Entity\Controller\EntityControllerTest::testDeleteEditTitleCallbacks with data set #3 ('editTitle', null, 'Edit')
TypeError: Drupal\Component\Utility\Html::escape(): Argument #1 ($text) must be of type string, null given, called in /var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php on line 238
/var/www/html/core/lib/Drupal/Component/Utility/Html.php:433
/var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php:238
/var/www/html/core/lib/Drupal/Component/Render/FormattableMarkup.php:211
/var/www/html/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php:195
/var/www/html/core/lib/Drupal/Component/Utility/ToStringTrait.php:15
/var/www/html/core/tests/Drupal/Tests/Core/Entity/Controller/EntityControllerTest.php:55
Steps to reproduce
- create entity class with drush gen content-entity
- remove label field from code
- open edit entity form
Proposed resolution
Return only "Delete" and "Edit" from the respective title callbacks in the case of an entity label() returning NULL.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3319212
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3319212-null-label-error
changes, plain diff MR !13726
Comments
Comment #2
andrew answer commentedPatch provided.
Comment #3
bhanu951 commentedSetting it to need review for test bot and review.
Comment #4
joachim commented> return $this->t('Edit %label', ['%label' => $entity->label() ?? '']);
That'll produce a stray space - 'Edit ' instead of 'Edit'.
Comment #5
andrew answer commented@joachim fixed.
Comment #6
joachim commentedThat should probably just be:
because that way it also covers the case where label() returns an empty string.
Comment #7
andrew answer commented@joachim,
usually a label is required (if it exists), so it cannot be an empty string. Anyway, if label is an empty string, is this my case?
is_null is mandatory because this way we can detect the absence of a label field.
The patch should have space and two CR at the end, added.
Comment #8
andrew answer commentedComment #9
andrew answer commentedComment #10
andrew answer commentedComment #11
penyaskitoI agree with @joachim, if empty we should return "Edit" and not "Edit " (extra space).
This needs tests.
Comment #13
othermachines commentedHere's a re-rolled patch (11.1.x.)
Comment #14
borisson_Still needs tests, the remark in #11 seems to be fixed.
Comment #17
dcam commentedI converted the patch from #13 to an MR and added a Unit test. Then I realized that
EntityController::deleteTitle()has the exact same problem. So I expanded the scope to include it.It's a bit odd.
::deleteTitle()returns an empty string if the entity can't be loaded. Whereas::editTitle()returns nothing (a NULL value). My new test does not cover these cases, partly because it would require different mocking and partly because I feel like making the two callbacks behave the same maybe ought to be handled in a follow-up issue. In which case the NULL entity test cases can be written at that time. Not my call though and that's WAY out of scope for this bug fix.Comment #18
geek-merlinHope this can be addressed in the related issue. See comment #3504477-20: Allow all Renderable values in SDC slots.
UPS: I suppose this is the wrong issue.
Comment #19
geek-merlinComment #20
joachim commentedGood catch on the other callbacks!
And nice job making the test a unit test.
But I'm really not keen on the mixing of if/else and ternary that there is now.
We could maybe use the nullsafe operator like this (pseudocode):
Comment #21
dcam commentedYeah, it was kind of ugly. Unfortunately, the nullsafe operator doesn't help here because the functions don't return values based only on the label. They also return something in the case of NULL entities. Still, I didn't try to clean up the callbacks before, so I did it now. I'm not sure that I like this much either, but at least it should be easy to understand.
Comment #22
joachim commented> Unfortunately, the nullsafe operator doesn't help here because the functions don't return values based only on the label. They also return something in the case of NULL entities.
Sorry, I don't follow.
We start with:
We have either an EntityInterface, or NULL, according to the docs for that method.
If it's NULL, then $entity?->label() is NULL.
If it's not NULL, then $entity?->label() returns the label or NULL according to its docs.
So in all cases, $entity?->label() is NULL or the label.
So we can do:
Comment #23
dcam commentedCurrently, if the
$entityis NULL theneditTitle()returns NULL anddeleteTitle()returns an empty string. This is separate from what happens iflabel()returns a value or NULL. If we want to maintain the current behavior of the functions, then we have to handle the case of a missing entity.Comment #24
joachim commentedAh right, thanks for explaining.
When editTitle() returns NULL it'll cause the same TypeError when it's output, so that's problematic too. We should always be returning a string, an empty one at worst.
It's going to be a really rare pathway though - you'd need to be on a route that has say node/42/edit as a parent path, and node 42 doesn't exist, so that the path breadcrumb builder tries to get the page title for node/42/edit.
Comment #25
pingwin4egI get a similar issue with deleted entities that had no labels, it is a kind of mix of the issue described here and #3554974: FormattableMarkup: NULL @ and % placeholder values should log a notice and use empty string.
EntityDeleteFormTraitandDeleteMultipleFormuselabel()method while logging.So I'm thinking, would it be better to return an ID from the default
label()method (inContentEntityBaseor even inEntityBaseclass)?EntityDeleteFormTraitalready uses IDs in messages and strings presented to the user (but not in the log message for some reason):I'd only prepend an ID with the # character, but this is a personal preference, also an ID can be non-numeric.
Comment #27
smustgrave commentedEchoing the sentiment about the unit tests, pasting the test-only run here too for others https://git.drupalcode.org/issue/drupal-3319212/-/jobs/8306385
From what I can tell there are no open threads and the changes make sense to me. Don't mind marking for you.
Comment #29
longwave@pingwin4eg I think that is worth exploring in a followup - should all entities have (non empty) labels by default?
In the meantime let's solve this immediate issue by committing this. Doesn't backport cleanly to 10.6, so this is going in 11.3 and up.