Read this first: Typed Data API in Drupal 8
Introduced here: #1696640: Implement API to unify entity properties and fields

Unlike many other languages, PHP is a very loosely typed language. [..] There needs to be a consistent way of typing this data, or describing the data.

There is a known pattern for this, and it is called Value object.
The crucial aspect about value objects is that they are immutable.

Value objects should be immutable:[3] this is required for the implicit contract that two value objects created equal, should remain equal.

There was a nice session about this on DrupalCamp Ghent. (Dunno if there will ever be any audio)

I also found a library which already does this:
https://github.com/nicolopignatelli/valueobjects
I suspect there are others. This is just the first that I found.

------------

Drupal's TypedData API attempts to solve the same problem, but someone decided to make the data objects mutable.
So now we have a home-made solution that is inferior to what the rest of the (modern) world does, or will do in the future.

I also have the suspicion that the objects being mutable makes the entire system more complex. They need to keep a reference to the parent object, to notify it about changes.

I don't have a specific suggestion what to do here. Maybe we will just say "too late" and that's it.
Or maybe we can introduce value objects, but only use them in some cases where it is not too expensive to change.

Comments

yched’s picture

Dude, way too late to reconsider in D8 at this point :-)

donquixote’s picture

Well, for all the entity stuff, probably yes, way too late.
But for new features it could be something to consider, or not?

Too bad I did not follow the introduction of TypedData, and anyway I only learned about value objects very recently. Maybe I would have learned it earlier if I had been involved. If, if, if.

But I imagine anyone looking at typed data who is aware of value objects will say "duh" and "why?".

larowlan’s picture

I think they are supposed to be mutable? You can update values too.

donquixote’s picture

Yes. Drupal's TypedData values (like String, Duration, Uri) are meant to be mutable. This makes them different from the ValueObject pattern, where these objects are supposed to be immutable.

Being immutable has a number of advantages, and this is why value objects are such a nice thing.
You can pass them around without being afraid that they might be manipulated.

If Drupal's TypedData were implemented as immutable value objects, we would not need stuff like notifying the parent of a change (in class TypedData):

  /**
   * {@inheritdoc}
   */
  public function setValue($value, $notify = TRUE) {
    $this->value = $value;
    // Notify the parent of any changes.
    if ($notify && isset($this->parent)) {
      $this->parent->onChange($this->name);
    }
  }

With value objects, instead of updating a value, you replace the object with a new value object with the modified value.

// TypedData approach.
$number = new Integer(5);
// Update the value.
$number->setValue(6);
// Increment by 2.
$number->increment(2);

// ValueObject approach.
$number = new Integer(5);
// Update the value.
$number = new Integer(6);
// Increment by 2.
$number = $number->plus(2);

Of course there might be valid reasons why this approach would be a bad fit for the entity system, or other places where TypedData is currently used.

But from the purpose description of the TypedData API, it seems that value objects would have been the better solution.

dawehner’s picture

Well, while its nice that you can be sure that objects are never changed ... what do you want to do with cases in what you actually want the values to be changed?
It seems pretty straightforward for simple data values, but how would you do it for entity references? All kind of other objects could held a reference to the initial value object ... and those references would somehow needs to be updated?

fago’s picture

Version: 8.0.x-dev » 9.x-dev
Component: base system » typed data system

Yeah, typed data objects are mutable. Also, they do not cover simple values only, but all kind of data including entities - which by design aren't value objects.

// ValueObject approach.
$number = new Integer(5);

The problem I see with that is that the classes and thus implementations used cannot be controlled. E.g., for an integer field the system controls which class $items[0] has to be, so that other parts of the system can rely on it. And yes, that class can be swapped out also, thus shouldn't be hard-coded.

So our objects aren't immulatble, but instead they are context-aware what gives you nice API additions like $item->getEntity().

Not sure what the scope of the issue is here, but it's way too late to change this for d8 for sure.

effulgentsia’s picture

So our objects aren't immutable, but instead they are context-aware

Right. The key feature of Typed Data is not just that there's a class wrapper around the value, but that we can get both metadata (e.g., label, settings) and context (e.g., name, parent) about the underlying data. That allows for powerful configuration UIs in Panels, Rules, etc. To make the objects immutable, everywhere that code needs to construct a new value, it would need a way to instantiate a new object (allowing for a swappable class per #6) with the same metadata and context. Potentially doable, so if you want to experiment with that in a sandbox, go for it (and please share the link to it), but I'd suggest waiting until Panels and Rules are ported to D8, so you can see some of the use cases (outside of the entity/field systems) around Typed Data, which might inform how to go about refactoring it.

So now we have a home-made solution that is inferior to what the rest of the (modern) world does, or will do in the future.

There just aren't many (any?) PHP frameworks that are designed to power the kind of configuration UIs that makes Drupal so special, so I don't think it's fair to say that a home-made solution for Drupal is necessarily inferior to other libraries. It might be or might not be, depending on what other library we compare it to. https://github.com/nicolopignatelli/valueobjects has only been around for 1 year (so is less mature than Typed Data) and is nowhere near sufficient for Drupal's needs. But, if either that one matures or some other very robust library comes along and gets widely adopted by the PHP community, then it would make sense for us to see how we can best incorporate it.

donquixote’s picture

So.. the things I can say here are rather limited. I think I understand (and like) the concept of value objects. But I have not spent much time studying the use cases of our TypedData API. I still hope that we can all learn something here, maybe improve the docs, so it is more than just noise.

I remember how annoying it is in D7 that basically anything can manipulate an entity object, and even worse, these modifications are cached.
Passing immutable objects around to components that are not supposed to leave it unmodified, seems like a good idea.

Maybe the doc page on https://www.drupal.org/node/1794140 can clarify why being context-aware is a good thing.

catch’s picture

Status: Active » Closed (works as designed)

Going to close this as works as designed. However a new issue to expand the documentation at https://www.drupal.org/node/1794140 would be good.

Version: 9.x-dev » 9.0.x-dev

The 9.0.x branch will open for development soon, and the placeholder 9.x branch should no longer be used. Only issues that require a new major version should be filed against 9.0.x (for example, removing deprecated code or updating dependency major versions). New developments and disruptive changes that are allowed in a minor version should be filed against 8.9.x, and significant new features will be moved to 9.1.x at committer discretion. For more information see the Allowed changes during the Drupal 8 and 9 release cycles and the Drupal 9.0.0 release plan.