Skip to content

Commit dc7c6a1

Browse files
committed
[#17393] Finish entity value resolver docs
1 parent 36d91fe commit dc7c6a1

File tree

2 files changed

+116
-191
lines changed

2 files changed

+116
-191
lines changed

best_practices.rst

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,13 @@ Instead, you must use dependency injection to fetch services by
246246
:ref:`type-hinting action method arguments <controller-accessing-services>` or
247247
constructor arguments.
248248

249-
Use EntityValueResolver If It Is Convenient
250-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
249+
Use Entity Value Resolvers If They Are Convenient
250+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251251

252-
If you're using :doc:`Doctrine </doctrine>`, then you can *optionally* use the
253-
`EntityValueResolver`_ to automatically query for an entity and pass it as an
254-
argument to your controller. It will also show a 404 page if no entity can be
255-
found.
252+
If you're using :doc:`Doctrine </doctrine>`, then you can *optionally* use
253+
the :ref:`EntityValueResolver <doctrine-entity-value-resolver>` to
254+
automatically query for an entity and pass it as an argument to your
255+
controller. It will also show a 404 page if no entity can be found.
256256

257257
If the logic to get an entity from a route variable is more complex, instead of
258258
configuring the EntityValueResolver, it's better to make the Doctrine query
@@ -451,7 +451,6 @@ you must set up a redirection.
451451
.. _`Symfony Demo`: https://github.com/symfony/demo
452452
.. _`download Symfony`: https://symfony.com/download
453453
.. _`Composer`: https://getcomposer.org/
454-
.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
455454
.. _`feature toggles`: https://en.wikipedia.org/wiki/Feature_toggle
456455
.. _`smoke testing`: https://en.wikipedia.org/wiki/Smoke_testing_(software)
457456
.. _`Webpack`: https://webpack.js.org/

doctrine.rst

Lines changed: 110 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -598,61 +598,21 @@ the :ref:`doctrine-queries` section.
598598
see the web debug toolbar, install the ``profiler`` :ref:`Symfony pack <symfony-packs>`
599599
by running this command: ``composer require --dev symfony/profiler-pack``.
600600

601+
.. _doctrine-entity-value-resolver:
602+
601603
Automatically Fetching Objects (EntityValueResolver)
602604
----------------------------------------------------
603605

604-
In many cases, you can use the `EntityValueResolver`_ to do the query for you
605-
automatically! First, enable the feature:
606-
607-
.. configuration-block::
608-
609-
.. code-block:: yaml
610-
611-
# config/packages/doctrine.yaml
612-
doctrine:
613-
orm:
614-
controller_resolver:
615-
enabled: true
616-
auto_mapping: true
617-
evict_cache: false
618-
619-
.. code-block:: xml
620-
621-
<!-- config/packages/doctrine.xml -->
622-
<?xml version="1.0" encoding="UTF-8" ?>
623-
<container xmlns="http://symfony.com/schema/dic/services"
624-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
625-
xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
626-
xsi:schemaLocation="http://symfony.com/schema/dic/services
627-
https://symfony.com/schema/dic/services/services-1.0.xsd
628-
http://symfony.com/schema/dic/doctrine
629-
https://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
630-
631-
<doctrine:config>
632-
<!-- by convention the env var names are always uppercase -->
633-
<doctrine:orm>
634-
<doctrine:controller_resolver auto_mapping="true" evict_cache="false"/>
635-
</doctrine:orm>
636-
</doctrine:config>
637-
638-
</container>
639-
640-
.. code-block:: php
606+
.. versionadded:: 6.2
641607

642-
// config/packages/doctrine.php
643-
use Symfony\Config\DoctrineConfig;
608+
Entity Value Resolver was introduced in Symfony 6.2.
644609

645-
return static function (DoctrineConfig $doctrine) {
646-
$controllerResolver = $doctrine->orm()
647-
->entityManager('default')
648-
// ...
649-
->controllerResolver();
610+
.. versionadded:: 2.7.1
650611

651-
$controllerResolver->autoMapping(true);
652-
$controllerResolver->evictCache(true);
653-
};
612+
Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle 2.7.1.
654613

655-
Now, simplify your controller::
614+
In many cases, you can use the ``EntityValueResolver`` to do the query for you
615+
automatically! You can simplify the controller to::
656616

657617
// src/Controller/ProductController.php
658618
namespace App\Controller;
@@ -676,15 +636,17 @@ Now, simplify your controller::
676636
That's it! The bundle uses the ``{id}`` from the route to query for the ``Product``
677637
by the ``id`` column. If it's not found, a 404 page is generated.
678638

679-
This behavior can be enabled on all your controllers, by setting the ``auto_mapping``
680-
parameter to ``true``. Or individually on the desired controllers by using the
681-
``MapEntity`` attribute:
639+
This behavior is enabled by default on all your controllers. You can
640+
disable it by setting the ``doctrine.orm.controller_resolver.auto_mapping``
641+
config option to ``false``.
642+
643+
When disabled, you can enable it individually on the desired controllers by
644+
using the ``MapEntity`` attribute::
682645

683646
// src/Controller/ProductController.php
684647
namespace App\Controller;
685648

686649
use App\Entity\Product;
687-
use App\Repository\ProductRepository;
688650
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
689651
use Symfony\Component\HttpFoundation\Response;
690652
use Symfony\Component\Routing\Annotation\Route;
@@ -702,31 +664,41 @@ parameter to ``true``. Or individually on the desired controllers by using the
702664
}
703665
}
704666

667+
.. tip::
668+
669+
When enabled globally, it's possible to disabled the behavior on a specific
670+
controller, by using the ``MapEntity`` set to ``disabled``.
671+
672+
public function show(
673+
#[CurrentUser]
674+
#[MapEntity(disabled: true)]
675+
User $user
676+
): Response {
677+
// User is not resolved by the EntityValueResolver
678+
// ...
679+
}
680+
705681
Fetch Automatically
706682
~~~~~~~~~~~~~~~~~~~
707683

708684
If your route wildcards match properties on your entity, then the resolver
709-
will automatically fetch them:
710-
711-
.. configuration-block::
712-
713-
.. code-block:: php-attributes
685+
will automatically fetch them::
714686

715-
/**
716-
* Fetch via primary key because {id} is in the route.
717-
*/
718-
#[Route('/product/{id}')]
719-
public function showByPk(Post $post): Response
720-
{
721-
}
687+
/**
688+
* Fetch via primary key because {id} is in the route.
689+
*/
690+
#[Route('/product/{id}')]
691+
public function showByPk(Post $post): Response
692+
{
693+
}
722694

723-
/**
724-
* Perform a findOneBy() where the slug property matches {slug}.
725-
*/
726-
#[Route('/product/{slug}')]
727-
public function showBySlug(Post $post): Response
728-
{
729-
}
695+
/**
696+
* Perform a findOneBy() where the slug property matches {slug}.
697+
*/
698+
#[Route('/product/{slug}')]
699+
public function showBySlug(Post $post): Response
700+
{
701+
}
730702

731703
Automatic fetching works in these situations:
732704

@@ -743,145 +715,99 @@ attribute and using the `MapEntity options`_.
743715
Fetch via an Expression
744716
~~~~~~~~~~~~~~~~~~~~~~~
745717

746-
If automatic fetching doesn't work, use an expression:
747-
748-
.. configuration-block::
749-
750-
.. code-block:: php-attributes
718+
If automatic fetching doesn't work, you can write an expression using the
719+
:doc:`ExpressionLanguage component </components/expression_language>`::
751720

752-
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
753-
754-
#[Route('/product/{product_id}')]
755-
public function show(
756-
#[MapEntity(expr: 'repository.find(product_id)')]
757-
Product $product
758-
): Response {
759-
}
760-
761-
Use the special ``MapEntity`` attribute with an ``expr`` option to
762-
fetch the object by calling a method on your repository. The
763-
``repository`` method will be your entity's Repository class and
764-
any route wildcards - like ``{product_id}`` are available as variables.
765-
766-
This can also be used to help resolve multiple arguments:
721+
#[Route('/product/{product_id}')]
722+
public function show(
723+
#[MapEntity(expr: 'repository.find(product_id)')]
724+
Product $product
725+
): Response {
726+
}
767727

768-
.. configuration-block::
728+
In the expression, the ``repository`` variable will be your entity's
729+
Repository class and any route wildcards - like ``{product_id}`` are
730+
available as variables.
769731

770-
.. code-block:: php-attributes
732+
This can also be used to help resolve multiple arguments::
771733

772-
#[Route('/product/{id}/comments/{comment_id}')]
773-
public function show(
774-
Product $product
775-
#[MapEntity(expr: 'repository.find(comment_id)')]
776-
Comment $comment
777-
): Response {
778-
}
734+
#[Route('/product/{id}/comments/{comment_id}')]
735+
public function show(
736+
Product $product
737+
#[MapEntity(expr: 'repository.find(comment_id)')]
738+
Comment $comment
739+
): Response {
740+
}
779741

780-
In the example above, the ``$product`` argument is handled automatically,
742+
In the example above, the ``$product`` argument is handled automatically,
781743
but ``$comment`` is configured with the attribute since they cannot both follow
782744
the default convention.
783745

784-
.. _`MapEntity options`:
785-
786-
787746
MapEntity Options
788747
~~~~~~~~~~~~~~~~~
789748

790-
A number of ``options`` are available on the ``MapEntity`` annotation to
749+
A number of options are available on the ``MapEntity`` annotation to
791750
control behavior:
792751

793-
* ``id``: If an ``id`` option is configured and matches a route parameter, then
794-
the resolver will find by the primary key:
795-
796-
.. configuration-block::
797-
798-
.. code-block:: php-attributes
799-
800-
#[Route('/product/{product_id}')]
801-
public function show(
802-
Product $product
803-
#[MapEntity(id: 'product_id')]
804-
Comment $comment
805-
): Response {
806-
}
807-
808-
* ``mapping``: Configures the properties and values to use with the ``findOneBy()``
809-
method: the key is the route placeholder name and the value is the Doctrine
810-
property name:
811-
812-
.. configuration-block::
813-
814-
.. code-block:: php-attributes
815-
816-
#[Route('/product/{category}/{slug}/comments/{comment_slug}')]
817-
public function show(
818-
#[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])]
819-
Product $product
820-
#[MapEntity(mapping: ['comment_slug' => 'slug'])]
821-
Comment $comment
822-
): Response {
823-
}
824-
825-
* ``exclude`` Configures the properties that should be used in the ``findOneBy()``
826-
method by *excluding* one or more properties so that not *all* are used:
827-
828-
.. configuration-block::
829-
830-
.. code-block:: php-attributes
831-
832-
#[Route('/product/{slug}/{date}')]
833-
public function show(
834-
#[MapEntity(exclude: ['date'])]
835-
Product $product
836-
\DateTime $date
837-
): Response {
838-
}
839-
840-
* ``stripNull`` If true, then when ``findOneBy()`` is used, any values that
841-
are ``null`` will not be used for the query.
842-
843-
* ``entityManager`` By default, the ``EntityValueResolver`` uses the *default*
844-
entity manager, but you can configure this:
752+
``id``
753+
If an ``id`` option is configured and matches a route parameter, then
754+
the resolver will find by the primary key::
845755

846-
.. configuration-block::
756+
#[Route('/product/{product_id}')]
757+
public function show(
758+
Product $product
759+
#[MapEntity(id: 'product_id')]
760+
Comment $comment
761+
): Response {
762+
}
847763

848-
.. code-block:: php-attributes
764+
``mapping``
765+
Configures the properties and values to use with the ``findOneBy()``
766+
method: the key is the route placeholder name and the value is the Doctrine
767+
property name::
849768

850-
#[Route('/product/{id}')]
851-
public function show(
852-
#[MapEntity(entityManager: ['foo'])]
853-
Product $product
854-
): Response {
855-
}
769+
#[Route('/product/{category}/{slug}/comments/{comment_slug}')]
770+
public function show(
771+
#[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])]
772+
Product $product
773+
#[MapEntity(mapping: ['comment_slug' => 'slug'])]
774+
Comment $comment
775+
): Response {
776+
}
856777

857-
* ``evictCache`` If true, forces Doctrine to always fetch the entity from the
858-
database instead of cache.
778+
``exclude``
779+
Configures the properties that should be used in the ``findOneBy()``
780+
method by *excluding* one or more properties so that not *all* are used:
859781

860-
* ``disabled`` If true, the ``EntityValueResolver`` will not try to replace
861-
the argument.
782+
#[Route('/product/{slug}/{date}')]
783+
public function show(
784+
#[MapEntity(exclude: ['date'])]
785+
Product $product
786+
\DateTime $date
787+
): Response {
788+
}
862789

863-
.. tip::
790+
``stripNull``
791+
If true, then when ``findOneBy()`` is used, any values that are
792+
``null`` will not be used for the query.
864793

865-
When enabled globally, it's possible to disabled the behavior on a specific
866-
controller, by using the ``MapEntity`` set to ``disabled``.
794+
``entityManager``
795+
By default, the ``EntityValueResolver`` uses the *default*
796+
entity manager, but you can configure this::
867797

798+
#[Route('/product/{id}')]
868799
public function show(
869-
#[CurrentUser]
870-
#[MapEntity(disabled: true)]
871-
User $user
800+
#[MapEntity(entityManager: ['foo'])]
801+
Product $product
872802
): Response {
873-
// User is not resolved by the EntityValueResolver
874-
// ...
875803
}
876804

877-
.. versionadded:: 6.2
878-
879-
Entity Value Resolver was introduced in Symfony 6.2.
880-
881-
.. versionadded:: 2.7.1
805+
``evictCache``
806+
If true, forces Doctrine to always fetch the entity from the database
807+
instead of cache.
882808

883-
Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle
884-
2.7.1.
809+
``disabled``
810+
If true, the ``EntityValueResolver`` will not try to replace the argument.
885811

886812
Updating an Object
887813
------------------

0 commit comments

Comments
 (0)