@@ -598,17 +598,21 @@ the :ref:`doctrine-queries` section.
598
598
see the web debug toolbar, install the ``profiler `` :ref: `Symfony pack <symfony-packs >`
599
599
by running this command: ``composer require --dev symfony/profiler-pack ``.
600
600
601
- Automatically Fetching Objects (ParamConverter)
602
- -----------------------------------------------
601
+ .. _doctrine-entity-value-resolver :
603
602
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
+ ----------------------------------------------------
606
605
607
- .. code-block :: terminal
606
+ .. versionadded :: 6.2
607
+
608
+ Entity Value Resolver was introduced in Symfony 6.2.
608
609
609
- $ composer require sensio/framework-extra-bundle
610
+ .. versionadded :: 2.7.1
610
611
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::
612
616
613
617
// src/Controller/ProductController.php
614
618
namespace App\Controller;
@@ -621,7 +625,7 @@ Now, simplify your controller::
621
625
622
626
class ProductController extends AbstractController
623
627
{
624
- #[Route('/product/{id}', name: 'product_show' )]
628
+ #[Route('/product/{id}')]
625
629
public function show(Product $product): Response
626
630
{
627
631
// use the Product!
@@ -632,7 +636,178 @@ Now, simplify your controller::
632
636
That's it! The bundle uses the ``{id} `` from the route to query for the ``Product ``
633
637
by the ``id `` column. If it's not found, a 404 page is generated.
634
638
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.
636
811
637
812
Updating an Object
638
813
------------------
@@ -895,8 +1070,6 @@ Learn more
895
1070
.. _`Transactions and Concurrency` : https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/transactions-and-concurrency.html
896
1071
.. _`DoctrineMigrationsBundle` : https://github.com/doctrine/DoctrineMigrationsBundle
897
1072
.. _`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
900
1073
.. _`limit of 767 bytes for the index key prefix` : https://dev.mysql.com/doc/refman/5.6/en/innodb-limits.html
901
1074
.. _`Doctrine screencast series` : https://symfonycasts.com/screencast/symfony-doctrine
902
1075
.. _`API Platform` : https://api-platform.com/docs/core/validation/
0 commit comments