Project

General

Profile

Actions

Bug #36102

open

In 1:n relationships the parent might get lost

Added by Björn Steinbrink about 12 years ago. Updated over 8 years ago.

Status:
New
Priority:
Must have
Assignee:
-
Category:
Extbase
Target version:
-
Start date:
2012-04-15
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
6.2
PHP Version:
Tags:
Complexity:
Is Regression:
No
Sprint Focus:

Description

Given two classes that have a 1:n relationship modelled by means of a parent field in the child table like this:

class Parent {
  /**
   * @var Tx_Extbase_Persistence_ObjectStorage<Child>
   */
  public $children;
}

class Child {
  /**
   * @var Parent
   */
  public $parent;
}

Updating the relationship can result in the child being stripped of its parent.

$newParent = $parentRepository->...;
$child = $childRepository->...;

$child->parent->children->remove($child),
$newParent->children->add($child);
$child->parent = $newParent;

This code sequence gives a consistent state in memory, but during persistAll(), extbase will set the child's parent to NULL.

This is because extbase tries to be smart and to fix the parent field in case that the user didn't fully update the involved parties, but just added the child to its new parent.

In the above scenario, the objects are saved in this order
  1. $newParent
  2. $child
  3. $oldParent
This leads to the following updates:
  1. In persistObjectStorage(), called for $newParent the parent field gets updated to the new parent uid.
  2. In persistObject() for $child the parent field gets updated to the new parent uid again.
  3. In persistObjectStorage() for $oldParent, the child is stripped of its parent by calling detachObjectFromParentObject()

Thus the child becomes orphaned.

This can be worked around by means of calling persistAll() yourself, because of another bug that causes ObjectStorages not to be marked as clean, thus they get persisted again when extbase calls persistAll() itself, but this time the detaching won't happen, because the clean state of the parent object has been updated.

Actions #1

Updated by Marc Bastian Heinrichs about 12 years ago

Could you also post the tca of Parent children and Child parent, please?
It sounds more like an not supported relation type in T3 core than a bug in extbase.

Actions #2

Updated by Björn Steinbrink about 12 years ago

    'parent' => array(
      'config' => array(
        'type' => 'select',
        'foreign_table' => 'tx_extension_domain_model_parent',
        'foreign_class' => 'Tx_Extension_Domain_Model_Parent',
        'minitems' => 0,
        'maxitems' => 1,
      )
    )

and

    'children' => array(
      'exclude' => 1,
      'config' => array(
        'type' => 'inline',
        'multiple' => 1,
        'foreign_class' => 'Tx_Extension_Domain_Model_Child',
        'foreign_table' => 'tx_extension_domain_model_child',
        'foreign_field' => 'parent',
        'foreign_sortby' => 'sorting',
      )
    )

That the parent field is accessible doesn't even matter, it also fails if you just update the object storages for the children in the old and new parent, if things get persisted in the "wrong" order (the new parent is persisted first).

The use case is that I'm moving a child from one parent to another, that means that I either directly replace the parent, or that I first remove the parent and then set the new one. But extbase might as well end up first setting the new parent and then removing it.

A possible workaround might be to check that the child still has the old parent, and only unset its parent if that is the case, i.e. generate an update statement like

UPDATE tx_extension_domain_model_child SET parent = NULL WHERE parent = 123

But that could lead to other weird bugs in other cases, like this:

$oldParent->remove(child);
$newParent->add(child);
// somewhere else
$child->setParent($oldParent);

If $oldParent gets persisted last, the $child would still end up without a parent.

Key in this example is that the in-memory state is inconsistent again. The two directions of the relationships don't match. IMHO extbase should just require that relationships are kept consistent in-memory (i.e. always update both sides), and then just persist the "parent" field, without trying to be smart. Having to keep it all in sync is the price one pays for using an object model instead of relational data.

Actions #3

Updated by Alexander Schnitzler over 11 years ago

  • Assignee set to Alexander Schnitzler
  • Target version set to Extbase 6.1

I guess we have to check if we have to rewrite the persistAll method and I guess we will not make it before 6.1.

Actions #4

Updated by Alexander Schnitzler about 11 years ago

  • Status changed from New to Needs Feedback
Actions #5

Updated by Alexander Schnitzler about 11 years ago

  • Target version changed from Extbase 6.1 to Extbase 6.2
Actions #6

Updated by Anja Leichsenring almost 11 years ago

  • Target version changed from Extbase 6.2 to Extbase 6.3
Actions #7

Updated by Alexander Schnitzler over 10 years ago

  • Assignee deleted (Alexander Schnitzler)
Actions #8

Updated by Alexander Opitz over 9 years ago

  • Project changed from 534 to TYPO3 Core
  • Category deleted (Extbase: Generic Persistence)
  • Status changed from Needs Feedback to New
  • Target version deleted (Extbase 6.3)
  • TYPO3 Version set to 6.2
  • Is Regression set to No
Actions #9

Updated by Mathias Schreiber over 8 years ago

  • Category set to Extbase
Actions

Also available in: Atom PDF