Follow-up to #2030637: Expand FieldConfig/BaseFieldOverride/FieldConfigBase with methods

Part of meta-issue #2016679: Expand Entity Type interfaces to provide methods, protect the properties.

Drupal\Core\Field\FieldConfigBase class variables should not be accessed directly. There is only one variable FieldConfigBase::default_value left to set protected.Functions should be use to access the variable. For instance use getDescription() and setDescription($description) for the protected class variable description. For a boolean variable the getter function becomes isVariableName(). In object-oriented programming this is called encapsulation.

Remaining tasks

  • Update the class variables and make them protected.
  • Create getters and setters for frequently used get and set functionality.
  • Update drupal to use the getters and setters instead of accessing variables directly.
  • There are no tests required because the added functions are only getters and setters.

For more info over what should be done see the issue summary of #2016679: Expand Entity Type interfaces to provide methods, protect the properties.

Maintainer decision

The naming of getter and setter function is getNameOfTheVariable() and setNameOfTheVariable(). For the variable $default_value would that result in getDefaultValue() and setDefaultValue(). The setter function is already in core and sets the value of $default_value. The getter function is also in core and is used for something else. There are three viable options from which we can choose:

  1. Keep the function getDefaultValue as is and use an other name for the getter function. For instance getTheDefaultValue();
  2. Rename the current function getDefaultValue() (to for instance getDefaultValueEntity()) and use getDefaultValue for the getter function.
  3. Combine both functions in getDefaultValue(). If it has a parameter then use it as the current function and if there is no parameter use it as the getter function (function getDefaultValue(FieldableEntityInterface $entity = NULL)).

What we need is a maintainers decision for which solution to choose.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because properties should not be public, API methods should not be allowed to be sidestepped.
Prioritized changes Prioritized since it is a bug and it reduces fragility.
Disruption Somewhat disruptive for core as well as contributed and custom modules:
  • BC break for anything using the public properties: code will need to convert to the methods
  • BC break for anything (mis)using properties that should not really be public: will require minor refactoring
  • BC break for alternate implementations of a given entity interface (rare/probably nonexistent): they will need to implement the new methods

But impact will be greater than the disruption, so it is allowed in the beta.

CommentFileSizeAuthor
#48 interdiff.txt2.08 KByched
#48 2529034-field-config-base-default-48.patch28.06 KByched
#44 2529034-diff-39-44-without-callback-pass.txt2.62 KBvijaycs85
#44 2529034-diff-39-44-with-callback-fail.txt2.41 KBvijaycs85
#44 2529034-44-with-callback-fail.patch27.55 KBvijaycs85
#44 2529034-44-without-callback-pass.patch27.57 KBvijaycs85
#39 2529034-37-39.txt579 bytesvijaycs85
#39 2529034-39.patch27.1 KBvijaycs85
#37 2529034-field-config-base-default-37.patch27.08 KBpenyaskito
#37 2529034-field-config-base-default.interdiff.34-37.txt1.53 KBpenyaskito
#34 2529034-field-config-base-default-34.patch27.01 KBpenyaskito
#34 2529034-field-config-base-default.interdiff.31-34.txt3.06 KBpenyaskito
#31 2529034-field-config-base-default-31.patch23.95 KBpenyaskito
#31 2529034-field-config-base-default.interdiff.27-31.txt2 KBpenyaskito
#27 2529034-field-config-base-default.interdiff.21-27.txt609 bytespenyaskito
#27 2529034-field-config-base-default-27.patch23.18 KBpenyaskito
#21 2529034-field-config-base-default.interdiff.19-21.txt3.75 KBpenyaskito
#21 2529034-field-config-base-default-21.patch22.59 KBpenyaskito
#19 2529034-field-config-base-default.interdiff.17-19.txt14.59 KBpenyaskito
#19 2529034-field-config-base-default-19.patch21.63 KBpenyaskito
#17 2529034-field-config-base-default-17.patch15.15 KBpenyaskito
#13 2529034-field-config-base-default-13.patch13.6 KBpenyaskito
#13 2529034-field-config-base-default.interdiff.8-13.txt10.55 KBpenyaskito
#12 2529034-field-config-base-default-12.patch30.13 KBpenyaskito
#12 2529034-field-config-base-default.interdiff.8-12.txt10.55 KBpenyaskito
#8 2529034-field-config-base-default.interdiff.5-8.txt4.32 KBpenyaskito
#8 2529034-field-config-base-default-8.patch12.55 KBpenyaskito
#5 2529034-field-config-base-default.interdiff.2-5.txt1.09 KBpenyaskito
#5 2529034-field-config-base-default-5.patch9.15 KBpenyaskito
#2 field-config-base-default-2.patch8.06 KBpenyaskito
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

daffie’s picture

Title: Replace direct access to FieldConfig::default_value with methods » Replace direct access to FieldConfigBase::default_value with methods
penyaskito’s picture

Status: Active » Needs review
FileSize
8.06 KB

Rolling the ball. FieldConfigBase::getDefaultValue($entity) already exists, so it's hard to name properly how to get the default value for the field config. Suggestions welcome.

penyaskito’s picture

+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -284,11 +284,12 @@ public function hasNewEntity() {
   public static function calculateDependencies(FieldDefinitionInterface $field_definition) {
...
-    if (is_array($field_definition->default_value) && count($field_definition->default_value)) {
+    $default_value = $field_definition->getTheDefaultValue();

getTheDefaultValue() has been added to FieldConfigInterface, but maybe should it be on FieldDefinitionInterface?

Status: Needs review » Needs work

The last submitted patch, 2: field-config-base-default-2.patch, failed testing.

penyaskito’s picture

Changing a missed reference to the property.

andypost’s picture

+++ b/core/modules/comment/comment.module
@@ -143,11 +143,12 @@ function comment_theme() {
 function comment_field_config_create(FieldConfigInterface $field) {
...
+    $default_value = $field->getTheDefaultValue();
+    if (!isset($default_value)) {
...
+    $default_value += array(array());
+    $default_value[0] += array(
       'status' => CommentItemInterface::OPEN,

Maybe now it's possible to make comment field default value with this method... follow-up

Status: Needs review » Needs work

The last submitted patch, 5: 2529034-field-config-base-default-5.patch, failed testing.

penyaskito’s picture

Fix failures, missed some accesses to the variable.

Mile23’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Field/FieldConfigInterface.php
@@ -94,6 +94,19 @@ public function setSetting($setting_name, $value);
   /**
+   * Gets the set default value.
+   *
+   * Note that if a default value callback is set, it will take precedence over
+   * any value here.
+   *
+   * @return mixed $value
+   *   The default value in the format as returned by
+   *   \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
+   *
+   */
+  public function getTheDefaultValue();

So FieldDefinitionInterface already has a getDefaultValue() (no 'the'). Then FieldConfigInterface has setDefaultValue() which defines its parameter as the output of FieldDefinitionInterface::getDefaultValue().

FieldConfigBase then implements FieldDefinitionInterface, which as you can see accesses default_value according to some logic.

This means that FieldConfigBase::getDefaultValue() and FieldConfigBase::setDefaultValue() already implements the encapsulation we desire, or at least should.

This patch should not define getTheDefaultValue() and should instead focus on having calling code use the interface rather than the public method.

penyaskito’s picture

FieldDefinitionInterface::getDefaultValue receives an FieldableEntityInterface, which sometimes we don't have if working directly with field definitions. That's why I defined a new getter. That entity is needed because we call later to ::processDefaultValue, which needs the entity.

Should we ignore that if the entity is null?

Mile23’s picture

I guess I really don't know. :-)

Having a setDefaultValue() and also a setTheDefaultValue() seems rather confusing in terms of DX.

penyaskito’s picture

Assigned: Unassigned » penyaskito
Status: Needs work » Needs review
FileSize
10.55 KB
30.13 KB

Let's try allowing NULLs then.

penyaskito’s picture

penyaskito’s picture

Assigned: penyaskito » Unassigned

Yay

daffie’s picture

Issue summary: View changes
Issue tags: +Needs subsystem maintainer review

We need a maintainers decision which function naming we should use. I have updated the IS with the maintainers options as I see them.

yched’s picture

So, summarizing:

The runtime default value of a field can be specified in two different ways, each with its associated setter :
- default_value / setDefaultValue() : a litteral value,
- default_value_callback / setDefaultValueCallback() : a callback, possibly accepting the host $entity as a param.

Those are currently combined in a single "getter", getDefaultValue(), whose intent is to compute the runtime default value for a given field in a given entity, based on one of the two underlying properties (callback if present, else litteral). It is thus not really a getter for the two basic properties above, instead it smushes/resolves them to a runtime litteral.
And there are no real getters for the underlying properties (litteral / callback)

--> I'd suggest we keep getDefaultValue() for what it currently is, and add separate, dedicated getters for those two params :
FieldDefinitionInterface::getDefaultValueLitteral() / getDefaultValueCallback(),
with proper docs stating that getDefaultValue($entity) should be used instead for determining the actual runtime default value for a given field in a given entity.

Then we'd have :
setDefaultValue($value) : set as litteral value
setDefaultValueCallback($callback) : set as a callback
getDefaultValueLitteral() : get the litteral value
getDefaultValueCallback() : get the callback
getDefaultValue($entity) : get/compute the runtime default value for an entity

It's not entirely symmetric (setDefaultValue() is the setter for which getDefaultValueLitteral() is the getter), but IMO that's still the most intuitive DX solution. I'm not sure we want to rename setDefaultValue() to setDefaultValueLitteral() at this point.

Also, I notice that FieldConfigBase currently misses the setDefaultValueCallback() setter for default_value_callback (BaseFieldDefinition has it). That was probably an overlook in #2030637: Expand FieldConfig/BaseFieldOverride/FieldConfigBase with methods, we could add it here as well.

penyaskito’s picture

Makes sense to me, wondered if we wanted to really expose those methods.
This patch covers what yched asked for if I understood correctly.

yched’s picture

Status: Needs review » Needs work

Thanks @penyaskito ! A couple remarks :

  1. +++ b/core/lib/Drupal/Core/Field/FieldConfigInterface.php
    @@ -94,6 +94,19 @@ public function setSetting($setting_name, $value);
    +   * Gets the set default value.
    +   *
    +   * Note that if a default value callback is set, it will take precedence over
    +   * any value here.
    +   *
    +   * @return mixed $value
    +   *   The default value in the format as returned by
    +   *   \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
    +   *
    +   */
    +  public function getDefaultValueLiteral();
    

    Why do we redefine this here ? It's already defined in FDI, which FCI extends.

    What we need to add in FCI is setDefaultValueCallback(), see next item below :-)

  2. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -520,6 +534,14 @@ public function setDefaultValue($value) {
       /**
        * {@inheritdoc}
        */
    +  public function setDefaultValueCallback($value) {
    

    Great, but the @inheritdoc is a lie atm :-), the method is not defined/documented in any interface currently implemented by FieldConfigBase

    We need to add it to FieldConfigInterface, that's where the various setters live atm.

  3. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -230,6 +230,20 @@ public function getTargetBundle() {
    +  public function getDefaultValueLiteral() {
    +    return $this->default_value;
    +  }
    +
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function getDefaultValueCallback() {
    +    return $this->default_value_callback;
    +  }
    

    Minor, code organisation : the rest of the class tries to group getters and setters together (getters first), let's do the same here.

  4. +++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
    @@ -179,6 +179,30 @@ public function getDisplayOptions($display_context);
    +   * If you want the runtime default value, you should call ::getDefaultValue
    

    Not sure of our standards here, but shouldn't we add a trailing () after the method name ?

    For completion and clarity, I'd suggest that crossreference getDefaultValueLiteral(), getDefaultValueCallback(), getDefaultValue() all cross-reference each other. Something like :

    - getDefaultValueLiteral() / getDefaultValueCallback()
    "This method retrives the raw property assigned to the field definition. When computing the runtime default value for a field in a given entity, ::getDefaultValue() should be used instead."

    - getDefaultValue() :
    "This method computes the runtime default value for a field in a given entity. To access the raw properties assigned to the field definition, ::getDefaultValueLiteral() / ::getDefaultValueCallback() should be used instead.

  5. +++ b/core/modules/comment/comment.module
    @@ -143,11 +143,12 @@ function comment_theme() {
    +    $default_value = $field->getDefaultValueLiteral();
    +    if (!isset($default_value)) {
    

    The isset() looks weird now that we extract a variable. An is_null() would be more explicit ?

    In fact, now that we have getters / setters, we could ensure that we always get and set array() instead of NULL, and remove that check here.

    The phpdoc in HEAD for FieldDefinitionInterface::getDefaultValue() already states that it always returns an array, empty for "no default".
    The phpdoc for the newly added FieldDefinitionInterface::getDefaultValueLiteral() says the same, so it should already be the case ?

  6. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
    @@ -284,11 +284,12 @@ public function hasNewEntity() {
    +    if (is_array($default_value) && count($default_value)) {
    

    If we enforce the getter always returns an array, the is_array() check shouldn't be needed, and we could just do
    if ($default_value = $this->getDefaultValueLitteral())

  7. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
    @@ -305,19 +306,23 @@ public static function calculateDependencies(FieldDefinitionInterface $field_def
    +    $default_value = $field_definition->getDefaultValueLiteral();
    +    if (!empty($default_value)) {
    

    Same here, could be if ($default_value = ...). And the "if ($changed)" block at the end should move inside that first if(), after the foreach().

  8. +++ b/core/modules/comment/src/Tests/CommentFieldsTest.php
    @@ -61,7 +61,8 @@ function testCommentDefaultFields() {
    -    $this->assertEqual($field->default_value[0]['status'], CommentItemInterface::CLOSED);
    +    $default_value = $field->getDefaultValueLiteral();
    +    $this->assertEqual($default_value[0]['status'], CommentItemInterface::CLOSED);
    

    Minor, but we don't really *need* to extract a var, this could stay inlined. Feel free to ignore :-)

penyaskito’s picture

This one should make @yched happier :-)

yched’s picture

Thanks @penyaskito. A couple more details :-)

  1. +++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
    @@ -430,6 +430,20 @@ public function isDisplayConfigurable($display_context) {
    +  public function getDefaultValueLiteral() {
    +    return isset($this->definition['default_value']) ? $this->definition['default_value'] : NULL;
    

    Actually, according to the convention and PHPdoc, the "not set" case should return [] rather than NULL ?

    + just like you did in FieldConfigBase, the getDefaultValue() method in BaseFieldDefinition could call getDefaultValueLiteral() rather than duplicating that line ?

    (actually, if getDefaultValue() only relied on getDefaultValueLiteral() / getDefaultValueCallback(), it could be moved to a trait and shared between FieldConfigBase & BaseFieldDefinition. Let's call that a followup though, I think there are more common runtime logic that could be shared in a trait between the two)

  2. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -403,19 +403,61 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
    +  public function setDefaultValue($value) {
    +    if (!is_array($value)) {
    +      $key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
    +      // Convert to the multi value format to support fields with a cardinality
    +      // greater than 1.
           $value = array(
    -        array($property => $value),
    +        array($key => $value),
           );
    

    That's incorrect for an incoming $value === NULL, this will end up assigning a default value with NULL in the first property.

    We should be converting NULL to [] here, as getDefaultValueLitteral() does - in fact, no need to do it in both places, it should be enough to do it here in the setter.

    Likewise, no need to do the "normalize scalar to array using the first property" processing in both the getter and setter, doing it in the setter should be enough (we probablky need to keep it in getDefaultValue() that does "compute callback or litteral", since it currently allows the callback to return NULL or a scalar, we don't want a BC break here)

    Similar "non-duplicate processing / single-point of entry" logic should probably be checked in the equivalent methods in BaseFieldDefinition too ?

penyaskito’s picture

Oh, good catch. I double-checked that the default value is an empty array and it cannot be set to a NULL value.
Removed the logic in getters and reviewed it on setters on FileConfigBase and BaseFieldDefinition.

Status: Needs review » Needs work

The last submitted patch, 21: 2529034-field-config-base-default-21.patch, failed testing.

penyaskito’s picture

@yched So this is an API change, or should we allow NULLs then? I'm not sure I got it right.

yched’s picture

@penyaskito: we should keep allowing NULL, but have the setter internally translate it to [] (empty array) before assigning it to a definition property. That preserves BC.

penyaskito’s picture

@yched: The test that fails is because after that, getting the value will return array() instead of NULL, is that right and should we change the test, or if the property is an empty array we should be returning NULL?

yched’s picture

Yes, the getter returns [] for "no default value", that is what its phpdoc says.
"empty array" is the official value for "no default value". The setter accepts NULL instead for BC, but internally we always store [].

penyaskito’s picture

Changed the test expected output then.

yched’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs subsystem maintainer review +Needs change record

Thanks @penyaskito ! Looks good.

This will likely need a change record though.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. Yep we need a CR. It's good to see @yched's involvement in this issue. I like the current solution and I think it makes default value handling of fields more robust.
  2. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -146,7 +146,7 @@
    -  public $default_value = array();
    +  protected $default_value = array();
    

    Is there no $default_value_callback property to protect? Hmmm... yes and it is already protected. We still have public access of it...

    if (empty($this->getFieldDefinition()->default_value_callback)) {
    

    in Drupal\Core\Field\FieldItemList::defaultValuesForm() and Drupal\datetime\Plugin\Field\FieldType::defaultValuesForm(). Is this broken atm?

  3. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -419,6 +411,47 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
    +  public function setDefaultValueCallback($value) {
    +    $this->default_value_callback = $value;
    

    $value should be $callback like the interface.

yched’s picture

#29.2 - indeed. That doesn't fatal out because it's inside an empty() check, which eats the incorrect property access and always returns TRUE (see http://3v4l.org/9A8aB), but the underlying functionnality (don't show the UI to set a litteral default value when there is a default value callback) is indeed broken.

penyaskito’s picture

Fixed both access to default_value_callback. Should we have tests covering that?
Fixed also the argument name.

penyaskito’s picture

Issue tags: -Needs change record

Drafted a change record.

yched’s picture

Yeah, I guess we miss a test that checks that if a FieldConfig has a default_value_callback set, then the "Edit Field" UI doesn't show the "default value" input widget :-/

penyaskito’s picture

WIP of the test for that. There are some errors related to providing config schema, but aside of that it should work.

Status: Needs review » Needs work

The last submitted patch, 34: 2529034-field-config-base-default-34.patch, failed testing.

vijaycs85’s picture

few minor points:

  1. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -418,6 +410,47 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
    +  public function getDefaultValueLiteral() {
    ...
    +  public function setDefaultValue($value) {
    

    Minor: Would be nice to have both setter/getter same naming. Do you see anything wrong with getDefaultValue()?

  2. +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
    @@ -418,6 +410,47 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
    +      if ($value === NULL) {
    +        $value = [];
    +      }
    

    Do we really need this check? If the value is empty, don't we get warning already?

    <?php
    
    class Foo {
        protected $bar;
        public function setBar($value) {
            $this->bar = $value;
        }
    }
    
    $object = new Foo();
    $object->setBar();
    
    // Result:
    Warning: Missing argument 1 for Foo::setBar(), called in /in/kG7ps on line 11 and defined in /in/kG7ps on line 5
    
    Notice: Undefined variable: value in /in/kG7ps on line 6
    

    May be we can just stick with old isset()

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
    @@ -306,18 +306,21 @@ public static function calculateDependencies(FieldDefinitionInterface $field_def
    +      if ($changed) {
    +        $field_definition->setDefaultValue($default_value);
    +      }
    

    Doesn't look like this is scope of this issue.

  4. +++ b/core/modules/comment/src/Tests/CommentFieldsTest.php
    @@ -60,8 +60,8 @@ function testCommentDefaultFields() {
    +    $this->assertEqual($field->getDefaultValueLiteral()[0]['status'], CommentItemInterface::CLOSED);
    

    is it safe to assume that 'status' going to be there?. it's not new change, just wondering...

  5. +++ b/core/modules/field/src/Tests/FieldDefaultValueCallbackTest.php
    @@ -0,0 +1,96 @@
    +    /** @var FieldStorageConfig $field_storage */
    ...
    +    /** @var FieldConfig $field_config */
    

    Minor: thought we follow full class name?

  6. +++ b/core/modules/field/src/Tests/FieldDefaultValueCallbackTest.php
    @@ -0,0 +1,96 @@
    +    $this->assertNoFieldByName('default_value_input[field_test][0][value]', 'Calculated default value', 'The default field form is visible.');
    

    visible? for 'No field'

  7. +++ b/core/modules/field/src/Tests/FormTest.php
    @@ -613,7 +613,7 @@ function testHiddenField() {
    +    $this->field->setDefaultValue(array());
    

    May be we can add resetDefaultValue() ?
    Note: passing just NULL as argument set it as array.

penyaskito’s picture

Thanks for reviewing, Vijay!

1. getDefaultValue already exists and have logic, so we need something for accessing the raw value. See #16.
2. We allow NULL values as a default value, so API consumers could pass that. Internally that must be translated to an empty array.
3. Probably, but makes no harm as this line was already modified. Do you want to change it back?
4. Well, does are tests and we now comment value columns, so I would say it is safe.
5. Good find, changed it.
6. Oh, thanks. Changed the assert text.
7. I don't think it will be a common use case aside of using it on tests.

Vijay, maybe you could take a look at the test error? It is related to config schema and I'm not really sure how we should define the field config schema.

Status: Needs review » Needs work

The last submitted patch, 37: 2529034-field-config-base-default-37.patch, failed testing.

vijaycs85’s picture

Status: Needs work » Needs review
FileSize
27.1 KB
579 bytes

@penyaskito, I guess, the save() just returns status (ref: \Drupal/Core/Config/Entity/ConfigEntityStorage.php:299).

penyaskito’s picture

@vijaycs85: I didn't expect that inconsistency between content entities and config entities.
However, I changed that while doing some cleanup, I expect #39 to fail for the said reason: config schema of the field.

vijaycs85’s picture

I didn't expect that inconsistency between content entities and config entities.

It doesn't look like content entity returns the object either:

Drupal/Core/Entity/ContentEntityStorageBase.php:211

 protected function doSave($id, EntityInterface $entity) {
   ...
    if ($entity->isNew()) {
   ...
      $return = SAVED_NEW;
    }
    else {
      $return = $entity->isDefaultRevision() ? SAVED_UPDATED : FALSE;
    }
   ...
    return $return;
  }

Anyways, let's see what testbot says.

Status: Needs review » Needs work

The last submitted patch, 39: 2529034-39.patch, failed testing.

penyaskito’s picture

@vijaycs85 Sorry for my confusion and the misunderstanding. What I would love you to look is at the error we have in the latest one:

Drupal\Core\Config\Schema\SchemaIncompleteException: Schema errors for field.field.node.article.field_test with the following errors: field.field.node.article.field_test:default_value.value variable type is string but applied schema class is Drupal\Core\Config\Schema\Mapping in Drupal\Core\Config\Testing\ConfigSchemaChecker->onConfigSave() (line 98 of /var/lib/drupaltestbot/sites/default/files/checkout/core/lib/Drupal/Core/Config/Testing/ConfigSchemaChecker.php). 

Thanks!

vijaycs85’s picture

sorry for the delay @penyaskito. Looks like we got two problems. one with default_value and another one with default_value_callback.

1. default_value is an easy one. It just need another array before the ['value' => 'value']. you can see this at Drupal/Core/Field/FieldConfigBase::setDefaultValue()

public function setDefaultValue($value){
....
      // Convert to the multi value format to support fields with a cardinality
      // greater than 1.
      $value = array(
        array($key => $value),
      );


}

2. There is an assumption that the callback should be a string:

core/config/schema/core.data_types.schema.yml
field_config_base:
  type: config_entity
  mapping:
    id:
...
    default_value_callback:
      type: string
      label: 'Default value callback'
...

I can see this has been implied at Drupal/Core/Field/BaseFieldDefinition.php

  public function setDefaultValueCallback($callback) {
    if (isset($callback) && !is_string($callback)) {
      throw new \InvalidArgumentException('Default value callback must be a string, like "function_name" or "ClassName::methodName"');
    }

I tired to make the callback as static and represent the method as string 'ClassName::method', but it's throwing error:

Class 'Drupal\simpletest\WebTestBase' not found	PHP Fatal error	FieldDefaultValueCallbackTest.php	20	Unknown	

Anyway, I am attaching 2 patches, one without callback test and all green and with callback test which is failing.

Status: Needs review » Needs work

The last submitted patch, 44: 2529034-44-with-callback-fail.patch, failed testing.

yched’s picture

getDefaultValue() uses call_user_func() for the default_value_callback, and yes, the setting has to be a string, so either a regular function name or 'namespacedClass::staticMethod' should work.

So I'm not sure why $field_config->setDefaultValueCallback('Drupal\field\Tests\FieldDefaultValueCallbackTest::calculateDefaultValue')->save();in @vijaycs85's 2529034-44-with-callback-fail.patch doesn't pass. I'd suggest trying with an absolute namespace (adding a leading '\' before 'Drupal\field\...') ?

yched’s picture

Note that this will slightly conflict with #2552799: FieldType with no available widget causes Fatal error. Depending on which gets in first, the other one will need a reroll

yched’s picture

Tried my suggestion in #46 :

Drupal\field\Tests\FieldDefaultValueCallbackTest :
-    $field_config->setDefaultValueCallback('Drupal\field\Tests\FieldDefaultValueCallbackTest::calculateDefaultValue')->save();
+    $field_config->setDefaultValueCallback('\Drupal\field\Tests\FieldDefaultValueCallbackTest::calculateDefaultValue')->save();

but that still causes the same fatal as @vijaycs85 describes in #44 : "Class 'Drupal\simpletest\WebTestBase' not found in FieldDefaultValueCallbackTest.php line 20"

If put the static callback in *another* class than the test class itself (say, FieldConfigBase, random pick) things work fine :
- if I manually edit field.field.node.article.body to put :
default_value_callback: 'Drupal\Core\Field\FieldConfigBase::calculateDefaultValue'
then the default value works as expected (node/add/article shows "Calculated default value" prefilled in the Body widget)
- If I modify the test with :
$field_config->setDefaultValueCallback('Drupal\field\Tests\FieldDefaultValueCallbackTest::calculateDefaultValue')->save()
then the test is green.

I tried using a static method in a different class in the same FieldDefaultValueCallbackTest.php file, that fails as well.
So it looks like this is only when referring to a class in the test file itself... Weird.

Fixed that by moving the method to a different class in a separate file.

yched’s picture

Status: Needs work » Needs review
yched’s picture

Status: Needs review » Reviewed & tested by the community

Green, I'll allow myself to set back to RTBC since I only contributed a small test fix in the last patch.

penyaskito’s picture

I'm happy with it too. RTBC+1

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 48: 2529034-field-config-base-default-48.patch, failed testing.

daffie’s picture

Back to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

I think this is the final public property on a config entity in core and as such it is a complete outlier and making it protected before we get to RC seems worth the probably limited disruption it will cause.

Committed dd8f566 and pushed to 8.0.x. Thanks!

  • alexpott committed dd8f566 on 8.0.x
    Issue #2529034 by penyaskito, vijaycs85, yched: Replace direct access to...
yched’s picture

Yay, that's one nice WTF going away. Thanks @penyaskito !

jibran’s picture

penyaskito’s picture

@jibran Sorry for that. https://www.drupal.org/node/2546598 should be helpful.

yched’s picture

Adjusted the wording of the CR a bit :-)

Status: Fixed » Closed (fixed)

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