Spin-off from #2064191: Fix the 'target_bundle' field setting - 'target_bundles' are not validated when entity_reference is enabled

Problem/Motivation

The "selection handler" plugins are in charge of defining which entities can be referenced by an e_r field. However, they don't take part in the validation of entity_ref fields :
- ERItem::getConstraints() only hardcodes a check against the list of referenceable bundles, if the field uses the default "selection handler" plugin. That was added as a quickfix in #2064191: Fix the 'target_bundle' field setting - 'target_bundles' are not validated when entity_reference is enabled.
- Other selection handlers are left out of the validation process entirely. That includes ViewsSelector, which lets you define referenceable entities using a View, or even the various NodeSelector, UserSelector, that extend the default "by bundle" SelectorBase by refining the logic a bit (e.g "can't reference an unpublished node")

What is affected :
- REST operations
- The "options" widget are immune by design (they only let you select valid options), but they are seldom used (unusable with 20+ referenceable entities)
- The autocomplete widget is affected : it doesn't call the Selection plugin's validateReferenceableEntities(), because it uses the EntityAutocomplete FAPI element with #validate_reference = FALSE, which is correct for a widget (leave the real data validation to data-level constraints, to avoid doing the work twice), except the ValidReference constraint will do it (which it doesn't atm).
Meaning, the autocompletion list only shows valid options, but the user can manually type "whatever (42)", with 42 being the ID of any entity of the target type, and no error will be reported (other than the 'invalid bundle' violation caught by our hardcoded Bundle constraint). Any other restriction imposed by the Selection plugin is ignored .
- Other (contrib/custom) widgets can be affected as well, depending on their implementation.

Proposed resolution

It should be the selection handler plugins that define what is checked during validation, since they are the ones that already contain the logic for "what is referenceable for this field".

In #2064191: Fix the 'target_bundle' field setting - 'target_bundles' are not validated when entity_reference is enabled, it was considered to repurpose the existing ValidReference constraint to operate on the FieldItemList level (rather than Item-by-Item, which is a perf drag), and have its validator :
- collect the target_ids across each Item, call the *existing* $selector_plugin->validateReferenceableEntities($ids), and flag a violation on each delta whose target_id is deemed invalid
- collect the fresh ("autosaved") entities across each item, call a *new* $selector_plugin->validateNewEntities($entities) method (name TBD), and flag a violation on each delta whose entity is deemed invalid

As a side note : ViewsSelector is inherently incompatible with "autosave". You cannot validate that an $entity that isn't in the database yet will match a given View. How fun :-) That would probably need taking into account for the field config UI in #2412569: Target bundle for auto-created entity references with multiple target bundles. For the validation issue here, I guess ViewsSelector::isEntityValid() would just say "no / not supported" for unsaved entities.

Remaining tasks

- Decide/refine on the new SelectorInterface::validateNewEntities($entities)
- Fix tests

API changes

A new method would be needed on SelectorInterface to validate entities that are not in the db yet.

CommentFileSizeAuthor
#74 interdiff.txt10.88 KBamateescu
#74 2577963-74.patch39.98 KBamateescu
#74 2577963-74-test-only.patch6.48 KBamateescu
#71 interdiff.txt9.46 KBamateescu
#71 2577963-71.patch33.29 KBamateescu
#65 2577963-65.patch31.98 KBamateescu
#57 interdiff.txt732 bytesamateescu
#57 2577963-57.patch31.89 KBamateescu
#54 interdiff.txt16.29 KBamateescu
#54 2577963-54.patch31.17 KBamateescu
#44 interdiff.txt7.87 KByched
#40 2577963-ER_validation-40.patch23.47 KByched
#39 2577963-ER_validation-39.patch18.55 KByched
#34 interdiff.txt1.27 KByched
#34 2577963-ER_validation-34.patch21.56 KByched
#33 interdiff.txt16.12 KByched
#33 2577963-ER_validation-33.patch21.68 KByched
#26 interdiff.txt1.79 KByched
#26 2577963-ER_validation-26.patch21.77 KByched
#20 interdiff.txt1.86 KByched
#20 2577963-ER_validation-20.patch21.97 KByched
#17 interdiff.txt2.83 KByched
#17 2577963-ER_validation-17.patch20.75 KByched
#11 interdiff.txt5.52 KByched
#11 2577963-ER_validation-11.patch19.24 KByched
#7 interdiff.txt714 bytesyched
#7 2577963-ER_validation-7.patch13.72 KByched
#3 2577963-ER_validation-2.patch13.37 KByched
#2 2577963-ER_validation-1.patch14.6 KByched
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched created an issue. See original summary.

yched’s picture

Status: Active » Needs review
FileSize
14.6 KB

Attached is my 1st attempt at a patch.

As I found out in #2576501: Helper issue for ER validation [#2577963] , relying on Selection::validateReferenceableEntities() and its EntityQuery seems to throw many errors.

- Some of them are caused by the fact that NodeSelector, UserSelector, ... refine their EntityQueries with extra conditions (e.g user.status = 1), thus further restricting the set of valid references. Those tests would likely need to be adjusted (e.g the referenced $user used by the test must have its status = 1)

- Some of them are weird fails in the infamous \Drupal\Core\Entity\Query\Sql\Tables::addField() :-/
Like : CommentNonNodeTest, fails because Tables::addField() can't find the 'status' field on users.
Couldn't get to the bottom of those yet.

yched’s picture

Without the stale update, leftover from the previous issue.

The last submitted patch, 2: 2577963-ER_validation-1.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 3: 2577963-ER_validation-2.patch, failed testing.

yched’s picture

Those weird EntityQuery fails are probably related to this code from EntityReferenceItem :

  public static function defaultFieldSettings() {
    return array(
      'handler' => 'default:' . (\Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user'),

This means that base e_r fieds that don't bother specifying a 'handler' setting, get a handler for a completely unrelated entity type (user or node, depending on what modules are enabled) ?
That looks potentially very problematic, even outside of this issue here :-)

IMO we should have a method on ERItem / ERItemList to "get the selection handler", and that method would then take care of "if 'default' then 'default:$target_entity_type" ?

yched’s picture

Status: Needs work » Needs review
FileSize
13.72 KB
714 bytes

So, "if 'default' then 'default:$target_entity_type" is already taken care of by SelectionPluginManager::getInstance().

Then maybe the default value for 'handler' setting should just be 'default', without trying to guess an arbitrary derivative ?

The last submitted patch, 2: 2577963-ER_validation-1.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 7: 2577963-ER_validation-7.patch, failed testing.

The last submitted patch, 3: 2577963-ER_validation-2.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
19.24 KB
5.52 KB

A couple easy fixes.

yched’s picture

Opened a separate issue for #7 and "the default value for 'handler' setting should just be 'default', without trying to guess an arbitrary derivative", because that would be good fixing regardless of this issue here.

#2578249: Some e_r fields get the wrong Selection handler

Status: Needs review » Needs work

The last submitted patch, 11: 2577963-ER_validation-11.patch, failed testing.

yched’s picture

Looks like FileSelector's $query->condition('status', FILE_STATUS_PERMANENT); messes with a lot of tests doing file uploads. Investigating.

yched’s picture

Yeah, so the issue is that newly uploaded files remain with status = 0 until the containing node has been saved (not fully sure when that happens exactly).

So they are still at status 0 when we run validation, and then FileSelection::validateReferenceableEntities() doesn't find them in its EntityQuery.

Also of note : this status = 0 condition in the validateReferenceableEntities() EntityQuery is added for file fields by FileSelection, but not for image fields since there is no ImageSelection.

yched’s picture

File status going from 0 to 1 happens dunring FileFieldItemList::postSave() during the save of the parent entity.

So when we validate the entity, newly uploaded files are not "valid references" in the sense of FileSelection::validateReferenceableEntities(). They only become "valid" when the entity gets saved.

Damn. That seems like a serious dent in the 'validation should use the same (efficient) logic than SelectionInterface::validateReferenceableEntities()" approach:-/

Unless validateReferenceableEntities() takes a second param that lets FileSelection be more tolerant with status :-/

yched’s picture

Status: Needs work » Needs review
FileSize
20.75 KB
2.83 KB

Unless validateReferenceableEntities() takes a second param that lets FileSelection be more tolerant with status :-/

Giving a shot at that. Still raw/undocumented, just checking where that brings us with tests.

amateescu’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php
@@ -49,6 +49,20 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON
+   * Validates that entities can be referenced by this field.

Selection handlers are not tied to the ER field anymore, so this one-line description shouldn't mention it either. The same is true for the issue title for that matter :)

Status: Needs review » Needs work

The last submitted patch, 17: 2577963-ER_validation-17.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
21.97 KB
1.86 KB

Haven't fully processed #18 :-)

Meanwhile, this might be green.
(some image related tests failed because of the

field.field.node.article.field_image.yml : handler: 'default:node'
field.field.user.user.user_picture.yml : handler: 'default:node'

weirdness mentioned in #2578249-7: Some e_r fields get the wrong Selection handler)

yched’s picture

@amateescu #18 : ok, but that comment is just being consistent with the existing one for validateReferenceableEntities() ?

Also, this issue is not tying Selection handlers to know about e_r fields, but is making the ValidReferenceConstraint, used by e_r fields, use the Selection handlers assigned o the field.
(Selection handlers don't know about fields, but fields do rely on Selection handlers, right ?)

amateescu’s picture

Selection handlers don't know about fields, but fields do rely on Selection handlers, right ?

Yes :)

And the description of SelectionInterface::validateReferenceableEntities() in HEAD is the one that's wrong, not the fault of this patch, sorry :/

Status: Needs review » Needs work

The last submitted patch, 20: 2577963-ER_validation-20.patch, failed testing.

The last submitted patch, 7: 2577963-ER_validation-7.patch, failed testing.

The last submitted patch, 11: 2577963-ER_validation-11.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
21.77 KB
1.79 KB

And that one should be green.

The adjustments in Selection plugins (new validateReferenceableNewEntities() method, new $strict param in validateReferenceableEntities()) are still rough, but aiming for a green bot atm :-)

The last submitted patch, 20: 2577963-ER_validation-20.patch, failed testing.

The last submitted patch, 17: 2577963-ER_validation-17.patch, failed testing.

yched’s picture

Issue tags: -beta target +rc deadline

That's actually an rc deadline, since this necessarily means adding a new method to SelectionInterface

yched’s picture

Issue summary: View changes

Expanded the IS about why the autocreate widget is affected (accepts any input, only "has correct bundle ?" will ever be checked)

yched’s picture

That "$strict = TRUE/FALSE" additonal param in validateReferenceableEntities() to be able to bypass the 'file.status' = 0 check is really messy - and would de facto mean REST operations become able to reference non-permanent files. Not good.

Instead, what about turning FileSelection::buildEntityQuery() into : "file.status = 1 OR (file.uid = current user AND file.created < now() - 1 or 2 seconds)" ?
That would allow the valdation to run while the newly uploaded file hasn't been made permanent yet by the parent entity being saved ?

yched’s picture

Priority: Normal » Major

Also, the holes in validation, even on the most common widget (autocreate), still make this at least major IMO.

yched’s picture

Patch :
- Removes the awkward $strict param that was added in #17, and instead adjusts the EntityQuery in FileSelection to be tolerant on file uploads (as per #31)
- Differentiates between "entity is not referenceable" and "entity does not exist" in error messages (adds one EntityQuery, but only if there are violations). Reverts a couple tests to the "does not exist" message.

Still @todo :
Adjust the shape of the new validateReferenceableNewEntities() method (current one is not great DX to override in sub classes)

yched’s picture

Silly me.

Allow "file.status = 1 OR (file.uid = current user AND file.created < now() - 1 or 2 seconds)"

That works for tests, but not IRL of course, because this will pass validation for the ajax upload, but will then fail when the user finally submits the whole entity form (also, my silly code checked for 2 minutes instead of two seconds...)

So the window has to be larger (how long do we allow you to keep your entity form opened before you actually submit it ? a couple hours ? a day ?) or even just "allow temp file created by the current user, no time restriction" :/

Going for the latter (no time restriction on temporary file created by the current user)...

fago’s picture

The change makes totally sense. Had a short look at the patch:

  1. +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php
    @@ -49,6 +49,24 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON
    +   * Validates that entities can be referenced.
    +   *
    +   * @todo naming...
    

    validateReferenceableAutoCreatedEntities() maybe ?

  2. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php
    @@ -26,10 +26,24 @@ class ValidReferenceConstraint extends Constraint {
    +  public $invalidAutocreateMessage = 'This entity (%type: %label)cannot be referenced.';
    

    missing space before "cannot"

yched’s picture

IMO that here approach would also be the correct fix for #2587957: Arbitrary files can be referenced in file fields even if validation using form API would fail (recent critical)

catch’s picture

Issue tags: -rc deadline +rc target triage
amateescu’s picture

The patch looks great to me!

I can't decide if validateReferenceableAutoCreatedEntities() is more clear than the current method name, I'm ok with either of them.

yched’s picture

yched’s picture

Then starting to apply the rule of "new (autocreate) entities should have the same checks than existing entities" (i.e validateReferenceableNewEntities() must apply the same logic on Entity objects than buildEntityQuery() does for entity queries).

This is where things get funny - the test fails reveal that autocreate widget is broken with nodes :-)
- The autocreate widget creates nodes with just the input string as label and that's it, meaning node.status stays at 0.
- But you're not supposed to reference unpublished nodes, and NodeSelection now checks that on autocreate entities too.
Thus the current test for the autocreate widget, EntityReferenceAutoCreateTest, that is based on creating/referencing nodes, fails.

The underlying bug is that the autocreate widget creates unpublished nodes, which is probably not the desired behavior to begin with.
By adding the checks, the patch here exposes that upfront and makes the autocreate widget useless/broken on nodes (will always fail validation when trying to create a new node and reference it)

yched’s picture

Going on a vacation in 2 days, so I don't think I'll be able to push this forward myself. Just wanted to post what I had so far.

The last submitted patch, 39: 2577963-ER_validation-39.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 40: 2577963-ER_validation-40.patch, failed testing.

yched’s picture

FileSize
7.87 KB

Forgot the interdiff for #40.

drnikki’s picture

xjm’s picture

Issue tags: -rc target triage

Since this is critical now, it no longer needs to be triaged as an RC target. Thanks!

amateescu’s picture

The underlying bug is that the autocreate widget creates unpublished nodes, which is probably not the desired behavior to begin with.

The fix for this should be pretty simple, we just move the responsibility of creating the new entity to the selection handlers, because they already have hardcoded logic about what makes an entity referenceable :)

yched’s picture

@amateescu : yep, probably. That would also let entity types with no label key support autocreate ?
And would also let us more clearly state that ViewSelection doesn't support autocreate.

Which brings : maybe those two new methods on the Selection (create the fresh entity, check that the new entity is valid) could be a secondary interface (SelectionWithAutocreate), thus removing the API break ?

I'm not where I can work on that myself though ;-)

amateescu’s picture

Hm.. having a separate interface would also help with hiding the autocreate feature in the UI if the target entity type doesn't support it, but that means we would need to provide a new selection handler for each core / contrib / custom entity type, which is.. not very DX friendly :/

About having two methods, one to create the entity and one to validate it, isn't that a bit superfluous? I mean.. the method that creates the new entity should also know very well if it's valid or not.

I can work on the patch once we reach an agreement on the direction :)

yched’s picture

About having two methods, one to create the entity and one to validate it, isn't that a bit superfluous? I mean.. the method that creates the new entity should also know very well if it's valid or not.

Wondered about that too, but the "validate" method is needed for REST and other direct API uses. The "create" method would be used only by the widgets in form/UI flows.

amateescu’s picture

Right, two methods it is then. Now we just need to settle on separate interface or not :)

swentel’s picture

+++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
@@ -260,6 +260,15 @@ public function validateReferenceableEntities(array $ids) {
+    // ViewsSelection does not supprt autocreate, there is no way to check that

typo: supprt

yched’s picture

@amateescu: oops, I missed #51 somehow.

Not too easy for me to reason on the inheritance model without being able to look at the codebase. But what if :
- autocreate methods were added to a secondary interface,
- DefaultSelection doesn't implement that interface, but the individual selection derivatives can, helped if needed by a trait providing base implementations ?

Then autocreate becomes opt-in by entity type, that's kind of a behavior break, but it also sort of reflects the current state of affairs (autocreate is mostly supported on taxo terms)

Maybe I'm missing things, other suggestions welcome, as I said I'm not in the best position atm ;-)

amateescu’s picture

Status: Needs work » Needs review
FileSize
31.17 KB
16.29 KB

Then autocreate becomes opt-in by entity type, that's kind of a behavior break, but it also sort of reflects the current state of affairs (autocreate is mostly supported on taxo terms)

I think I'm more in favor of keeping autocreate as opt-out by default, for the reasons mentioned in #49.

Here's a patch that fixes the creation of new entities for core entity types by moving the createNewEntity() method to the selection handlers. Both new methods are now on a new interface, as discussed above, and DefaultSelection implements this interface in order to keep the current behavior from HEAD.

Status: Needs review » Needs work

The last submitted patch, 54: 2577963-54.patch, failed testing.

yched’s picture

#54 : works for me :-)

Still a behavior change though : if your custom selection class doesn't implement the new interface, no autocreate. That's probably not likely to affect a lot of existing code, but we'd need to run that by core committers regarding an RC / minor version strategy.

Also, would be good to hear DER people about this patch in general, I guess ?

amateescu’s picture

Status: Needs work » Needs review
FileSize
31.89 KB
732 bytes

The test fail from #54 is caused by a test form element that was supposed to have its validation disabled but it didn't :/

Still a behavior change though : if your custom selection class doesn't implement the new interface, no autocreate.

We could write a short change notice for this, which should also mentioned that \Drupal\Core\Entity\Element\EntityAutocomplete::createNewEntity() was moved to the new "selection with autocreate" interface, but I'll leave it to core committers to decide if we need it or not.

jibran’s picture

+++ b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php
@@ -18,6 +18,16 @@ class EntityReferenceFieldItemList extends FieldItemList implements EntityRefere
+    $constraints[] = $constraint_manager->create('ValidReference', []);

I tried the above patch and DER is failing tests because of this. DER doesn't use ValidReference constraint. It has its own ValidDynamicReference constraint so after this patch DER has to unset ValidReference constraint in DERFieldItemList class which we can do in #2405467: Add EntityType and Bundle constraint on entity property of DynamicEntityReferenceItem so no biggie.

Other then constraint DER doesn't touch EntityAutocomplete or EntityReferenceSelection just use them as is so moving the method createNewEntity won't affect DER at all.

+1 form DER.

alexpott’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record

If we're going to have a behaviour change then we need a CR

amateescu’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record

The last submitted patch, 39: 2577963-ER_validation-39.patch, failed testing.

The last submitted patch, 40: 2577963-ER_validation-40.patch, failed testing.

The last submitted patch, 54: 2577963-54.patch, failed testing.

klausi’s picture

Status: Needs review » Needs work
amateescu’s picture

Status: Needs work » Needs review
FileSize
31.98 KB

No worries, here's a reroll :)

Status: Needs review » Needs work

The last submitted patch, 65: 2577963-65.patch, failed testing.

klausi’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php
    @@ -7,39 +7,142 @@
    +  public function __construct(SelectionPluginManagerInterface $selection_manager, EntityManagerInterface $entity_manager) {
    +    $this->selectionManager = $selection_manager;
    +    $this->entityManager = $entity_manager;
    

    entity_manager is deprecated, use one of the new services.

  2. +++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
    @@ -42,6 +42,32 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
    +    // In order to create a referenceable comment, it needs to published.
    +    $entity->status->value = CommentInterface::PUBLISHED;
    

    can we use setPublished() here if we know that the entity is of CommentInterface? When we deal with base fields we should never use the weird $entity->{$random_field_name}->value access method but always the dedicated methods on the interface.

  3. +++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
    @@ -42,6 +42,32 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
    +      $entities = array_filter($entities, function ($entity) {
    +        return $entity->status->value === CommentInterface::PUBLISHED;
    +      });
    

    same here, setPublished().

  4. +++ b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php
    @@ -27,8 +27,39 @@ class FileSelection extends DefaultSelection {
    +    // In order to create a referenceable file, it needs to have a "permanent"
    +    // status.
    +    $entity->status->value = FILE_STATUS_PERMANENT;
    

    can we use setPermanent() here from FileInterface?

  5. +++ b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php
    @@ -27,8 +27,39 @@ class FileSelection extends DefaultSelection {
    +    $entities = array_filter($entities, function ($entity) {
    +      return $entity->status->value === FILE_STATUS_PERMANENT || $entity->uid->target_id === $this->currentUser->id();
    +    });
    

    can we use isPermanent() and getOwnerId() here form FileInterface?

I'm a bit worried about new files that are set to permanent immediately, but I'm not familiar enough with the ER selection handling code, would have to test that.

klausi’s picture

+++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
@@ -42,6 +42,32 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
+    $entity = parent::createNewEntity($entity_type_id, $bundle, $label, $uid);

We should probably not call the variable $entity here but rather $comment instead. That way it is immediately clear that we are dealing with comments only here, because the selection handler is only for comments anyway.

Also in all the other selection handler places added by this patch.

klausi’s picture

Hm, we only allow auto creation of new entities on taxonomy term reference fields in core. There is currently no way to auto create a file through an entity reference field.

In order to even get an entity reference field that references files (not a file field!) you need to add a field referencing nodes, then edit it and switch it to referencing files.

I don't see the use case for using auto_create with files since there is no form in Drupal that would allow you creating files. The one and only place to add files is via field fields, file entities are completely dependent on that field, they can only be created there. The additions to FileSelection.php look a bit bogus to me, since that code can never be used in Drupal core. Maybe we should let that open for some contrib module like media that knows more about the use case of file creation/selection? Or maybe I'm just missing something here.

klausi’s picture

+++ b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php
@@ -18,6 +18,16 @@ class EntityReferenceFieldItemList extends FieldItemList implements EntityRefere
+    $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();

should use $this->getTypedDataManager() instead of \Drupal.

amateescu’s picture

Status: Needs work » Needs review
FileSize
33.29 KB
9.46 KB

Thanks for the reviews, @klausi :)

This patch fixes #67, #68, #70 and the current test failures.

Hm, we only allow auto creation of new entities on taxonomy term reference fields in core. There is currently no way to auto create a file through an entity reference field.

That's only true for the UI, the "autocreate" API in HEAD supports all entity types. For example, this piece of code uses the autocreate/autosave functionality:


$user = User::create([
  'name' => 'username',
  'status' => 1,
]);

// Don't call $user->save();

$referencing_entity->user_reference_field->entity = $user;

// At this point, we should be checking if the referenced user entity is valid, which we don't do in HEAD and it's what this patch fixes.
$referencing_entity->validate();

// At this point, the referenced user entity will also be saved as part of the "parent" save process.
$referencing_entity->save();
In order to even get an entity reference field that references files (not a file field!) you need to add a field referencing nodes, then edit it and switch it to referencing files.

I don't understand this at all.. can you explain in a bit more detail what you see as not working?

Berdir’s picture

I think what he meant is that the "File" option under References is a file field and not an entity reference field. But you don't need to select node first and then change, you can also select other and then select file.

But yes, it's definitely not possible to create a working new file through a normal entity reference field, unlike terms or other things, creating the entity isn't enough, you actually need a file behind it. As @yched commented in the other issue, the only possible use case is using that for validation through the inherited behavior/configuration in a file field reference instead of the custom constraint that we added now.

klausi’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionWithAutocreateInterface.php
    @@ -0,0 +1,52 @@
    +   * This method should replicate the logic implemented by
    +   * validateReferenceableEntities(), but applied to newly created entities that
    +   * have not been saved yet.
    

    what validateReferenceableEntities() method do you mean? It does not exist on this interface? Should you point to another interface that has this method? Or should this interface extend the other interface?

  2. +++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
    @@ -42,6 +42,33 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
    +        return $entity->status->value === CommentInterface::PUBLISHED;
    

    we should use $comment as variable name and $comment->isPublished()

  3. +++ b/core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php
    @@ -48,4 +48,31 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
    +      $entities = array_filter($entities, function ($entity) {
    +        return $entity->status->value === NODE_PUBLISHED;
    +      });
    

    same here, let's use $node and ->isPublished().

  4. +++ b/core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php
    @@ -171,6 +171,40 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
    +      $entities = array_filter($entities, function ($entity) {
    +        return $entity->status->value === 1;
    +      });
    

    same here, let's use $user and ->isActive()

This patch does not add any new test coverage, could you list in the issue summary which test cases cover the new code? I'm having a hard time finding them. Basically there should be a test somewhere that adds an invalid + unsaved entity into a reference field of another valid entity, then calls validate() on that entity and get the violations from the invalid referenced entity.

It seems that EntityAutocomplete::validateEntityAutocomplete() duplicates the work of ValidReferenceConstraintValidator a bit, but since EntityAutocomplete is a form element that must function independently of the entity validation system this should be fine.

Otherwise the changes make sense to me.

amateescu’s picture

Status: Needs work » Needs review
FileSize
6.48 KB
39.98 KB
10.88 KB

Fixed all the points from #73 and added test coverage. Thanks for reviewing again :)

klausi’s picture

Status: Needs review » Needs work
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
@@ -367,6 +381,100 @@ public function testValidation() {
+    $title = $this->randomString();

Please no random data in tests, see #2571183: Deprecate random() usage in tests to avoid random test failures. Please remove all random*() calls from the lines that you add and use hard-coded strings instead.

The test does not cover the case where multiple entities are referenced in one field. We should at least have one that mixes multiple existing and new entities in one field so that we test that the array_diff/reduce stuff works.

The last submitted patch, 74: 2577963-74-test-only.patch, failed testing.

effulgentsia’s picture

I discussed this issue with @catch and @xjm, so want to update folks here that RC4 will be tagged within the next couple of hours and will be the final RC. If this doesn't land by then, I think we'll need to do the following:

  1. Move this issue to 8.1 for the parts that can be done in a minor, and if necessary, open an issue for 9.x to do the rest.
  2. Open a different critical issue for 8.0 to at least add text to the UI that lets people know that when they configure the ER selection, that it's only for UX purposes of the widget, and not a firm constraint (e.g., a content editor can bypass it via the autocomplete widget by manually typing a different ID). Such UI text could potentially be committed between RC4 and 8.0.0.
catch’s picture

Status: Needs work » Reviewed & tested by the community

The randomness in the tests should not block commit, until #2571183: Deprecate random() usage in tests to avoid random test failures lands nothing is deprecated, and we've not agreed to block patches for using those methods yet. I agree with dropping the randomness in general though.

The extra test coverage would be great, but I think this patch gets perilous for 8.x at all once RC4 is tagged, so I would strongly consider doing that in a follow-up.

Or to put it differently:

- if we put it into RC4 as is, and something horrible is picked up before 8.0.0 is tagged via RC4 testing, we can roll it back.

- if we put it in after RC4, we might not pick anything horrible up until after 8.0.0 is tagged.

- if we don't put it in at all, that is horrible.

So the first option, for me at least, is the least horrible.

I don't see this being fixable in 8.1.x as is, I think we'd have to do something like https://www.drupal.org/project/field_validation in core and then have an 'entity reference selection handler validator' which could get messy fast just to me what for me is a natural user expectation of the current interface.

Given all of those things, moving to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

I agree with catch's comment in #78 - committing this patch as-is is probably the best way to go. I think we're missing test coverage where multiple new entities are valid and invalid and where we mix new and existing entities.

Committed fbe0991 and pushed to 8.0.x. Thanks!

diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
index 4d1a257..6dd6e10 100644
--- a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -14,7 +14,6 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element\Textfield;
 use Drupal\Core\Site\Settings;
-use Drupal\user\EntityOwnerInterface;
 
 /**
  * Provides an entity autocomplete form element.

Removed unused use on commit.

catch’s picture

The last submitted patch, 39: 2577963-ER_validation-39.patch, failed testing.

The last submitted patch, 40: 2577963-ER_validation-40.patch, failed testing.

The last submitted patch, 54: 2577963-54.patch, failed testing.

The last submitted patch, 57: 2577963-57.patch, failed testing.

The last submitted patch, 65: 2577963-65.patch, failed testing.

The last submitted patch, 71: 2577963-71.patch, failed testing.

The last submitted patch, 74: 2577963-74-test-only.patch, failed testing.

Status: Fixed » Needs work

The last submitted patch, 74: 2577963-74.patch, failed testing.

kylebrowning’s picture

Status: Needs work » Fixed

per #79.

Berdir’s picture

+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -40,7 +40,6 @@
  *   default_formatter = "entity_reference_label",
  *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
- *   constraints = {"ValidReference" = {}}
  * )

There are at least three contrib modules that used that constraint like this. Payment, Simplenews and Entity Reference Revisions.

The problem is that ValidReferenceConstraintValidator dies with a fatal error if you still have that as it expects a list of field items now and not a single one.

Would have been nice to avoid an error but we need to at least mention this in the change record. Not quite sure what to write, is it enough to just remove this or do those modules need to do anything?

  • alexpott committed fbe0991 on 8.1.x
    Issue #2577963 by yched, amateescu, klausi: Let entity_ref Selection...
amateescu’s picture

As discussed with @Berdir, I updated the change record to mention the changes needed for the ValidReference constraint: https://www.drupal.org/node/2606656/revisions/view/9103328/9127282

Status: Fixed » Closed (fixed)

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