Bug #13324

It's not possible to update entities without repository

Added by Bastian Waidelich over 10 years ago. Updated about 10 years ago.

Status:
Resolved
Priority:
Must have
Category:
Persistence
Start date:
2011-02-24
Due date:
% Done:

100%

Estimated time:
PHP Version:
Has patch:
Complexity:

Description

Adding/removing entities to/from a collection of the parent object is possible by using the attach()/detach() methods of the SplObjectStorage.
But there is no equivalent for updating objects yet.

More specific: How could one update a comment of the BlogExample without creating a CommentRepository.


Related issues

Related to Conference Management - Task #27629: Adjust Models to enable editingResolvedKarsten Dambekalns2011-06-21

Actions
Related to Conference Management - Task #27566: Streamline Conference editing pluginResolvedBastian Waidelich2011-06-20

Actions
Related to TYPO3.Flow - Feature #28486: Don't clone, use explicit change tracking insteadResolvedKarsten Dambekalns2011-08-17

Actions
#1

Updated by Bastian Waidelich over 10 years ago

in the repository updating is done like
if ($this->persistenceManager->getIdentifierByObject($existingObject) !== NULL) { $this->persistenceManager->replaceObject($existingObject, $newObject); [...]

in the replaceObject() method.
But the repository also keeps track of added and removed objects.

Maybe we need to work with a custom SplObjectStorage like in Extbase?

#2

Updated by Karsten Dambekalns over 10 years ago

No update needed. If you change a persisted object (attached to anything persisted), changes are picked up automagically. No need for an update. If that does not work it's a bug, but we don't need anything new here!

#3

Updated by Bastian Waidelich over 10 years ago

Karsten Dambekalns wrote:

No update needed.

How would you solve this:

public function updateAction(Comment $comment) {
  ...
}

usually you would do $this->commentRepository->update($comment);
So I'd expect something like $post->update($comment);

FYI i solved this for now by introducing following method to the abstract base controller:

/**
 * Replaces an existing object with the same identifier by the given object
 *
 * @param object $modifiedObject The modified object
 */
public function update($modifiedObject) {
    if ($modifiedObject->FLOW3_Persistence_isClone() !== TRUE) {
        throw new \F3\FLOW3\Persistence\Exception\IllegalObjectTypeException('The object given to update() was not a clone of a persistent object.', 1298540772);
    }
    $uuid = $this->persistenceManager->getIdentifierByObject($modifiedObject);
    if ($uuid === NULL) {
        throw new \F3\FLOW3\Persistence\Exception\UnknownObjectException('The "modified object" does not have an existing counterpart in this repository.', 1298540861);
    }
    $existingObject = $this->persistenceManager->getObjectByIdentifier($uuid);

    if ($this->persistenceManager->getIdentifierByObject($existingObject) === NULL) {
        throw new \F3\FLOW3\Persistence\Exception\UnknownObjectException('The "existing object" is unknown to the persistence backend.', 1298540871);
    }
    $this->persistenceManager->replaceObject($existingObject, $modifiedObject);
}

#4

Updated by Jochen Rau over 10 years ago

I would agree with Karsten here: no change needed. Otherwise it would break the aggregate boundaries and thus the idea of atomic domain transactions. There are two options to handle an incoming Comment:

1. find the corresponding Post (through a method of the PostRepository) and update the Comment through a public function of this Post, or
2. treat the Comment as Aggregate Root and implement a CommentRepository (Comment Aggregates within a Post Aggregate doesn't break anything IMO).

#5

Updated by Bastian Waidelich over 10 years ago

Jochen Rau wrote:

I would agree with Karsten here: [...]

[...] update the Comment through a public function of this Post

How? That was the point of this issue ;)
Of course my "solution" is just a hack to get it working for me. We needed s.th. like $this->comments->update($modifiedComment); but SplObjectStorage can't do that obviously..
We probably need a specialized "entity collection" that extends SplObjectStorage and knows how to find the original object by the modified clone..

#6

Updated by Karsten Dambekalns over 10 years ago

In theory update() should take care of that. It recursively checks for entities that are clones and should update/replace them as well.

#7

Updated by Bastian Waidelich over 10 years ago

Karsten Dambekalns wrote:

In theory update() should take care of that. [...]

..but there is no update() in \SplObjectStorage ;)

#8

Updated by Karsten Dambekalns over 10 years ago

The correct solution when taking the domain model into account would be to fetch the Post the comment is attached to and map the new values onto the Comment referenced by this Post. In other words: the problem is that the comment is completely detached from it's surroundings in this scenario...

#9

Updated by Bastian Waidelich over 10 years ago

  • Status changed from New to Needs Feedback

Karsten Dambekalns wrote:

The correct solution when taking the domain model into account would be to fetch the Post
the comment is attached to and map the new values onto the Comment referenced by this Post.
In other words: the problem is that the comment is completely detached from it's surroundings
in this scenario...

So, it's not solvable at all??

I need this to work for the conference site atm and with the FLOW3 refactoring my work around from above does not work anymore..
The case is even simpler, cause it's a 1:1 relation:
I just want to update the location of the current conference, so I tried

public function updateAction(\F3\Conference\Domain\Model\Conference\Location $location) {
  $this->conference->setLocation($location);
}

But I end up with a new Location in the database and $this->conference->getLocation() returns NULL.

help

#10

Updated by Bastian Waidelich over 10 years ago

After digging a bit deeper into this issue, here my findings:
The problem seems to be that \Doctrine\Common\Persistence\ObjectManager::contains() returns FALSE for a cloned object!?
\Doctrine\ORM\UnitOfWork::getEntityIdentifier() returns a UUID instead of the expected identitye (array('title' => 'Location Title')).

For now, i fixed it in the Conference package by writing:

$this->persistenceManager->merge($modifiedObject);

But I think, that the framework should handle $this->conference->setLocation($modifiedLocation) automatically..

#11

Updated by Karsten Dambekalns over 10 years ago

  • Assignee set to Karsten Dambekalns

Ok, for the conference when editing intermission types it's somewhat clear: editing the duration works, editing the title doesn't. That's because the title is part of the identity, and changing this is non-trivial at best. Because the system cannot know that the incoming object with the identity "Foo" should replace what still is "Bar" in persistent storage. Similar to changing your name: new passport, new contracts, new anything, but nothing trivial.

#12

Updated by Bastian Waidelich over 10 years ago

Karsten Dambekalns wrote:

[...] the title is part of the identity, and changing this is non-trivial at best.

I see, but do you have a idea/suggestion on how to solve this? Or do we have to revert the models to use a uuid column?

#13

Updated by Sebastian Kurfuerst about 10 years ago

  • Target version set to 1.0 beta 1
#14

Updated by Karsten Dambekalns about 10 years ago

With the new persistence behavior this should be solved, check!

#15

Updated by Karsten Dambekalns about 10 years ago

  • Status changed from Needs Feedback to Accepted
  • Target version changed from 1.0 beta 1 to 1.0 beta 2

I am rather convinced this works as expected now, but I'll double check next week.

#16

Updated by Karsten Dambekalns about 10 years ago

  • Status changed from Accepted to Resolved
  • % Done changed from 0 to 100

The way used in https://review.typo3.org/#change,4692 is correct and then updating works fine. This can probably be made easier, but that is handled in separate issues.

Also available in: Atom PDF