1
0
mirror of synced 2025-02-09 00:39:25 +03:00

Complete re-write of "Transitive persistence / Cascade Operations" (#6219)

Follow-up of https://github.com/doctrine/doctrine2/pull/6171
This commit is contained in:
Thomas Landauer 2017-01-12 12:49:46 +01:00 committed by mikeSimonson
parent 4e304df495
commit 63e4eea9e2

View File

@ -407,31 +407,14 @@ There are two approaches to handle this problem in your code:
Transitive persistence / Cascade Operations
-------------------------------------------
When working with many associated entities, you sometimes don't want to "expose" all entities to your PHP application.
Therefore Doctrine 2 provides a mechanism for transitive persistence through cascading of certain operations.
Doctrine 2 provides a mechanism for transitive persistence through cascading of certain operations.
Each association to another entity or a collection of
entities can be configured to automatically cascade the following operations to the associated entities:
``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``.
.. note::
Cascade operations are performed in memory. That means collections and related entities
are fetched into memory, even if they are still marked as lazy when
the cascade operation is about to be performed. However this approach allows
entity lifecycle events to be performed for each of these operations.
However, pulling objects graph into memory on cascade can cause considerable performance
overhead, especially when cascading collections are large. Makes sure
to weigh the benefits and downsides of each cascade operation that you define.
To rely on the database level cascade operations for the delete operation instead, you can
configure each join column with the **onDelete** option. See the respective
mapping driver chapters for more information.
The following example is an extension to the User-Comment example
of this chapter. Suppose in our application a user is created
whenever they write their first comment. In this case we would use the
following code:
The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application.
Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new
comment might look like in your controller (without ``cascade: persist``):
.. code-block:: php
@ -444,34 +427,36 @@ following code:
$em->persist($myFirstComment); // required, if `cascade: persist` isn't set
$em->flush();
Even if you *persist* a new User that contains our new Comment this
code requires an explicit call to
``EntityManager#persist($myFirstComment)``. Doctrine 2 does not
cascade the persist operation to all nested entities that are new
as well.
More complicated is the deletion of all user's comments when the user is
removed from the system:
Note that the Comment entity is instantiated right here in the controller.
To avoid this, ``cascade: persist`` allows you to "hide" the Comment entity from the controller,
only accessing it through the User entity:
.. code-block:: php
<?php
$user = $em->find('User', $deleteUserId);
foreach ($user->getAuthoredComments() as $comment) {
$em->remove($comment);
// User entity
class User
{
private $id;
private $comments;
public function __construct()
{
$this->id = User::new();
$this->comments = new ArrayCollection();
}
public function comment(string $text, DateTimeInterface $time) : void
{
$newComment = Comment::create($text, $time);
$newComment->setUser($this);
$this->comments->add($newComment);
}
// ...
}
$em->remove($user);
$em->flush();
Without the loop over all the authored comments Doctrine would use
an UPDATE statement only to set the foreign key to NULL and only
the User would be deleted from the database during the
flush()-Operation.
To have Doctrine handle both cases automatically we can change the
``User#commentsAuthored`` property to cascade both the "persist"
and the "remove" operation.
If you then set up the cascading to the ``User#commentsAuthored`` property...
.. code-block:: php
@ -488,30 +473,51 @@ and the "remove" operation.
//...
}
Since ``cascade: persist`` is configured for the ``User#commentsAuthored``
association, you can now create a user and persist their comments as follows:
...you can now create a user and an associated comment like this:
.. code-block:: php
<?php
$user = new User();
$myFirstComment = new Comment();
$user->addComment($myFirstComment);
$myFirstComment->setUser($user);
$user->comment('Lorem ipsum', new DateTime());
$em->persist($user);
$em->flush();
.. note::
This code does not associate the newly created comment with the user.
To achieve this, you *always* have to call ``$myFirstComment->setUser($user);``
no matter if ``cascade: persist`` is set or not.
The idea of ``cascade: persist`` is not to save you any lines of code in the controller.
If you instantiate the comment object in the controller (i.e. don't set up the user entity as shown above),
even with ``cascade: persist`` you still have to call ``$myFirstComment->setUser($user);``.
Thanks to ``cascade: remove``, you can easily delete a user and all linked comments without having to loop through them:
.. code-block:: php
<?php
$user = $em->find('User', $deleteUserId);
$em->remove($user);
$em->flush();
.. note::
Cascade operations are performed in memory. That means collections and related entities
are fetched into memory (even if they are marked as lazy) when
the cascade operation is about to be performed. However this approach allows
entity lifecycle events to be performed for each of these operations.
However, pulling objects graph into memory on cascade can cause considerable performance
overhead, especially when the cascaded collections are large. Make sure
to weigh the benefits and downsides of each cascade operation that you define.
To rely on the database level cascade operations for the delete operation instead, you can
configure each join column with :doc:`the **onDelete** option <working-with-objects>`.
Even though automatic cascading is convenient, it should be used
with care. Do not blindly apply ``cascade=all`` to all associations as
it will unnecessarily degrade the performance of your application.
For each cascade operation that gets activated Doctrine also
For each cascade operation that gets activated, Doctrine also
applies that operation to the association, be it single or
collection valued.