Problem/Motivation

When creating an integer field as shown in http://drupal.stackexchange.com/questions/188367/how-to-add-uniquefield-... , the UniqueField constraint throws the following error:

The website encountered an unexpected error. Please try again later.
TypeError: Argument 1 passed to Drupal\Core\Form\FormState::setError() must be of the type array, null given, called in /var/www/html/web/core/lib/Drupal/Core/Field/WidgetBase.php on line 454 in Drupal\Core\Form\FormState->setError() (line 1156 of core/lib/Drupal/Core/Form/FormState.php).

I could reproduce this bug on core 8.1.7, 8.1.8, 8.2.0-beta1+33-dev (2016-Aug-14) and 8.3.x-dev (2016-Aug-14).

Steps to reproduce:

Proposed resolution

class NumberWidget extends WidgetBase {
  // ...
  public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
    if (!empty($element['value'])) {
      return $element['value'];
    }
    return $element[0]['value'];
  }
}

Remaining tasks

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gogowitsch created an issue. See original summary.

Gogowitsch’s picture

Issue summary: View changes
jlbellido’s picture

Version: 8.1.8 » 8.3.x-dev
Issue summary: View changes

I was able to reproduce it against the 8.3.x branch. This error is also happening when you add a custom constraint.

I've added steps to reproduce and updated the summary using the template.

jlbellido’s picture

Status: Active » Needs review
FileSize
751 bytes

I've tested the proposed solution and it works for me.
I'm attaching a patch based on the proposed solution by @Gogowitsch

Thanks!!

Berdir’s picture

Issue tags: +Needs tests

Thanks for the patch. Not sure I fully understand this yet, a test would help to explain in which situation this happens exactly.

krknth’s picture

I'm able to reproduce this. Here the steps i'v followed as per mentioned in the summary

  1. Fresh Drupal instalation (8.3.x), Added Number (integer) field with machine name - field_code(Code) to article content type
  2. Now, i'v enabled module with following code. Module name is - my_module
  3. File : my_module.info.yml

    name: My module
    type: module
    description: 'Test module'
    

    File : my_module.module

    <?php
    
    /**
     * Implements hook_entity_bundle_field_info_alter().
     */
    function my_module_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
       $a =1;
        if ($bundle === 'page' || $bundle === 'article') {
            if (isset($fields['field_code'])) {
                $fields['field_code']->addConstraint('MyModuleCodeUnique', []);
            }
        }
    }
    

    File : src/Plugin/Validation/Constraint/CodeUnique.php

    <?php
    
    namespace Drupal\my_module\Plugin\Validation\Constraint;
    
    use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
    
    /**
     * Checks if a field is unique.
     *
     * @Constraint(
     *   id = "MyModuleCodeUnique",
     *   label = @Translation("MyModule code unique", context = "Validation"),
     * )
     */
    class CodeUnique extends UniqueFieldConstraint {
    
        public $message = 'The code %value is already taken.';
    
    }
    
  4. Next, created a article with code field value as 1. And i tried to add another article with same code field value as 1. But i got error -

    The website encountered an unexpected error. Please try again later.

  5. Then i checked dblogs, and found this error
    TypeError: Argument 1 passed to Drupal\Core\Form\FormState::setError() must be of the type array, null given, called in /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Field/WidgetBase.php on line 439 in Drupal\Core\Form\FormState->setError() (line 1093 of /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormState.php) #0 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Field/WidgetBase.php(439): Drupal\Core\Form\FormState->setError(NULL, Object(Drupal\Core\StringTranslation\TranslatableMarkup)) #1 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php(255): Drupal\Core\Field\WidgetBase->flagErrors(Object(Drupal\Core\Field\FieldItemList), Object(Symfony\Component\Validator\ConstraintViolationList), Array, Object(Drupal\Core\Form\FormState)) #2 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Entity/ContentEntityForm.php(241): Drupal\Core\Entity\Entity\EntityFormDisplay->flagWidgetsErrorsFromViolations(Object(Drupal\Core\Entity\EntityConstraintViolationList), Array, Object(Drupal\Core\Form\FormState)) #3 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Entity/ContentEntityForm.php(187): Drupal\Core\Entity\ContentEntityForm->flagViolations(Object(Drupal\Core\Entity\EntityConstraintViolationList), Array, Object(Drupal\Core\Form\FormState)) #4 [internal function]: Drupal\Core\Entity\ContentEntityForm->validateForm(Array, Object(Drupal\Core\Form\FormState)) #5 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormValidator.php(83): call_user_func_array(Array, Array) #6 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormValidator.php(270): Drupal\Core\Form\FormValidator->executeValidateHandlers(Array, Object(Drupal\Core\Form\FormState)) #7 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormValidator.php(119): Drupal\Core\Form\FormValidator->doValidateForm(Array, Object(Drupal\Core\Form\FormState), 'node_article_fo...') #8 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormBuilder.php(571): Drupal\Core\Form\FormValidator->validateForm('node_article_fo...', Array, Object(Drupal\Core\Form\FormState)) #9 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Form/FormBuilder.php(314): Drupal\Core\Form\FormBuilder->processForm('node_article_fo...', Array, Object(Drupal\Core\Form\FormState)) #10 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Entity/EntityFormBuilder.php(48): Drupal\Core\Form\FormBuilder->buildForm('node_article_fo...', Object(Drupal\Core\Form\FormState)) #11 /var/www/html/drupals/git/drupal/core/modules/node/src/Controller/NodeController.php(113): Drupal\Core\Entity\EntityFormBuilder->getForm(Object(Drupal\node\Entity\Node)) #12 [internal function]: Drupal\node\Controller\NodeController->add(Object(Drupal\node\Entity\NodeType)) #13 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array) #14 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/Render/Renderer.php(574): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() #15 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure)) #16 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) #17 [internal function]: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() #18 /var/www/html/drupals/git/drupal/vendor/symfony/http-kernel/HttpKernel.php(144): call_user_func_array(Object(Closure), Array) #19 /var/www/html/drupals/git/drupal/vendor/symfony/http-kernel/HttpKernel.php(64): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1) #20 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #21 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #22 /var/www/html/drupals/git/drupal/core/modules/page_cache/src/StackMiddleware/PageCache.php(99): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #23 /var/www/html/drupals/git/drupal/core/modules/page_cache/src/StackMiddleware/PageCache.php(78): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true) #24 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #25 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(50): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #26 /var/www/html/drupals/git/drupal/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #27 /var/www/html/drupals/git/drupal/core/lib/Drupal/Core/DrupalKernel.php(656): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #28 /var/www/html/drupals/git/drupal/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request)) #29 {main}.
    

I did same steps after applying patch, and it worked for me.

Berdir’s picture

Component: entity system » field system
Priority: Normal » Major
Status: Needs review » Needs work

See also #2027059: Improve the documentation of WidgetBase::errorElement() for mapping violation property paths to form elements, which talks about a more generic fix and has a test. If that is fixed by this then we could maybe include that and get it in, because this will require a test to be committed.

I also think a fatal error when submitting a form qualifies as a major even though it requires custom code in this example, but #2027059: Improve the documentation of WidgetBase::errorElement() for mapping violation property paths to form elements sounds like it is enough to just have a required number field.

xjm’s picture

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

amateescu’s picture

amateescu’s picture

Title: UniqueField doesn't work on NumberWidget Entity field » Widget validation still crashes on ItemList violations for widgets with a custom errorElement() implementation
Status: Closed (duplicate) » Needs work
Issue tags: +Entity validation
Related issues: +#2319719: Widget validation crashes on ItemList violations
FileSize
2.25 KB

I looked into this a bit more after @ibustos's comment from #2901943-15: Content entity form validation does not respect the #limit_validation_errors property from field widgets and it's true that the patch over there does not fix this issue.

This is actually related to #2319719: Widget validation crashes on ItemList violations which didn't fix the problem entirely.

The problem is that \Drupal\Core\Entity\Entity\EntityFormDisplay::movePropertyPathViolationsRelativeToField() fails to provide a valid violation property path for widgets that have a custom imeplementation for \Drupal\Core\Field\WidgetInterface::errorElement().

Here's a test which shows the error. I'm not sure what the right fix should be yet..

andypost’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 11: 2784015-11-test-only.patch, failed testing. View results

ibustos’s picture

Good analysis @amateescu, definitely in the right direction. Looking at that function the problem seems to be that $violation->getPropertyPath(); does not return the correct property path.

That propertyPath property seems to be set at Drupal\Core\TypedData\Validation\RecursiveContextualValidator::validateNode() which is a recursive function.

I will be looking more into that.

amateescu’s picture

Status: Needs work » Needs review
FileSize
4.79 KB
3.44 KB

I dove into this today and it seems that I was wrong in #11, \Drupal\Core\Entity\Entity\EntityFormDisplay::movePropertyPathViolationsRelativeToField() works just fine and the actual problem is two-fold:

1. the purpose of \Drupal\Core\Field\WidgetInterface::errorElement() is to be called only when a) the widget handles multiple values (deltas) on its own and b) when the calling code already has a specific delta to work with
2. \Drupal\Core\Field\WidgetBaseInterface::flagErrors() does not split properly violations that are on the field item level from the ones that are on the field item list level

The only sane solution that I can think of is to make \Drupal\Core\Field\WidgetBaseInterface::flagErrors() bypass \Drupal\Core\Field\WidgetInterface::errorElement() for violations that are on the field item list level, so here's a patch that implements this proposed solution.

Note that only the conclusion from #11 was wrong, the test-only patch is correct :)

Status: Needs review » Needs work

The last submitted patch, 15: 2784015-15.patch, failed testing. View results

amateescu’s picture

Status: Needs work » Needs review
FileSize
3.05 KB
4.78 KB
1022 bytes

All the failures in #15 show the sad state of the entity_test module (the fact that all test entity types are installed on every functional test, even when the test itself doesn't use it) :/

Which means that we need to use a field type and a widget that are always available, so I picked the 'integer' field type and the 'number' widget.

The last submitted patch, 17: 2784015-17-test-only.patch, failed testing. View results

amateescu’s picture

Just closed a duplicate issue: #2840102: errorElement of Number Widget breaks with multivalue fields

It would be nice to have this fixed in 8.4.1 along with #2901943: Content entity form validation does not respect the #limit_validation_errors property from field widgets, which was already committed. Anyone up for a review? :)

CRZDEV’s picture

Patch 2784015-17.patch #17 seems to solve the problem, thanks!

amateescu’s picture

Title: Widget validation still crashes on ItemList violations for widgets with a custom errorElement() implementation » Widget validation crashes on ItemList violations for widgets with a custom errorElement() implementation
Related issues: +#2887731: EntityreferenceAutocompleteWidget constraint cannot stop form submission
amateescu’s picture

In the issue mentioned above I found out about #2574095: File upload and entity autocomplete results in fatal and #2569897: Required Long Text With Summary + form rebuild = PHP fatal error, which tried to fix the the errorElement() implementation of EntityReferenceAutocompleteWidget and TextareaWithSummaryWidget but didn't address the actual root cause.

So I think this issue should also take care of fixing the technical debt introduced by the two issues mentioned above. This interdiff basically reverts the incorrect fixes that were committed over there while still passing their tests.

schrammos’s picture

Patch #17 fixes the problem of #2614250 Number widget validation can break AJAX actions for me. Applied to D8.4.2

tim_zionandzion’s picture

Patch #17 fixes the problem of #2614250 Number widget validation can break AJAX actions for me. Applied to D8.4.2

Works for me as well in resolving the same issue in same version with a paragraph containing a number field on a content type that also contained a mutli taxonomy term field.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

aquaphenix’s picture

Component: field system » ajax system
Status: Needs review » Reviewed & tested by the community

I have tested the patch on #22 by amateescu and confirm the patch fixes the problem for me.

amateescu’s picture

Component: ajax system » field system
oknate’s picture

Patch #22 Fixed a bug where I couldn't remove a paragraph that held number widget, because of fatal error in ajax callback.

Notice: Undefined index: value in Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget->errorElement() (line 114 of /Users/oknate/dev/mysite/web/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php)

Thanks!

alexpott’s picture

Credited @krknth for explicit steps to reproduce, @Gogowitsch for reporting the issue, @ibustos for review comments,

alexpott’s picture

Committed 3b951a0 and pushed to 8.6.x. Thanks!

Leaving this at rtbc against 8.5.x since I think we should consider this for backport to 8.5.0 beta since this bug seems have affected lots of projects.

  • alexpott committed 3b951a0 on 8.6.x
    Issue #2784015 by amateescu, jlbellido, Gogowitsch, krknth, ibustos:...

  • catch committed 720b520 on 8.5.x authored by alexpott
    Issue #2784015 by amateescu, jlbellido, Gogowitsch, krknth, ibustos:...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Cherry-picked to 8.5.x, agreed on the backport.

Status: Fixed » Closed (fixed)

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