File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 9292 $ routes ->add ('send_message ' , '/send-message ' )
9393 ->controller (App \Controller \SendMessageController::class)
9494 ->methods (['GET ' ]);
95+
96+ $ routes ->add ('app_create_user ' , '/create-user ' )
97+ ->controller (App \Controller \CreateUserController::class)
98+ ->methods (['GET ' ]);
99+
100+ $ routes ->add ('app_create_user_with_confirmation ' , '/create-user-with-confirmation ' )
101+ ->controller (App \Controller \CreateUserWithConfirmationController::class)
102+ ->methods (['GET ' ]);
95103};
Original file line number Diff line number Diff line change 2626 'entity ' => User::class,
2727 'lazy ' => true ,
2828 ]);
29+
30+ // Doctrine listeners used by App\Tests\Functional\IssuesCest to reproduce the
31+ // security/request context regressions. Each is public so the tests can grab
32+ // it and read the state it captured during the request.
33+
34+ // Issue #34 (entity-listener path).
35+ $ services ->set (App \Doctrine \CurrentUserListener::class)
36+ ->public ()
37+ ->tag ('doctrine.orm.entity_listener ' , [
38+ 'event ' => Events::prePersist,
39+ 'entity ' => User::class,
40+ 'lazy ' => true ,
41+ ]);
42+
43+ // Issue #34 (event-listener path).
44+ $ services ->set (App \Doctrine \CurrentUserEventListener::class)
45+ ->public ();
46+
47+ // Issue #150 (request_stack inside a Doctrine listener).
48+ $ services ->set (App \Doctrine \RequestStackListener::class)
49+ ->public ();
50+
51+ // Issue #151 (shared listener instance across reboots).
52+ $ services ->set (App \Doctrine \FlushCounterListener::class)
53+ ->public ();
54+
55+ // Issue #90 (email sent from a Doctrine entity listener).
56+ $ services ->set (App \Doctrine \SendConfirmationListener::class)
57+ ->tag ('doctrine.orm.entity_listener ' , [
58+ 'event ' => Events::postPersist,
59+ 'entity ' => User::class,
60+ 'lazy ' => true ,
61+ ]);
2962};
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Controller ;
6+
7+ use App \Entity \User ;
8+ use Doctrine \ORM \EntityManagerInterface ;
9+ use Symfony \Bundle \FrameworkBundle \Controller \AbstractController ;
10+ use Symfony \Component \HttpFoundation \Response ;
11+
12+ /**
13+ * Persists a user (one prePersist + one flush) so the Doctrine listeners under
14+ * test fire within a real request. Shared trigger for issues #34, #150 and #151.
15+ */
16+ final class CreateUserController extends AbstractController
17+ {
18+ public function __invoke (EntityManagerInterface $ em ): Response
19+ {
20+ $ em ->persist (User::create ('jane_doe@gmail.com ' , '123456 ' ));
21+ $ em ->flush ();
22+
23+ return new Response ('Created ' );
24+ }
25+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Controller ;
6+
7+ use App \Doctrine \SendConfirmationListener ;
8+ use App \Entity \User ;
9+ use Doctrine \ORM \EntityManagerInterface ;
10+ use Symfony \Bundle \FrameworkBundle \Controller \AbstractController ;
11+ use Symfony \Component \HttpFoundation \Response ;
12+
13+ /**
14+ * Persists the marker user that makes App\Doctrine\SendConfirmationListener send
15+ * a confirmation email from inside a Doctrine entity listener. Trigger for #90.
16+ */
17+ final class CreateUserWithConfirmationController extends AbstractController
18+ {
19+ public function __invoke (EntityManagerInterface $ em ): Response
20+ {
21+ $ em ->persist (User::create (SendConfirmationListener::TRIGGER_EMAIL , '123456 ' ));
22+ $ em ->flush ();
23+
24+ return new Response ('Created ' );
25+ }
26+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Doctrine ;
6+
7+ use App \Entity \User ;
8+ use Doctrine \Bundle \DoctrineBundle \Attribute \AsDoctrineListener ;
9+ use Doctrine \ORM \Event \PrePersistEventArgs ;
10+ use Doctrine \ORM \Events ;
11+ use Symfony \Component \Security \Core \Security ;
12+
13+ /**
14+ * Issue #34, event-listener path: a Doctrine event listener registered on the
15+ * connection's event manager (not the ORM entity-listener resolver) must also
16+ * see the logged-in user. This is the case the issue thread describes as a
17+ * Doctrine "subscriber". Triggered by the `/create-user` route.
18+ *
19+ * @see https://github.com/Codeception/module-symfony/issues/34
20+ */
21+ #[AsDoctrineListener(event: Events::prePersist)]
22+ final class CurrentUserEventListener
23+ {
24+ public ?string $ currentUserIdentifier = null ;
25+
26+ public function __construct (private readonly Security $ security )
27+ {
28+ }
29+
30+ public function prePersist (PrePersistEventArgs $ args ): void
31+ {
32+ if (!$ args ->getObject () instanceof User) {
33+ return ;
34+ }
35+
36+ $ this ->currentUserIdentifier = $ this ->security ->getUser ()?->getUserIdentifier();
37+ }
38+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Doctrine ;
6+
7+ use App \Entity \User ;
8+ use Symfony \Component \Security \Core \Security ;
9+
10+ /**
11+ * Issue #34, entity-listener path: the security token must be available inside a
12+ * Doctrine entity listener, exactly as it is inside a controller. Registered as a
13+ * lazy `doctrine.orm.entity_listener` in config/services.php and triggered by the
14+ * `/create-user` route (App\Controller\CreateUserController).
15+ *
16+ * @see https://github.com/Codeception/module-symfony/issues/34
17+ */
18+ final class CurrentUserListener
19+ {
20+ public ?string $ currentUserIdentifier = null ;
21+
22+ public function __construct (private readonly Security $ security )
23+ {
24+ }
25+
26+ public function prePersist (User $ user ): void
27+ {
28+ $ this ->currentUserIdentifier = $ this ->security ->getUser ()?->getUserIdentifier();
29+ }
30+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Doctrine ;
6+
7+ use Doctrine \Bundle \DoctrineBundle \Attribute \AsDoctrineListener ;
8+ use Doctrine \ORM \Event \OnFlushEventArgs ;
9+ use Doctrine \ORM \Events ;
10+
11+ /**
12+ * Issue #151: when the EntityManager was persisted across kernel reboots, the
13+ * listener instance held by Doctrine diverged from the one returned by
14+ * grabService(), so state recorded during the request was invisible to the test.
15+ * Both must be the same shared instance. Triggered by the `/create-user` route,
16+ * which performs exactly one flush.
17+ *
18+ * @see https://github.com/Codeception/module-symfony/issues/151
19+ */
20+ #[AsDoctrineListener(event: Events::onFlush)]
21+ final class FlushCounterListener
22+ {
23+ public int $ flushes = 0 ;
24+
25+ public function onFlush (OnFlushEventArgs $ args ): void
26+ {
27+ ++$ this ->flushes ;
28+ }
29+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Doctrine ;
6+
7+ use App \Entity \User ;
8+ use Doctrine \Bundle \DoctrineBundle \Attribute \AsDoctrineListener ;
9+ use Doctrine \ORM \Event \PrePersistEventArgs ;
10+ use Doctrine \ORM \Events ;
11+ use Symfony \Component \HttpFoundation \RequestStack ;
12+
13+ /**
14+ * Issue #150: the request_stack injected into a Doctrine listener must expose the
15+ * current request (and therefore its locale and session), instead of returning
16+ * null as it did when the EntityManager was persisted across kernel reboots.
17+ * Triggered by the `/create-user` route.
18+ *
19+ * @see https://github.com/Codeception/module-symfony/issues/150
20+ */
21+ #[AsDoctrineListener(event: Events::prePersist)]
22+ final class RequestStackListener
23+ {
24+ public bool $ hasRequest = false ;
25+ public ?string $ currentLocale = null ;
26+
27+ public function __construct (private readonly RequestStack $ requestStack )
28+ {
29+ }
30+
31+ public function prePersist (PrePersistEventArgs $ args ): void
32+ {
33+ if (!$ args ->getObject () instanceof User) {
34+ return ;
35+ }
36+
37+ $ request = $ this ->requestStack ->getCurrentRequest ();
38+ $ this ->hasRequest = $ request !== null ;
39+ $ this ->currentLocale = $ request ?->getLocale();
40+ }
41+ }
Original file line number Diff line number Diff line change 1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace App \Doctrine ;
6+
7+ use App \Entity \User ;
8+ use App \Utils \Mailer ;
9+
10+ /**
11+ * Issue #90: an email sent from a Doctrine entity listener must be collected by
12+ * the profiler so that seeEmailIsSent() works. Registered as a lazy
13+ * `doctrine.orm.entity_listener` and triggered by the dedicated
14+ * `/create-user-with-confirmation` route.
15+ *
16+ * @see https://github.com/Codeception/module-symfony/issues/90
17+ */
18+ final class SendConfirmationListener
19+ {
20+ /**
21+ * Marker address: only this user triggers the mail, so the listener
22+ * does not interfere with the email counts asserted in MailerCest.
23+ */
24+ public const TRIGGER_EMAIL = 'issue90-listener@example.com ' ;
25+
26+ public function __construct (private readonly Mailer $ mailer )
27+ {
28+ }
29+
30+ public function postPersist (User $ user ): void
31+ {
32+ if ($ user ->getEmail () === self ::TRIGGER_EMAIL ) {
33+ $ this ->mailer ->sendConfirmationEmail ($ user );
34+ }
35+ }
36+ }
You can’t perform that action at this time.
0 commit comments