Problem/Motivation

While trying to alter a node form to deny access to a boolean field, I faced an issue that disallow me to submit the form.
In a hook_form_alter I just changed #access on the field to FALSE.
When I submit the form I now get the following error:

Drupal\Core\Entity\EntityStorageException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: '' for column 'field_test_value' at row 1

Steps to reproduce:

  1. install Drupal standard
  2. add a boolean field named field_test on article content type not checked by default
  3. add a standard_form_alter function in standard.profile that adds #access FALSE to fied_test
  4. try to add a new article

Expected result: the node is created using the defaut value to the field_test field
Current result: EntityStorageException

Proposed resolution

TBD

Remaining tasks

Contributor tasks needed
Task Novice task? Contributor instructions Complete?
Create a patch Instructions Yes
Update the issue summary noting if allowed during the rc Template
Add automated tests Instructions

User interface changes

None.

API changes

None.

Data model changes

None.

CommentFileSizeAuthor
#56 boolean_field_with-2609252-56.patch8.95 KBGinovski
#56 interdiff-2609252-51-56.txt1.33 KBGinovski
#51 boolean_field_with-2609252-51.patch8.85 KBGinovski
#51 interdiff-test-2609252-49-51.txt3.51 KBGinovski
#51 interdiff-old-new-patch.txt1.05 KBGinovski
#51 test-only-old-patch-2781787-36.patch8.81 KBGinovski
#49 boolean_field_with-2609252-49.patch5.35 KBGinovski
#49 interdiff-2609252-36-49.txt1.16 KBGinovski
#36 boolean_field_with-2609252-36.patch5.3 KBeiriksm
#34 boolean_field_with-2609252-34.patch5.39 KBeiriksm
#34 interdiff-2609252.txt1.08 KBeiriksm
#31 boolean_field_with-2609252-31.patch5.41 KBeiriksm
#31 boolean_field_with-2609252-31-test-only.patch4.38 KBeiriksm
#29 boolean_field_with-2609252-29.patch4.94 KBeiriksm
#24 boolean_field_with-2609252-test_only-24.patch1.65 KBtoncic
#24 boolean_field_with-2609252-24.patch3.25 KBtoncic
#14 2609252-14-test-only.patch1.98 KBlokapujya
#13 2609252-13-test-only.patch0 byteslokapujya
#12 2609252-12-test-only.patch1.01 KBlokapujya
#5 field-schema-2609252-5.patch1.02 KBArla
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

DuaelFr created an issue. See original summary.

dawehner’s picture

That sounds like some default value is not applied? I guess your field doesn't have a default value, so maybe the NULL is passed along?

DuaelFr’s picture

Yep that totally sounds like you said.
The thing is that I'm talking about a boolean field added via Field UI so I have no way to set it's default value when I want it unchecked by default.

drubb’s picture

I've done a litte bit of debugging and found a difference regarding the values in $form_state supplied to the submit handlers:

- with '#access' set to true, $form_state->values['field_test']['value'] contains '0'
- with '#access' set to false, $form_state->values['field_test']['value'] contains 'false'

Looking at the form array, there's a difference, too:

- with '#access' set to true, '#default_value' contains 'false' and #value contains '0'
- with '#access' set to false, '#default_value' contains 'false' and #value contains 'false'

But in both cases, I don't get any errors.

Maybe it helps somehow.

Arla’s picture

Status: Active » Needs review
FileSize
1.02 KB

I accidentally created a duplicate of this: #2645316: "Incorrect integer value" for boolean fields with disabled access Reposting my patch from there. As stated there, "SqlContentEntityStorage uses the schema from the field definition to cast the value, but only for base fields (or more correctly, fields that live in the share table). I guess it should be done also for non-base fields."

swentel’s picture

Issue tags: +Needs tests

We'll definitely want a test for this

xjm’s picture

+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1244,7 +1244,8 @@ protected function saveToDedicatedTables(ContentEntityInterface $entity, $update
-            $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
+            $value = drupal_schema_get_field_value($attributes, $item->$column);
+            $record[$column_name] = !empty($attributes['serialize']) ? serialize($value) : $value;

The addition of a procedural function here caught my eye, but this class is also the only other place the function is used. So not in scope here to worry about that. Could be a followup.

The other usage has a comment above it that references #2279395: The PostgreSQL backend does not handle NULL values for serial fields gracefully; however, that other usage wasn't actually added in that patch. It was already there and has been at least since the EntityNG conversion.

alexpott’s picture

Issue tags: +Triaged core major

Discussed with @xjm and @catch. We agreed that this is a major bug as this is something that it is reasonable to expected to work and currently there is no way to programmatically disable a boolean field on an entity without causing this error.

Arla’s picture

Issue summary: View changes

Same error occurs if you're setting the boolean field to FALSE programmatically:

$node->field_test->value = FALSE;
$node->set('field_test', FALSE);

I'm surprised this aspect has not been discovered and repaired before.

(IS edit: adding "Yes" to the patch complete cell)

Berdir’s picture

Status: Needs review » Needs work

Needs work for tests.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

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

lokapujya’s picture

FileSize
1.01 KB

Ignore this one.

lokapujya’s picture

Status: Needs work » Needs review
FileSize
0 bytes

Ignore this one too.

lokapujya’s picture

Tried modifying a test to cover this situation.

lokapujya’s picture

The last submitted patch, 13: 2609252-13-test-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 14: 2609252-14-test-only.patch, failed testing.

lokapujya’s picture

  1. +++ b/core/modules/field/tests/modules/deny_access/deny_access.module
    @@ -0,0 +1,12 @@
    +function deny_access_form_alter(&$form, FormStateInterface $form_state, $form_id) {
    

    So, I created a module that sets the boolean field's #access to FALSE.

  2. +++ b/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
    @@ -58,7 +58,7 @@ function testBooleanField() {
    +    $field_name = 'booleanmachinename';//Unicode::strtolower($this->randomMachineName());
    

    Had to use an an actual name for the field in order to do it.

justAChris’s picture

@lokapujya: I don't think you should be modifying an existing test as that removes test coverage, try adding fields/cases to test instead

+++ b/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
@@ -58,7 +58,7 @@ function testBooleanField() {
+    $field_name = 'booleanmachinename';//Unicode::strtolower($this->randomMachineName());

Additionally, the resulting fails do not line up with the original error, perhaps because an existing test was modified:

exception: [Notice] Line 105 of core/modules/field/src/Tests/Boolean/BooleanFieldTest.php:
Undefined offset: 1Drupal\field\Tests\Boolean\BooleanFieldTest->testBooleanField() (Line: 1074)
lokapujya’s picture

That makes sense. This was just the quickest way to get started. Posted it to get the ball rolling. Is there a better place to put this test? Will wait for some more input before continuing. To anyone interested in contributing, feel free to go in a different direction if you want to post patch or add some more advice on creating this test.

Berdir’s picture

Just keep the existing part of the test as it is, and instead duplicate the relevant parts, possibly into a new testFormAccess or so and only there use your hardcoded name. Then in the form alter, check if the field is present and deny access if so.

Instead of a form alter, you could also implement hook_entity_field_access().

agentrickard’s picture

hook_entity_field_access() returns the same fatal error.

chx’s picture

drupal_schema_get_field_value is a really silly name it needs to be called (with a BC wrapper) drupal_schema_cast_field_value. Otherwise , great!

toncic’s picture

I tried to make new test case.

chx’s picture

I believe you forgot to git add the deny_access module and as such it is missing from the patch. Also, as #21and #22 says you can use hook_entity_field_access as well instead of form alter. Hook form alter #access FALSE 'ing a field widget in an entity form is dubious at best but most likely is completely wrong and hook_entity_field_access should be used instead.

The last submitted patch, 24: boolean_field_with-2609252-24.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 24: boolean_field_with-2609252-test_only-24.patch, failed testing.

eiriksm’s picture

Assigned: toncic » eiriksm

Just experienced this on a site. Working on this now, updating the patch per #25

eiriksm’s picture

Status: Needs work » Needs review
FileSize
4.94 KB

Status: Needs review » Needs work

The last submitted patch, 29: boolean_field_with-2609252-29.patch, failed testing.

eiriksm’s picture

OK, then. Was maybe a bit quick.

Here is a test-only patch and one with the suggested fix.

The last submitted patch, 31: boolean_field_with-2609252-31-test-only.patch, failed testing.

chx’s picture

Great! thanks for the quick reroll. You can call ->getName on the fieldDefinition and use AccessResult::forbiddenIf for shorter code.

eiriksm’s picture

Did not run the test, but here is a change as suggested. should(tm) work :)

chx’s picture

return AccessResult::forbiddenIf($field_definition->getName() === \Drupal::state()->get('field.test_boolean_field_access_field'));

forbiddenIf returns neutral if the condition is false and since we use === in the condition we know no funny business (like "foo" == 0) can ensue.

eiriksm’s picture

I posted that right before I went to bed, and realised that had to be what you meant. Almost got back up to post a new one :)

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

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

Berdir’s picture

Component: forms system » entity system
Soul88’s picture

Status: Needs review » Reviewed & tested by the community

Both tests and the fix look good to me.

alexpott’s picture

Status: Reviewed & tested by the community » Patch (to be ported)

Committed f4da728 and pushed to 8.3.x. Thanks!

Setting as 'patch to be ported' for cherry-pick to 8.2.x once 8.2.0 is out.

+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1257,7 +1257,9 @@ protected function saveToDedicatedTables(ContentEntityInterface $entity, $update
-            $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
+            $value = drupal_schema_get_field_value($attributes, $item->$column);
+            $record[$column_name] = !empty($attributes['serialize'])
+              ? serialize($value) : $value;

Wow - how painful that this database abstraction bleeds out into the entity storage layer. Funnily enough the only other usage of drupal_schema_get_field_value() is in SqlContentEntityStorage. It is tempting to ask for a followup issue to copy the logic into a castValue() method on this class and deprecate drupal_schema_get_field_value(). Doing that is certainly not in scope here.

diff --git a/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module b/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
index 9628d43..be56c37 100644
--- a/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
+++ b/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
@@ -1,10 +1,20 @@
 <?php
+
+/**
+ * @file
+ * Helper module for the boolean field tests.
+ *
+ * @see \Drupal\field\Tests\Boolean\BooleanFieldTest::testFormAccess()
+ */
+
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
 
 /**
  * Implements hook_entity_field_access().
  */
-function field_test_boolean_access_denied_entity_field_access($operation, FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
+function field_test_boolean_access_denied_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
   return AccessResult::forbiddenIf($field_definition->getName() === \Drupal::state()->get('field.test_boolean_field_access_field'));
 }

Coding standards fixes on commit.

  • alexpott committed f4da728 on 8.3.x
    Issue #2609252 by eiriksm, lokapujya, toncic92, Arla, chx, Berdir:...
alexpott’s picture

Status: Patch (to be ported) » Fixed

Committed e0fd432 and pushed to 8.2.x. Thanks!

  • alexpott committed e0fd432 on 8.2.x
    Issue #2609252 by eiriksm, lokapujya, toncic92, Arla, chx, Berdir:...

  • alexpott committed 01ed544 on 8.3.x
    Revert "Issue #2609252 by eiriksm, lokapujya, toncic92, Arla, chx,...

  • alexpott committed 0109905 on 8.2.x
    Revert "Issue #2609252 by eiriksm, lokapujya, toncic92, Arla, chx,...
alexpott’s picture

Status: Fixed » Needs work

This broke something in Drupal commerce. The serialization needs to occur before the call to drupal_schema_get_field_value() just as in \Drupal\Core\Entity\Sql\SqlContentEntityStorage::mapToStorageRecord(). And we need to add a test for this.

bojanz’s picture

The reason why this commit broke things is because it calls drupal_schema_get_field_value() before serialization, which attempts to cast an object to a string.

Possibly related: #2788023: Serialized single-value base field columns are not unserialized when loading entities and #2788637: Values in shared table for SQL content entity storage do not get unserialized..

mikeegoulding’s picture

This appears to still be an issue in 8.2.2

Ginovski’s picture

Status: Needs work » Needs review
FileSize
1.16 KB
5.35 KB

Added serialization before drupal_schema_get_field_value() is called.

Berdir’s picture

@bojanz: Do we have something in core to be able to test this? Do we need a field type?

Ginovski’s picture

Added test to assert the serialization.
4 Files in attachment:
1. Test-only with the old patch
2. Interdiff between old patch and new (serialization made before drupal_schema_get_field())
3. Interdiff (the added test for the new patch)
4. Final working patch with test

The last submitted patch, 51: test-only-old-patch-2781787-36.patch, failed testing.

Berdir’s picture

Nice, just two tiny things and we should be good to go.

  1. +++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
    @@ -0,0 +1,61 @@
    + *   description = @Translation("Dummy object field type used for tests."),
    

    copied I think?

    Lets write "Test field type that has an object as value to test serialization." as the description.

  2. +++ b/core/modules/field/tests/src/Kernel/TestObjectItemTest.php
    @@ -0,0 +1,52 @@
    +
    +  public function testTestObjectItem() {
    

    missing docblock

Berdir’s picture

Note that this actually also gives us test coverage for #2788637: Values in shared table for SQL content entity storage do not get unserialized., we just need to remove the @todo there.

Berdir’s picture

Status: Needs review » Needs work
Ginovski’s picture

Status: Needs work » Needs review
FileSize
1.33 KB
8.95 KB

Added description and missing docs for testObjectItem() function.

slimbk’s picture

Thank you it works!!

Berdir’s picture

Assigned: eiriksm » Unassigned
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Great work, I think this is ready to be committed again and hopefully stay in this time :)

See #51 for failing test.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 44227b1 to 8.3.x and 9d41d1c to 8.2.x. Thanks!

diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
index b8050d5..a20265e 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
@@ -58,4 +58,5 @@ public function setValue($values, $notify = TRUE) {
     }
     parent::setValue($values, $notify);
   }
+
 }
diff --git a/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module b/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
index 9628d43..e1ae2f7 100644
--- a/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
+++ b/core/modules/field/tests/modules/field_test_boolean_access_denied/field_test_boolean_access_denied.module
@@ -1,10 +1,18 @@
 <?php
+
+/**
+ * @file
+ * Module for testing denying access to boolean fields.
+ */
+
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
 
 /**
  * Implements hook_entity_field_access().
  */
-function field_test_boolean_access_denied_entity_field_access($operation, FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
+function field_test_boolean_access_denied_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
   return AccessResult::forbiddenIf($field_definition->getName() === \Drupal::state()->get('field.test_boolean_field_access_field'));
 }
diff --git a/core/modules/field/tests/src/Kernel/TestObjectItemTest.php b/core/modules/field/tests/src/Kernel/TestObjectItemTest.php
index 7443fa6..8a78755 100644
--- a/core/modules/field/tests/src/Kernel/TestObjectItemTest.php
+++ b/core/modules/field/tests/src/Kernel/TestObjectItemTest.php
@@ -20,6 +20,9 @@ class TestObjectItemTest extends FieldKernelTestBase {
    */
   public static $modules = array('field_test');
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
@@ -52,4 +55,5 @@ public function testTestObjectItem() {
     $this->assertTrue($entity->field_test->value instanceof \stdClass);
     $this->assertEquals($object, $entity->field_test->value);
   }
+
 }

Coding standards fixes on commit.

  • alexpott committed 44227b1 on 8.3.x
    Issue #2609252 by eiriksm, Ginovski, lokapujya, toncic, Arla, Berdir,...

  • alexpott committed 9d41d1c on 8.2.x
    Issue #2609252 by eiriksm, Ginovski, lokapujya, toncic, Arla, Berdir,...

Status: Fixed » Closed (fixed)

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