Skip to content

Commit

Permalink
Leverage LazyGhostTrait when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Oct 30, 2022
1 parent 1edfa91 commit eb6bc24
Show file tree
Hide file tree
Showing 36 changed files with 427 additions and 371 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:
- "default"
extension:
- "pdo_sqlite"
proxy:
- "common"
include:
- php-version: "8.0"
dbal-version: "2.13"
Expand All @@ -53,6 +55,10 @@ jobs:
- php-version: "8.1"
dbal-version: "3@dev"
extension: "sqlite3"
- php-version: "8.1"
dbal-version: "3@dev"
proxy: "lazy-ghost"
extension: "pdo_sqlite"

steps:
- name: "Checkout"
Expand All @@ -72,6 +78,10 @@ jobs:
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
if: "${{ matrix.dbal-version != 'default' }}"

- name: "Require specific lazy-proxy implementation"
run: "composer require symfony/var-exporter:^6.2@dev doctrine/persistence:^3.1@dev --no-update"
if: "${{ matrix.proxy == 'lazy-ghost' }}"

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ jobs:
if: "${{ matrix.dbal-version != 'default' }}"

- name: "Require specific persistence version"
run: "composer require doctrine/persistence ^${{ matrix.persistence-version }} --no-update"
if: "${{ matrix.persistence-version != 'default' }}"
run: "composer require symfony/var-exporter ^6.2@dev doctrine/persistence ^$([ ${{ matrix.persistence-version }} = default ] && echo '3.1@dev' || echo ${{ matrix.persistence-version }}) --no-update"

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
Expand Down Expand Up @@ -100,6 +99,9 @@ jobs:
coverage: "none"
php-version: "${{ matrix.php-version }}"

- name: "Require specific persistence version"
run: "composer require symfony/var-exporter ^6.2@dev doctrine/persistence ^3.1@dev --no-update"

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
Expand Down

This file was deleted.

78 changes: 0 additions & 78 deletions docs/en/cookbook/implementing-wakeup-or-clone.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ Cookbook
* **Implementation**:
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` |
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` |
:doc:`Using Wakeup Or Clone <cookbook/implementing-wakeup-or-clone>` |
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
:doc:`Validation <cookbook/validation-of-entities>` |
:doc:`Entities in the Session <cookbook/entities-in-session>` |
Expand Down
5 changes: 3 additions & 2 deletions docs/en/reference/advanced-configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,9 @@ identifier. You could simply do this:
$cart->addItem($item);
Here, we added an Item to a Cart without loading the Item from the
database. If you invoke any method on the Item instance, it would
fully initialize its state transparently from the database. Here
database. If you access any state that isn't yet available in the
Item instance, the proxying mechanism would fully initialize the
object's state transparently from the database. Here
$item is actually an instance of the proxy class that was generated
for the Item class but your code does not need to care. In fact it
**should not care**. Proxy objects should be transparent to your
Expand Down
41 changes: 9 additions & 32 deletions docs/en/reference/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,32 +74,13 @@ Entities
An entity is a lightweight, persistent domain object. An entity can
be any regular PHP class observing the following restrictions:


- An entity class must not be final or contain final methods.
- All persistent properties/field of any entity class should
always be private or protected, otherwise lazy-loading might not
work as expected. In case you serialize entities (for example Session)
properties should be protected (See Serialize section below).
- An entity class must not implement ``__clone`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
- An entity class must not implement ``__wakeup`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
You can also consider implementing
`Serializable <https://php.net/manual/en/class.serializable.php>`_,
but be aware that it is deprecated since PHP 8.1. We do not recommend its usage.
- PHP 7.4 introduces :doc:`the new magic method <https://php.net/manual/en/language.oop5.magic.php#object.unserialize>`
``__unserialize``, which changes the execution priority between
``__wakeup`` and itself when used. This can cause unexpected behaviour in
an Entity.
- An entity class must not be final nor read-only but
it may contain final methods or read-only properties.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
property with the same name. That is, if B inherits from A then B
must not have a mapped field with the same name as an already
mapped field that is inherited from A.
- An entity cannot make use of func_get_args() to implement variable parameters.
Generated proxies do not support this for performance reasons and your code might
actually fail to work when violating this restriction.
- Entity cannot access private/protected properties/methods of another entity of the same class or :doc:`do so safely <../cookbook/accessing-private-properties-of-the-same-class-from-different-instance>`.

Entities support inheritance, polymorphic associations, and
polymorphic queries. Both abstract and concrete classes can be
Expand Down Expand Up @@ -159,17 +140,13 @@ Serializing entities

Serializing entities can be problematic and is not really
recommended, at least not as long as an entity instance still holds
references to proxy objects or is still managed by an
EntityManager. If you intend to serialize (and unserialize) entity
instances that still hold references to proxy objects you may run
into problems with private properties because of technical
limitations. Proxy objects implement ``__sleep`` and it is not
possible for ``__sleep`` to return names of private properties in
parent classes. On the other hand it is not a solution for proxy
objects to implement ``Serializable`` because Serializable does not
work well with any potential cyclic object references (at least we
did not find a way yet, if you did, please contact us). The
``Serializable`` interface is also deprecated beginning with PHP 8.1.
references to proxy objects or is still managed by an EntityManager.
By default, serializing proxy objects does not initialize them. On
unserialization, resulting objects are detached from the entity
manager and cannot be initialiazed anymore. You can implement the
``__serialize()`` method if you want to change that behavior, but
then you need to ensure that you won't generate large serialized
object graphs and take care of circular associations.

The EntityManager
~~~~~~~~~~~~~~~~~
Expand Down
6 changes: 3 additions & 3 deletions docs/en/reference/dql-doctrine-query-language.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ not need to lazy load the association with another query.

Doctrine allows you to walk all the associations between
all the objects in your domain model. Objects that were not already
loaded from the database are replaced with lazy load proxy
instances. Non-loaded Collections are also replaced by lazy-load
loaded from the database are replaced with lazy-loading proxy
instances. Non-loaded Collections are also replaced by lazy-loading
instances that fetch all the contained objects upon first access.
However relying on the lazy-load mechanism leads to many small
However relying on the lazy-loading mechanism leads to many small
queries executed against the database, which can significantly
affect the performance of your application. **Fetch Joins** are the
solution to hydrate most or all of the entities that you need in a
Expand Down
24 changes: 0 additions & 24 deletions docs/en/reference/limitations-and-known-issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,27 +177,3 @@ MySQL with MyISAM tables
Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one
of the tables involved uses the storage engine MyISAM. You must use InnoDB or
other storage engines that support transactions if you need integrity.

Entities, Proxies and Reflection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Using methods for Reflection on entities can be prone to error, when the entity
is actually a proxy the following methods will not work correctly:

- ``new ReflectionClass``
- ``new ReflectionObject``
- ``get_class()``
- ``get_parent_class()``

This is why ``Doctrine\Common\Util\ClassUtils`` class exists that has similar
methods, which resolve the proxy problem beforehand.

.. code-block:: php
<?php
use Doctrine\Common\Util\ClassUtils;
$bookProxy = $entityManager->getReference('Acme\Book');
$reflection = ClassUtils::newReflectionClass($bookProxy);
$class = ClassUtils::getClass($bookProxy)¸
30 changes: 0 additions & 30 deletions docs/en/reference/working-with-objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,28 +162,6 @@ your code. See the following code:
echo "This will always be true!";
}
A slice of the generated proxy classes code looks like the
following piece of code. A real proxy class override ALL public
methods along the lines of the ``getName()`` method shown below:

.. code-block:: php
<?php
class UserProxy extends User implements Proxy
{
private function _load(): void
{
// lazy loading code
}
public function getName(): string
{
$this->_load();
return parent::getName();
}
// .. other public methods of User
}
.. warning::

Traversing the object graph for parts that are lazy-loaded will
Expand Down Expand Up @@ -414,14 +392,6 @@ Example:
// $entity now refers to the fully managed copy returned by the merge operation.
// The EntityManager $em now manages the persistence of $entity as usual.
.. note::

When you want to serialize/unserialize entities you
have to make all entity properties protected, never private. The
reason for this is, if you serialize a class that was a proxy
instance before, the private variables won't be serialized and a
PHP Notice is thrown.

The semantics of the merge operation, applied to an entity X, are
as follows:
Expand Down
1 change: 0 additions & 1 deletion docs/en/sidebar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/implementing-wakeup-or-clone
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
Expand Down
1 change: 0 additions & 1 deletion docs/en/toc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ Cookbook
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/implementing-wakeup-or-clone
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
Expand Down
Loading

0 comments on commit eb6bc24

Please sign in to comment.