Problem/Motivation

EntityForm::submit() calls $entity->save() without running $entity->validate() first. IEF handles form-level #required checks, but entity-level constraints (unique fields, custom constraints, etc.) are never validated. If client-side
HTML5 validation is bypassed, this can cause a database integrity constraint violation (500 error).

Steps to reproduce

1. Set up an entity browser with an entity_form widget for a content type that has entity-level constraints beyond simple #required fields.
2. Open the browser, bypass HTML5 validation.
3. Submit with invalid data.

Expected: Validation errors shown.
Actual: Entity saved without constraint checks — possible 500 error.

Proposed resolution

Add validate() to EntityForm that runs $entity->validate() and maps violations to form errors.

Remaining tasks

User interface changes

API changes

Data model changes

Command icon 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:

Comments

saidatom created an issue. See original summary.

saidatom’s picture

Assigned: Unassigned » saidatom

saidatom’s picture

Assigned: saidatom » Unassigned
Status: Active » Needs review
benstallings’s picture

Status: Needs review » Needs work

Claude Code says:

This looks correct. One minor observation: prepareEntities() always returns a single-element array (line 129), so the foreach loop is technically always one iteration. But it's future-proof and matches the parent's pattern, so it's fine.

Test stabilization (existing tests):
- Replaces assertWaitOnAjaxRequest() + pageTextContains() with waitForText() — this is a good fix for race conditions in JS tests. The AJAX request may complete before the assertion runs, or the text may appear via a different mechanism.
- Adds waitForButton('Use selected') before pressing it (line 142) — prevents clicking a button that hasn't appeared yet.

New test testEntityFormRequiredFieldValidation():
- Creates a content type with a required field, configures a custom form display, and verifies:
- When the required field is visible: submission is blocked with a validation error, no entity saved, no SQL crash.
- When the required field is hidden: submission succeeds because hidden fields aren't validated.
- This directly validates the form-display-aware validation logic.

One concern: The #[RunTestsInSeparateProcesses] attribute (line 18) was added to the entire class. This is a heavy hammer — it forces every test method to run in isolation, significantly slowing the test suite. Was this needed to fix a specific test pollution issue? If the issue is only in the new test, consider moving the attribute to just that method, or investigating the root cause.

Verdict

The validation logic is sound and follows Drupal best practices. The test is thorough and well-structured. The only question is whether #[RunTestsInSeparateProcesses] is necessary at the class level or could be scoped more narrowly.

alorenc’s picture

Assigned: Unassigned » alorenc
alorenc’s picture

Added suggestions and remarks to the MR.

alorenc’s picture

Assigned: alorenc » Unassigned
saidatom’s picture

Status: Needs work » Needs review
alorenc’s picture

Status: Needs review » Needs work
saidatom’s picture

Status: Needs work » Needs review
alorenc’s picture

Assigned: Unassigned » alorenc
alorenc’s picture

Assigned: alorenc » Unassigned
Status: Needs review » Reviewed & tested by the community

anybody made their first commit to this issue’s fork.