Updated: Comment #N

Problem/Motivation

Entity normalization is really useful. We primarily use this in HAL and REST, but it is completely decoupled.
However, it lives in the optional serialization.module, so is cannot be relied upon.

Proposed resolution

Instead of providing services in the serialization.module to normalize entities and their fields, move that logic onto the object itself.

Remaining tasks

Decide if EntityInterface::normalize() and TypedDataInterface::normalize() should have a more generic/specific/different name

User interface changes

N/A

API changes

EntityInterface and TypedDataInterface have a new normalize() method that turns an object into a set of arrays and scalars.
ConfigEntityInterface::getExportProperties() is now protected, call normalize() instead.
The following services have been removed:

  • serializer.normalizer.config_entity
  • serializer.normalizer.complex_data
  • serializer.normalizer.list

These were tagged services, and should not have been used externally anyway.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Status: Active » Needs review
FileSize
19.25 KB

I'm sad to see those unit tests go, maybe they should be reworked to test the logic of the five new normalize() implementations?

dawehner’s picture

Issue tags: +Needs change record

Because I can :p

  1. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
    @@ -269,4 +269,11 @@ public function url($rel = 'edit-form', $options = array()) {
    +   * {@inheritdoc}
    +   */
    +  public function normalize($format = NULL, array $context = array()) {
    +    return $this->getExportProperties();
    +  }
    +
     }
    

    General question: does it make sense to keep getExportProperties as public method?

  2. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
    --- a/core/lib/Drupal/Core/Entity/Entity.php
    +++ b/core/lib/Drupal/Core/Entity/Entity.php
    
    +++ b/core/lib/Drupal/Core/Entity/Entity.php
    @@ -442,4 +442,15 @@ protected function urlGenerator() {
    +    $attributes = array();
    +    foreach ($this as $name => $field) {
    +      $attributes[$name] = $field->normalize($format, $context);
    +    }
    
    +++ /dev/null
    @@ -1,53 +0,0 @@
    -class ConfigEntityNormalizerTest extends UnitTestCase {
    

    It feels wrong to let the entity class assume things about typed data. Afaik this code would be better for ContentEntityBase but even more important iterating over $this is problematic. We might at least should check instanceof here. Also: We can't just drop test coverage like that but replace it with tests for all the new methods.

damiankloip’s picture

That removes all the unit tests I did :(

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett
Issue summary: View changes
Issue tags: +Needs tests

Yes, I need to readd unit tests to replace that coverage, but I removed the classes they were testing...

And yeah, Entity::normalize() needs to be on ContenEntityBase, since that is what implements IteratorAggregate, I just didn't feel like diving in super deep until I was sure we wanted to even do this.

tim.plunkett’s picture

Issue summary: View changes
FileSize
38.12 KB

Changed getExportProperties() to protected.
Moved Entity::normalize() to ContentEntityBase, but left an empty normalize() implementation there for random test entities.
Added back ListNormalizerTest. The old ConfigEntityNormalizerTests is now covered by ConfigEntityBaseUnitTest.

fago’s picture

Decide if EntityInterface::normalize() and TypedDataInterface::normalize() should have a more generic/specific/different name

Could it be just toArray() everywhere? Do we need the format and context options? I guess this needs to be extendable anyway such that I can plug in normalizers for new formats in a separate class still?

  1. +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
    @@ -299,4 +299,16 @@ public function getEntityType();
    +   *   The format the normalization result will be encoded as
    

    Missing trailing point. What kind of formats are supported here. I guess this should point me to related documentation about the normalizer as this method is out of normalizer-context now.

  2. +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
    @@ -299,4 +299,16 @@ public function getEntityType();
    +   *   Context options for the normalizer
    

    What can be in there? Again probably some pointer (maybe for the whole method) would be good. Misses trailing point also.

tim.plunkett’s picture

@msonnabaum suggested toArray() at one point, I'm fine with that.

I wasn't sure about keeping or removing the $format and $context yet, I'm going to wait for @linclark to chime in here. I copy/pasted those docs, but we can improve them if we decide to keep those params.

yched’s picture

toArray() is nice, but "cast to an array" is also sort of what TypedData::getValue() does already (and I was kind of hoping it could get renamed toArray() at some point)
Related question: are the two (the current getValue() and the proposed normalize() / toArray()) really different ?

damiankloip’s picture

Regarding the format and context params; If we do go with this idea (I am semi-convinced at this stage :)) an entity should have no business with either of those.

I think we should also consider not using normalize() too. toArray() works better I think. Normalize is a term adopted by Serializer, to reflect how its architecture works (IMO).

yched, could they be consolidated to TypedData objects? In answer to your question, I don't think they really are different :)

tim.plunkett’s picture

FileSize
36.84 KB
12.94 KB

If the class implements ComplexDataInterface, normalize() == getPropertyValues(), otherwise it's the same as getValue(). I've updated to reflect that.

I've also renamed it to toArray() now, and removed $format and $context.

Berdir’s picture

See also #2002138: Use an adapter for supporting typed data on ContentEntities, which is dropping Entity::getValue() and renaming getPropertyValues() to getFieldValues. Not touching FieldItem yet, but yes, getValue() -> toArray() is exactly what I wanted to do there.

TypedDataInterface::toArray() is pretty crazy, because complex data is an extension of it, it shouldn't implement that.

An array representation of Non-ComplexData does not make much sense to, because most of those are scalar/primitive values. $string->toArray() => array('the_string') ? :)

Status: Needs review » Needs work

The last submitted patch, 10: entity-normalize-2216569-10.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
37.55 KB
8.02 KB

I have to think more on #11, but I might as well get this back to green.

Status: Needs review » Needs work

The last submitted patch, 13: entity-normalize-2216569-13.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
36.74 KB
fago’s picture

Related question: are the two (the current getValue() and the proposed normalize() / toArray()) really different ?

hm, for fields yes, but - speaking of the typed data API, no - not necessarily. The internal value is up to the data type and may be anything, including classed objects, etc.. It's just whatever the class wraps.

Berdir’s picture

Yes, on TypedDataInterface, getValue() can be anything and there might not be a useful array representation of that.

ComplexDataInterface on the other hand, should always have a useful to Array() representation as it contains a set of properties and their values can be returned as array. But their values might not be arrays.

In #2002138: Use an adapter for supporting typed data on ContentEntities, we discussed to rename getFieldValues (which I renamed from getPropertyValues()) to toArray(), so that content entities would only have that method and not an additional wrapper as currently added here.

tim.plunkett’s picture

tim.plunkett’s picture

Assigned: tim.plunkett » linclark
Issue tags: -KV Entity Storage

I'm not sure that we'll need this anymore once #2223361: Rename ComplexDataInterface::getPropertyValues() to toArray(), remove setPropertyValues() lands. $entity->toArray() is all I ever wanted from this anyway. Assigning to @linclark for her feedback, but I'm okay with closing this.

mgifford’s picture

Is this still needed? Shouldn't be postponed in anycase.

tim.plunkett’s picture

Assigned: linclark » Unassigned
Status: Active » Closed (won't fix)

Unnecessary