Posted by fago on September 7, 2012 at 1:39pm
25 followers
| Project: | Drupal core |
| Version: | 8.x-dev |
| Component: | entity system |
| Category: | task |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs review |
| Issue tags: | Entity Field API, sprint |
Issue Summary
The API should include a way of providing default values. E.g. this could be done by a method getDefaultValue() on ItemList and Item objects. Then, on entity creation values could be initialized with the defaults.
Comments
#1
Moving to core queue.
#2
#1803064: Horizontal extensibility of Fields: introduce the concept of behavior plugins gives an example use-case of "prepopulating" values.
#3
Quoting myself from http://drupal.org/node/1869250#comment-6893050:
#4
Question is whether the default value method should go into entity-interfaces or typed data interfaces.
Doing at typed-data level would make it possible to easily have default values for entity.field_text.0.format or so as well, so I guess it would be valuable to do so.
#5
Closely related: #1253820-34: It's impossible to submit no value for a field that has a default value.
#6
I think we have a really huge Drupalism/DrupalWTF problem if an essential OO concept like default class property values ceases to work. Replacing all object properties with class instances is a very weird concept already, but it is borderline acceptable as long as PHP developers only need minimal adjustments to cater for it.
Destroying the native concept of default values of class properties, however, is really too much IMHO. It's the tipping point at which PHP developers will reasonably blame Drupal for "Re-inventing OO in user-land PHP code" and a PHP class is no longer a PHP class. That's definitely detrimental for Drupal's health and future. A lot of the TypedData abstraction essentially exists to work around PHP's lack of primitive data type support, and PHP developers will already blame us for inventing and doing that; one doesn't have to look very far to find answers from PHP developers who strongly recommend to NOT do what we've done, for plethora of very good reasons; performance being a major one, and as you know, we're experiencing the consequences in various benchmarks already. Also, following the entire logic, one can only guess that one of the next steps is to invent polymorphism and mixins in this user-space OO layer (because, really, what prevents us from doing it? Nothing!) - ultimately rewriting PHP's native C code for OO support in PHP code.
I believe that this should ring all available alarm bells, and instead of abstracting even more, we should try hard to scale things back where possible.
Why can't we do something like this?
#7
The last submitted patch, drupal8.entity-default-values.6.patch, failed testing.
#8
I don't think we are re-inventing it - we are leveraging it and we are OO-ifying stuff that's not OO-ified in the language. But yes, I fully agree that we should avoid making typed data an abstraction layer above PHP-classes, but embrace them. See related #1867880: Bring data type plugins closer to their PHP classes.
However, PHP lacks a way to define an object as default value for a class property. Thus, we have to care about that ourselves somehow.
The patch suggested in #6 has the following problems:
- It suggests a simple value for a property, e.g. 'string', where it will be an object holding the string. Thus, the specified default there is wrong as an entity field will never be simply that value. But moreover it implies to devs that the object property / entity field would be just a plain string, what's not TRUE and so a DX WTF.
- We need to support dynamic values also, e.g. to support field API default values or simple cases as date fields defaulting to NOW. That could be solved via hook_entity_create() as discussed at #1253820: It's impossible to submit no value for a field that has a default value, but having it in a class method would be more straight-forward imho.
#9
Coming from #1839066: Implement the new entity field API for the image field type where we had a test fail related to default handling. Currently, we have some default values in the hook_schema() implementations, but that does not make much sense to me. We should have default values on entity-api level, such that entity_create() adds them in. If there is no default value, NULL must be a valid value and thus stored as that.
So maybe as a follow-up to this one, we should make sure our hook_schema() implementations do not provide default values and correctly allow NULL values if a field does not have a NOT NULL validation constraint.
#10
Default values can be at different levels, e.g. we can have
$entity->langcode have the default of array(0 => array('value' => und)) or we can have $entity->field_text->format default to the default formatter *if* a new item is created.
So it makes sense to specify the default value for the fields of the entity as well as for the values of an item, such that those are created when the whole structure is created. But it does not make sense to have default values for an individual item or a list. If you create an individual item, e.g. $string you either provide a value for it or not - I do not see it useful to have a $string to default to anything ;-). It only makes sense if a property is created as part of a complex data structure and no value for a property is provided.
#11
ok, here is an implementation that adds in default value support via typed data, i.e. you can define it per-field level as well as for stuff like $entity->field_text->format.
Attached implements default values for language fields, uuid fields and for the format of text field items. It also adds a simple way to specify static default values for fields via the field settings, such that stuff like having a comment name default of '' does not force you to create a new class. While the patch implements both, I've not verified the default-format functionality works.
What's missing from the patch is tests. I'd suggest adding the following unit test cases:
- Create a new entity and verify it has the langcode default set.
- Create an entity with an text field and add new field items. Make sure it adds in the default format as default value and that a newly created item with an empty text qualifies as isEmpty().
Setting to "Needs tests" - so someone could pick it up.
#12
added tests for default language & uuids.
#13
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldDefaultValueTest.phpundefined@@ -0,0 +1,73 @@
+ // Initiate the generator. This will lazy-load uuid.inc.
+ $this->uuid = new Uuid();
Not sure I understand that comment, there's no such file?
#14
re #13, yeah I just copied this comment over together with the uuid-related code borrowed from UuidUnitTest
#15
Can you try without those two lines? Shouldn't be necessary.
#16
#12: d8_default_value_12.patch queued for re-testing.
#17
The last submitted patch, d8_default_value_12.patch, failed testing.
#18
Re-rolled patch and fix the comment at both places.
#19
Re-rolled again against latest 8.x, no other changes.
#20
The last submitted patch, d8_default_value.patch, failed testing.
#21
ok, meanwhile NodeNG got in and quite some tests make use of nodes - also unit tests. Those tests would depend on filter_default_format() to work with that as default value, but that a) requires role permissions schema and b) have a valid format defined.
Both isn't the case for unit-test-cases, so instead of adding that in everywhere it might be better to properly inject the filter system as dependency and mock it in the tests. But that is really out of scope for this issue, thus attached patch removes the addition of the filter_default_format() for now.
Also fixed NodeNG to use uuid_field. Let's see what the bot says now.
#22
The last submitted patch, d8_default_value.patch, failed testing.
#23
Suppose uuid hinks should be filed in other issue
#24
Updated the patch since the latest changes (notify+term-ng). I slightly improved the approach such that field item classes can define their own default value, i.e. moved language defaults to the language item and analogously for uuid-fields.
The patch triggered some problems of #1869562: Avoid instantiating EntityNG field value objects by default, so I debugged this and fixed those. Looks like there are quite some fails left with node-ng and term-ng related issues - let's track them down too.
#25
Bot, have fun.
#26
The last submitted patch, d8_default.patch, failed testing.
#27
Coming from #1979260: Automatically populate uid on POST requests, I also want this to work:
<?php$values = array('title' => 'test node', 'type' => 'article');
$node = entity_create('node', $values);
$node->save();
?>
That throws Exceptions currently if comment module is enabled, because the node author is not set.
#28
Raising to major because of #27. It's a major (if not critical) bug for non-form-based creation of nodes to throw errors.
#29
ok, took another stab on fixing the test fails. Most interestingly:
* The patch changed entity create to ignore values for non-defined fields. That should have caused most test fails.
* Due to the default NULL values for all field items we trigger the problem of #1957888: Exception when a field is empty on programmatic creation in a test, thus incorporated a fix here. See the new cleanValue() method.
Also I Improved the test case a bit. See attached interdiff. Let's how this patch goes.
#30
#1957888: Exception when a field is empty on programmatic creation is already major
#31
The last submitted patch, d8_default.patch, failed testing.
#32
See the interdiff's in #1818570-38: Convert users to the new Entity Field API and comment #40 for a fix for the failing forum tests.
#33
Funny, for #1969728: Implement Field API "field types" as TypedData Plugins I needed to add a Field::filterEmpty() method that's basically the cleanValue() method added here.
Mine was a bit more straightforward though:
public function filterEmpty() {
if (isset($this->list)) {
$this->list = array_values(array_filter($this->list, function ($item) {
return !$item->isEmpty();
}));
}
}
(+ may I bikeshed that maybe 'filter empty" is more explicit than "clean" as to what the function does ? filterEmptyValues() maybe ?)
#34
Also, most probably not for this issue, but I'm a bit perplexed at the "default NULL values for all field items" behavior - i.e, an "empty field" is a Field *with* a FieldItem, that happens to have NULL properties - why is that exactly ?
#35
BTW, both the cleanValue() method in the current patch and the alternative code proposed in #33 will leave an "empty" Field with an empty $this->list - which breaks the behavior mentioned above (an "empty field" is a Field *with* a FieldItem, that happens to have NULL properties).
"Sometimes there is an empty FieldItem, sometimes there isn't" sounds wrong ?
#36
We've an empty field item object by default such that this gets cloned during prototyping the field object, thus speeds up getting further field objects.
Besides that, does this patch extend the behaviour to initialize a single field item as an array with NULL properties on entity_create(). I think having one field item by default is reasonable, as that way you can easily add a field item class with a given default - see the language reference field in the patch for example. Still, you can define defaults having multiple field items using the 'default_value' setting or by overriding applyDefaultValue() of the Field class.
Good suggestion! I re-named the method and added in your more stream-lined implementation :-) Also fixed the remaining test fails and updated the patch.
#37
> "Sometimes there is an empty FieldItem, sometimes there isn't" sounds wrong ?
Opened #1988492: Avoid having empty field items by default and answered there, hopefully we can improve that.
Still, this patch would add in empty items for newly created entities returned by entity_create(), as that is the default value. But if we manage to do #1988492: Avoid having empty field items by default they would not re-appear once you save/load the entity.
#38
The last submitted patch, d8_default.patch, failed testing.
#39
Added in berdir's interdiff from #1818570-40: Convert users to the new Entity Field API also ;-)
#40
#39: d8_default.patch queued for re-testing.
#41
setting tag for focus
#42
+++ b/core/lib/Drupal/Core/TypedData/Type/Map.php
@@ -230,4 +230,14 @@ public function onChange($property_name) {
+ /**
+ * {@inheritdoc}
+ */
+ public function applyDefaultValue($notify = TRUE) {
+ // Apply the default value of all properties.
+ foreach ($this->getProperties() as $property) {
+ $property->applyDefaultValue(FALSE);
+ }
+ }
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -1134,4 +1134,11 @@ public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
+ /**
+ * Implements \Drupal\Core\TypedData\TypedDataInterface::applyDefaultValue().
+ */
+ public function applyDefaultValue($notify = TRUE) {
+ $this->storage->applyDefaultValue($notify);
+ }
Isn't here a
return $this;missing? According to the function documentation it should allow chaining.+++ b/core/modules/forum/forum.module@@ -334,10 +334,13 @@ function forum_node_presave(EntityInterface $node) {
+ // Only do a shadow copy check if this is not a new node.
+ if (!$node->isNew()) {
Shouldn't that be "Only do a shadow copy if this is not a new node."?
The word "check" seems to be a leftover from an earlier sentence.
That's all I've found so far :)
#43
Thanks, addressed that and re-rolled.
#44
As discussed in #1957888: Exception when a field is empty on programmatic creation, the check there is actually correct. And this issue will need a re-roll once the other one is in :)
#45
ok, it got committed so re-rolled. I've kept the hunk that improved the comment (as noted by das-peter above) of the forum.module fix though.
#46
It's not an improvement :)
The previous comment is correct as explained in the other issue, in case the node is not new, we are *checking* if we have to create a shadow copy.
#47
true - I should have had a closer look - it's not referring to the if clause. Removed that hunk and re-rolled the hunk.
#48
+++ b/core/lib/Drupal/Core/Entity/EntityNG.phpundefined@@ -486,8 +482,7 @@ public function createDuplicate() {
+ $duplicate->{$entity_info['entity_keys']['uuid']}->applyDefaultValue();
Talking about applying a default value for UUID is a bit weird as we're actually generating a new one here but can't do much about this.
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.phpundefined@@ -21,6 +21,9 @@
+ * Supported settings (below the definition's 'settings' key) are:
+ * - default_value: (optional) If set, the default value to apply to the field.
This would allow to both set the default for the first field item and also multiple values. Not very intuitive, should we document this better here?
+++ b/core/modules/text/lib/Drupal/text/Type/TextItem.phpundefined@@ -54,6 +54,16 @@ public function getPropertyDefinitions() {
+ // Default to a simple check_plain().
+ // @todo: Add in the filter default format here.
+ $this->setValue(array('format' => NULL), $notify);
In case of configurable text fields, it depends on the configuration of the field if it should be NULL (check_plain(), or filter_default_format(). Should we respect that here?
We can also do it in a follow-up, but then @todo should be slightly reworded I think.
Patch nicely shows the code duplication of entity reference/term/file/image fields. There's an RTBC patch that converts term to extend from entity and use target_id and a green needs review patch for files/images. Would be nice if that could go in first I think, I hope that happens soon.
#49
About the default value setting, I think we should also try to find a better place to document the settings a certain class supports. Possibly simply define them with default value in the plugin definition? As we move to annotations, that would be almost the same place but then it's also obvious to search there for documentation.