@@ -598,17 +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- Automatically Fetching Objects (ParamConverter)
602- -----------------------------------------------
601+ .. _doctrine-entity-value-resolver :
603602
604- In many cases, you can use the ` SensioFrameworkExtraBundle `_ to do the query
605- for you automatically! First, install the bundle in case you don't have it:
603+ Automatically Fetching Objects (EntityValueResolver)
604+ ----------------------------------------------------
606605
607- .. code-block :: terminal
606+ .. versionadded :: 6.2
607+
608+ Entity Value Resolver was introduced in Symfony 6.2.
608609
609- $ composer require sensio/framework-extra-bundle
610+ .. versionadded :: 2.7.1
610611
611- Now, simplify your controller::
612+ Autowiring of the ``EntityValueResolver `` was introduced in DoctrineBundle 2.7.1.
613+
614+ In many cases, you can use the ``EntityValueResolver `` to do the query for you
615+ automatically! You can simplify the controller to::
612616
613617 // src/Controller/ProductController.php
614618 namespace App\Controller;
@@ -621,7 +625,7 @@ Now, simplify your controller::
621625
622626 class ProductController extends AbstractController
623627 {
624- #[Route('/product/{id}', name: 'product_show' )]
628+ #[Route('/product/{id}')]
625629 public function show(Product $product): Response
626630 {
627631 // use the Product!
@@ -632,7 +636,178 @@ Now, simplify your controller::
632636That's it! The bundle uses the ``{id} `` from the route to query for the ``Product ``
633637by the ``id `` column. If it's not found, a 404 page is generated.
634638
635- There are many more options you can use. Read more about the `ParamConverter `_.
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::
645+
646+ // src/Controller/ProductController.php
647+ namespace App\Controller;
648+
649+ use App\Entity\Product;
650+ use Symfony\Bridge\Doctrine\Attribute\MapEntity;
651+ use Symfony\Component\HttpFoundation\Response;
652+ use Symfony\Component\Routing\Annotation\Route;
653+ // ...
654+
655+ class ProductController extends AbstractController
656+ {
657+ #[Route('/product/{id}')]
658+ public function show(
659+ #[MapEntity]
660+ Product $product
661+ ): Response {
662+ // use the Product!
663+ // ...
664+ }
665+ }
666+
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+
681+ Fetch Automatically
682+ ~~~~~~~~~~~~~~~~~~~
683+
684+ If your route wildcards match properties on your entity, then the resolver
685+ will automatically fetch them::
686+
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+ }
694+
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+ }
702+
703+ Automatic fetching works in these situations:
704+
705+ * If ``{id} `` is in your route, then this is used to fetch by
706+ primary key via the ``find() `` method.
707+
708+ * The resolver will attempt to do a ``findOneBy() `` fetch by using
709+ *all * of the wildcards in your route that are actually properties
710+ on your entity (non-properties are ignored).
711+
712+ You can control this behavior by actually *adding * the ``MapEntity ``
713+ attribute and using the `MapEntity options `_.
714+
715+ Fetch via an Expression
716+ ~~~~~~~~~~~~~~~~~~~~~~~
717+
718+ If automatic fetching doesn't work, you can write an expression using the
719+ :doc: `ExpressionLanguage component </components/expression_language >`::
720+
721+ #[Route('/product/{product_id}')]
722+ public function show(
723+ #[MapEntity(expr: 'repository.find(product_id)')]
724+ Product $product
725+ ): Response {
726+ }
727+
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.
731+
732+ This can also be used to help resolve multiple arguments::
733+
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+ }
741+
742+ In the example above, the ``$product `` argument is handled automatically,
743+ but ``$comment `` is configured with the attribute since they cannot both follow
744+ the default convention.
745+
746+ MapEntity Options
747+ ~~~~~~~~~~~~~~~~~
748+
749+ A number of options are available on the ``MapEntity `` annotation to
750+ control behavior:
751+
752+ ``id ``
753+ If an ``id `` option is configured and matches a route parameter, then
754+ the resolver will find by the primary key::
755+
756+ #[Route('/product/{product_id}')]
757+ public function show(
758+ Product $product
759+ #[MapEntity(id: 'product_id')]
760+ Comment $comment
761+ ): Response {
762+ }
763+
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::
768+
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+ }
777+
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:
781+
782+ #[Route('/product/{slug}/{date}')]
783+ public function show(
784+ #[MapEntity(exclude: ['date'])]
785+ Product $product
786+ \D ateTime $date
787+ ): Response {
788+ }
789+
790+ ``stripNull ``
791+ If true, then when ``findOneBy() `` is used, any values that are
792+ ``null `` will not be used for the query.
793+
794+ ``entityManager ``
795+ By default, the ``EntityValueResolver `` uses the *default *
796+ entity manager, but you can configure this::
797+
798+ #[Route('/product/{id}')]
799+ public function show(
800+ #[MapEntity(entityManager: ['foo'])]
801+ Product $product
802+ ): Response {
803+ }
804+
805+ ``evictCache ``
806+ If true, forces Doctrine to always fetch the entity from the database
807+ instead of cache.
808+
809+ ``disabled ``
810+ If true, the ``EntityValueResolver `` will not try to replace the argument.
636811
637812Updating an Object
638813------------------
@@ -895,8 +1070,6 @@ Learn more
8951070.. _`Transactions and Concurrency` : https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/transactions-and-concurrency.html
8961071.. _`DoctrineMigrationsBundle` : https://github.com/doctrine/DoctrineMigrationsBundle
8971072.. _`NativeQuery` : https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/native-sql.html
898- .. _`SensioFrameworkExtraBundle` : https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html
899- .. _`ParamConverter` : https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
9001073.. _`limit of 767 bytes for the index key prefix` : https://dev.mysql.com/doc/refman/5.6/en/innodb-limits.html
9011074.. _`Doctrine screencast series` : https://symfonycasts.com/screencast/symfony-doctrine
9021075.. _`API Platform` : https://api-platform.com/docs/core/validation/
0 commit comments