cc-codeconductor 0.2.10 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/index.js +1636 -1078
- package/package.json +1 -1
- package/presets/agy/AGENTS.md +46 -2
- package/presets/claude/CLAUDE.md +62 -2
- package/presets/claude/skills/android/SKILL.md +119 -0
- package/presets/claude/skills/laravel-specialist/SKILL.md +264 -0
- package/presets/claude/skills/laravel-specialist/references/eloquent.md +351 -0
- package/presets/claude/skills/laravel-specialist/references/livewire.md +512 -0
- package/presets/claude/skills/laravel-specialist/references/queues.md +423 -0
- package/presets/claude/skills/laravel-specialist/references/routing.md +362 -0
- package/presets/claude/skills/laravel-specialist/references/testing.md +522 -0
- package/presets/claude/skills/php-pro/SKILL.md +208 -0
- package/presets/claude/skills/php-pro/references/async-patterns.md +412 -0
- package/presets/claude/skills/php-pro/references/laravel-patterns.md +377 -0
- package/presets/claude/skills/php-pro/references/modern-php-features.md +323 -0
- package/presets/claude/skills/php-pro/references/symfony-patterns.md +466 -0
- package/presets/claude/skills/php-pro/references/testing-quality.md +466 -0
- package/presets/codex/AGENTS.md +47 -2
- package/presets/codex/skills/android/SKILL.md +119 -0
- package/presets/codex/skills/laravel-specialist/SKILL.md +264 -0
- package/presets/codex/skills/laravel-specialist/references/eloquent.md +351 -0
- package/presets/codex/skills/laravel-specialist/references/livewire.md +512 -0
- package/presets/codex/skills/laravel-specialist/references/queues.md +423 -0
- package/presets/codex/skills/laravel-specialist/references/routing.md +362 -0
- package/presets/codex/skills/laravel-specialist/references/testing.md +522 -0
- package/presets/codex/skills/php-pro/SKILL.md +208 -0
- package/presets/codex/skills/php-pro/references/async-patterns.md +412 -0
- package/presets/codex/skills/php-pro/references/laravel-patterns.md +377 -0
- package/presets/codex/skills/php-pro/references/modern-php-features.md +323 -0
- package/presets/codex/skills/php-pro/references/symfony-patterns.md +466 -0
- package/presets/codex/skills/php-pro/references/testing-quality.md +466 -0
- package/presets/opencode/agents/implementer.md +2 -2
- package/presets/opencode/agents/orchestrator.md +132 -5
- package/presets/opencode/agents/reviewer.md +33 -0
- package/presets/opencode/prompts/v0.3.0/architect.md +221 -0
- package/presets/opencode/prompts/v0.3.0/docs.md +189 -0
- package/presets/opencode/prompts/v0.3.0/implementer.md +162 -0
- package/presets/opencode/prompts/v0.3.0/orchestrator.md +360 -0
- package/presets/opencode/prompts/v0.3.0/repo-explorer.md +110 -0
- package/presets/opencode/prompts/v0.3.0/reviewer.md +225 -0
- package/presets/opencode/prompts/v0.3.0/task-coach.md +155 -0
- package/presets/opencode/prompts/v0.3.0/tester.md +251 -0
- package/presets/opencode/skills/android/SKILL.md +119 -0
- package/presets/opencode/skills/laravel-specialist/SKILL.md +264 -0
- package/presets/opencode/skills/laravel-specialist/references/eloquent.md +351 -0
- package/presets/opencode/skills/laravel-specialist/references/livewire.md +512 -0
- package/presets/opencode/skills/laravel-specialist/references/queues.md +423 -0
- package/presets/opencode/skills/laravel-specialist/references/routing.md +362 -0
- package/presets/opencode/skills/laravel-specialist/references/testing.md +522 -0
- package/presets/opencode/skills/php-pro/SKILL.md +208 -0
- package/presets/opencode/skills/php-pro/references/async-patterns.md +412 -0
- package/presets/opencode/skills/php-pro/references/laravel-patterns.md +377 -0
- package/presets/opencode/skills/php-pro/references/modern-php-features.md +323 -0
- package/presets/opencode/skills/php-pro/references/symfony-patterns.md +466 -0
- package/presets/opencode/skills/php-pro/references/testing-quality.md +466 -0
- package/src/presets/manifests/agy.yml +3 -2
- package/src/presets/manifests/claude.yml +3 -2
- package/src/presets/manifests/codex.yml +3 -2
- package/src/presets/manifests/cursor.yml +3 -2
- package/src/presets/manifests/gemini.yml +3 -2
- package/src/presets/manifests/opencode.yml +3 -2
- package/src/presets/models/opencode.yml +6 -6
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Symfony Patterns
|
|
2
|
+
|
|
3
|
+
## Dependency Injection
|
|
4
|
+
|
|
5
|
+
```php
|
|
6
|
+
<?php
|
|
7
|
+
|
|
8
|
+
declare(strict_types=1);
|
|
9
|
+
|
|
10
|
+
namespace App\Service;
|
|
11
|
+
|
|
12
|
+
use App\Repository\UserRepositoryInterface;
|
|
13
|
+
use Psr\Log\LoggerInterface;
|
|
14
|
+
use Symfony\Component\Mailer\MailerInterface;
|
|
15
|
+
|
|
16
|
+
final readonly class UserService
|
|
17
|
+
{
|
|
18
|
+
public function __construct(
|
|
19
|
+
private UserRepositoryInterface $userRepository,
|
|
20
|
+
private MailerInterface $mailer,
|
|
21
|
+
private LoggerInterface $logger,
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
public function createUser(string $email, string $password): User
|
|
25
|
+
{
|
|
26
|
+
$user = new User($email, password_hash($password, PASSWORD_ARGON2ID));
|
|
27
|
+
|
|
28
|
+
$this->userRepository->save($user);
|
|
29
|
+
$this->logger->info('User created', ['email' => $email]);
|
|
30
|
+
|
|
31
|
+
return $user;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Service Configuration (services.yaml)
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
# config/services.yaml
|
|
40
|
+
services:
|
|
41
|
+
_defaults:
|
|
42
|
+
autowire: true
|
|
43
|
+
autoconfigure: true
|
|
44
|
+
bind:
|
|
45
|
+
string $projectDir: '%kernel.project_dir%'
|
|
46
|
+
bool $isDebug: '%kernel.debug%'
|
|
47
|
+
|
|
48
|
+
App\:
|
|
49
|
+
resource: '../src/'
|
|
50
|
+
exclude:
|
|
51
|
+
- '../src/DependencyInjection/'
|
|
52
|
+
- '../src/Entity/'
|
|
53
|
+
- '../src/Kernel.php'
|
|
54
|
+
|
|
55
|
+
# Interface binding
|
|
56
|
+
App\Repository\UserRepositoryInterface:
|
|
57
|
+
class: App\Repository\DoctrineUserRepository
|
|
58
|
+
|
|
59
|
+
# Service with specific configuration
|
|
60
|
+
App\Service\PaymentService:
|
|
61
|
+
arguments:
|
|
62
|
+
$apiKey: '%env(PAYMENT_API_KEY)%'
|
|
63
|
+
$timeout: 30
|
|
64
|
+
|
|
65
|
+
# Tagged services
|
|
66
|
+
App\EventSubscriber\:
|
|
67
|
+
resource: '../src/EventSubscriber/'
|
|
68
|
+
tags: ['kernel.event_subscriber']
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Controllers with Attributes
|
|
72
|
+
|
|
73
|
+
```php
|
|
74
|
+
<?php
|
|
75
|
+
|
|
76
|
+
declare(strict_types=1);
|
|
77
|
+
|
|
78
|
+
namespace App\Controller;
|
|
79
|
+
|
|
80
|
+
use App\DTO\CreateUserRequest;
|
|
81
|
+
use App\Entity\User;
|
|
82
|
+
use App\Service\UserService;
|
|
83
|
+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
84
|
+
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
85
|
+
use Symfony\Component\HttpFoundation\Response;
|
|
86
|
+
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
|
87
|
+
use Symfony\Component\Routing\Attribute\Route;
|
|
88
|
+
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|
89
|
+
|
|
90
|
+
#[Route('/api/users', name: 'api_users_')]
|
|
91
|
+
final class UserController extends AbstractController
|
|
92
|
+
{
|
|
93
|
+
public function __construct(
|
|
94
|
+
private readonly UserService $userService,
|
|
95
|
+
) {}
|
|
96
|
+
|
|
97
|
+
#[Route('', name: 'list', methods: ['GET'])]
|
|
98
|
+
#[IsGranted('ROLE_USER')]
|
|
99
|
+
public function list(): JsonResponse
|
|
100
|
+
{
|
|
101
|
+
$users = $this->userService->getAllUsers();
|
|
102
|
+
|
|
103
|
+
return $this->json($users, Response::HTTP_OK, [], [
|
|
104
|
+
'groups' => ['user:read'],
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#[Route('', name: 'create', methods: ['POST'])]
|
|
109
|
+
#[IsGranted('ROLE_ADMIN')]
|
|
110
|
+
public function create(
|
|
111
|
+
#[MapRequestPayload] CreateUserRequest $request
|
|
112
|
+
): JsonResponse {
|
|
113
|
+
$user = $this->userService->createUser(
|
|
114
|
+
$request->email,
|
|
115
|
+
$request->password
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return $this->json($user, Response::HTTP_CREATED, [], [
|
|
119
|
+
'groups' => ['user:read'],
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#[Route('/{id}', name: 'show', methods: ['GET'])]
|
|
124
|
+
public function show(User $user): JsonResponse
|
|
125
|
+
{
|
|
126
|
+
$this->denyAccessUnlessGranted('view', $user);
|
|
127
|
+
|
|
128
|
+
return $this->json($user, context: ['groups' => ['user:detail']]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## DTOs with Validation
|
|
134
|
+
|
|
135
|
+
```php
|
|
136
|
+
<?php
|
|
137
|
+
|
|
138
|
+
declare(strict_types=1);
|
|
139
|
+
|
|
140
|
+
namespace App\DTO;
|
|
141
|
+
|
|
142
|
+
use Symfony\Component\Validator\Constraints as Assert;
|
|
143
|
+
|
|
144
|
+
final readonly class CreateUserRequest
|
|
145
|
+
{
|
|
146
|
+
public function __construct(
|
|
147
|
+
#[Assert\NotBlank]
|
|
148
|
+
#[Assert\Email]
|
|
149
|
+
public string $email,
|
|
150
|
+
|
|
151
|
+
#[Assert\NotBlank]
|
|
152
|
+
#[Assert\Length(min: 8, max: 100)]
|
|
153
|
+
#[Assert\PasswordStrength(minScore: Assert\PasswordStrength::STRENGTH_MEDIUM)]
|
|
154
|
+
public string $password,
|
|
155
|
+
|
|
156
|
+
#[Assert\NotBlank]
|
|
157
|
+
#[Assert\Length(min: 2, max: 100)]
|
|
158
|
+
public string $name,
|
|
159
|
+
|
|
160
|
+
#[Assert\Choice(choices: ['admin', 'user', 'moderator'])]
|
|
161
|
+
public string $role = 'user',
|
|
162
|
+
) {}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
final readonly class UpdateUserRequest
|
|
166
|
+
{
|
|
167
|
+
public function __construct(
|
|
168
|
+
#[Assert\Email]
|
|
169
|
+
public ?string $email = null,
|
|
170
|
+
|
|
171
|
+
#[Assert\Length(min: 2, max: 100)]
|
|
172
|
+
public ?string $name = null,
|
|
173
|
+
|
|
174
|
+
#[Assert\Type('bool')]
|
|
175
|
+
public ?bool $isActive = null,
|
|
176
|
+
) {}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Event Subscribers
|
|
181
|
+
|
|
182
|
+
```php
|
|
183
|
+
<?php
|
|
184
|
+
|
|
185
|
+
declare(strict_types=1);
|
|
186
|
+
|
|
187
|
+
namespace App\EventSubscriber;
|
|
188
|
+
|
|
189
|
+
use App\Event\UserRegisteredEvent;
|
|
190
|
+
use Psr\Log\LoggerInterface;
|
|
191
|
+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
192
|
+
use Symfony\Component\Mailer\MailerInterface;
|
|
193
|
+
|
|
194
|
+
final readonly class UserSubscriber implements EventSubscriberInterface
|
|
195
|
+
{
|
|
196
|
+
public function __construct(
|
|
197
|
+
private MailerInterface $mailer,
|
|
198
|
+
private LoggerInterface $logger,
|
|
199
|
+
) {}
|
|
200
|
+
|
|
201
|
+
public static function getSubscribedEvents(): array
|
|
202
|
+
{
|
|
203
|
+
return [
|
|
204
|
+
UserRegisteredEvent::class => [
|
|
205
|
+
['sendWelcomeEmail', 10],
|
|
206
|
+
['logRegistration', 5],
|
|
207
|
+
],
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public function sendWelcomeEmail(UserRegisteredEvent $event): void
|
|
212
|
+
{
|
|
213
|
+
$user = $event->getUser();
|
|
214
|
+
// Send email logic
|
|
215
|
+
$this->logger->info('Welcome email sent', ['user_id' => $user->getId()]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
public function logRegistration(UserRegisteredEvent $event): void
|
|
219
|
+
{
|
|
220
|
+
$this->logger->info('User registered', [
|
|
221
|
+
'user_id' => $event->getUser()->getId(),
|
|
222
|
+
'email' => $event->getUser()->getEmail(),
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Custom Events
|
|
229
|
+
|
|
230
|
+
```php
|
|
231
|
+
<?php
|
|
232
|
+
|
|
233
|
+
declare(strict_types=1);
|
|
234
|
+
|
|
235
|
+
namespace App\Event;
|
|
236
|
+
|
|
237
|
+
use App\Entity\User;
|
|
238
|
+
use Symfony\Contracts\EventDispatcher\Event;
|
|
239
|
+
|
|
240
|
+
final class UserRegisteredEvent extends Event
|
|
241
|
+
{
|
|
242
|
+
public function __construct(
|
|
243
|
+
private readonly User $user,
|
|
244
|
+
private readonly \DateTimeImmutable $occurredAt = new \DateTimeImmutable(),
|
|
245
|
+
) {}
|
|
246
|
+
|
|
247
|
+
public function getUser(): User
|
|
248
|
+
{
|
|
249
|
+
return $this->user;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public function getOccurredAt(): \DateTimeImmutable
|
|
253
|
+
{
|
|
254
|
+
return $this->occurredAt;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Dispatching events
|
|
259
|
+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
|
260
|
+
|
|
261
|
+
final readonly class UserService
|
|
262
|
+
{
|
|
263
|
+
public function __construct(
|
|
264
|
+
private EventDispatcherInterface $eventDispatcher,
|
|
265
|
+
) {}
|
|
266
|
+
|
|
267
|
+
public function registerUser(string $email, string $password): User
|
|
268
|
+
{
|
|
269
|
+
$user = new User($email, $password);
|
|
270
|
+
// ... save user
|
|
271
|
+
|
|
272
|
+
$this->eventDispatcher->dispatch(new UserRegisteredEvent($user));
|
|
273
|
+
|
|
274
|
+
return $user;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Console Commands
|
|
280
|
+
|
|
281
|
+
```php
|
|
282
|
+
<?php
|
|
283
|
+
|
|
284
|
+
declare(strict_types=1);
|
|
285
|
+
|
|
286
|
+
namespace App\Command;
|
|
287
|
+
|
|
288
|
+
use App\Service\UserService;
|
|
289
|
+
use Symfony\Component\Console\Attribute\AsCommand;
|
|
290
|
+
use Symfony\Component\Console\Command\Command;
|
|
291
|
+
use Symfony\Component\Console\Input\InputArgument;
|
|
292
|
+
use Symfony\Component\Console\Input\InputInterface;
|
|
293
|
+
use Symfony\Component\Console\Input\InputOption;
|
|
294
|
+
use Symfony\Component\Console\Output\OutputInterface;
|
|
295
|
+
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
296
|
+
|
|
297
|
+
#[AsCommand(
|
|
298
|
+
name: 'app:user:create',
|
|
299
|
+
description: 'Create a new user',
|
|
300
|
+
)]
|
|
301
|
+
final class CreateUserCommand extends Command
|
|
302
|
+
{
|
|
303
|
+
public function __construct(
|
|
304
|
+
private readonly UserService $userService,
|
|
305
|
+
) {
|
|
306
|
+
parent::__construct();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
protected function configure(): void
|
|
310
|
+
{
|
|
311
|
+
$this
|
|
312
|
+
->addArgument('email', InputArgument::REQUIRED, 'User email')
|
|
313
|
+
->addArgument('password', InputArgument::REQUIRED, 'User password')
|
|
314
|
+
->addOption('admin', 'a', InputOption::VALUE_NONE, 'Make user admin');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
318
|
+
{
|
|
319
|
+
$io = new SymfonyStyle($input, $output);
|
|
320
|
+
|
|
321
|
+
$email = $input->getArgument('email');
|
|
322
|
+
$password = $input->getArgument('password');
|
|
323
|
+
$isAdmin = $input->getOption('admin');
|
|
324
|
+
|
|
325
|
+
$user = $this->userService->createUser($email, $password, $isAdmin);
|
|
326
|
+
|
|
327
|
+
$io->success(sprintf('User created with ID: %d', $user->getId()));
|
|
328
|
+
|
|
329
|
+
return Command::SUCCESS;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Voters (Authorization)
|
|
335
|
+
|
|
336
|
+
```php
|
|
337
|
+
<?php
|
|
338
|
+
|
|
339
|
+
declare(strict_types=1);
|
|
340
|
+
|
|
341
|
+
namespace App\Security\Voter;
|
|
342
|
+
|
|
343
|
+
use App\Entity\Post;
|
|
344
|
+
use App\Entity\User;
|
|
345
|
+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
346
|
+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
|
347
|
+
|
|
348
|
+
final class PostVoter extends Voter
|
|
349
|
+
{
|
|
350
|
+
public const VIEW = 'view';
|
|
351
|
+
public const EDIT = 'edit';
|
|
352
|
+
public const DELETE = 'delete';
|
|
353
|
+
|
|
354
|
+
protected function supports(string $attribute, mixed $subject): bool
|
|
355
|
+
{
|
|
356
|
+
return in_array($attribute, [self::VIEW, self::EDIT, self::DELETE])
|
|
357
|
+
&& $subject instanceof Post;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
protected function voteOnAttribute(
|
|
361
|
+
string $attribute,
|
|
362
|
+
mixed $subject,
|
|
363
|
+
TokenInterface $token
|
|
364
|
+
): bool {
|
|
365
|
+
$user = $token->getUser();
|
|
366
|
+
|
|
367
|
+
if (!$user instanceof User) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** @var Post $post */
|
|
372
|
+
$post = $subject;
|
|
373
|
+
|
|
374
|
+
return match ($attribute) {
|
|
375
|
+
self::VIEW => $this->canView($post, $user),
|
|
376
|
+
self::EDIT => $this->canEdit($post, $user),
|
|
377
|
+
self::DELETE => $this->canDelete($post, $user),
|
|
378
|
+
default => false,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private function canView(Post $post, User $user): bool
|
|
383
|
+
{
|
|
384
|
+
return $post->isPublished() || $this->isOwner($post, $user);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private function canEdit(Post $post, User $user): bool
|
|
388
|
+
{
|
|
389
|
+
return $this->isOwner($post, $user);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private function canDelete(Post $post, User $user): bool
|
|
393
|
+
{
|
|
394
|
+
return $this->isOwner($post, $user) || $user->hasRole('ROLE_ADMIN');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private function isOwner(Post $post, User $user): bool
|
|
398
|
+
{
|
|
399
|
+
return $post->getAuthor()->getId() === $user->getId();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Message Handler (Messenger)
|
|
405
|
+
|
|
406
|
+
```php
|
|
407
|
+
<?php
|
|
408
|
+
|
|
409
|
+
declare(strict_types=1);
|
|
410
|
+
|
|
411
|
+
namespace App\Message;
|
|
412
|
+
|
|
413
|
+
final readonly class SendWelcomeEmail
|
|
414
|
+
{
|
|
415
|
+
public function __construct(
|
|
416
|
+
public int $userId,
|
|
417
|
+
) {}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
namespace App\MessageHandler;
|
|
421
|
+
|
|
422
|
+
use App\Message\SendWelcomeEmail;
|
|
423
|
+
use App\Repository\UserRepositoryInterface;
|
|
424
|
+
use Symfony\Component\Mailer\MailerInterface;
|
|
425
|
+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
426
|
+
|
|
427
|
+
#[AsMessageHandler]
|
|
428
|
+
final readonly class SendWelcomeEmailHandler
|
|
429
|
+
{
|
|
430
|
+
public function __construct(
|
|
431
|
+
private UserRepositoryInterface $userRepository,
|
|
432
|
+
private MailerInterface $mailer,
|
|
433
|
+
) {}
|
|
434
|
+
|
|
435
|
+
public function __invoke(SendWelcomeEmail $message): void
|
|
436
|
+
{
|
|
437
|
+
$user = $this->userRepository->find($message->userId);
|
|
438
|
+
|
|
439
|
+
if (!$user) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Send email logic
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Dispatching messages
|
|
448
|
+
use Symfony\Component\Messenger\MessageBusInterface;
|
|
449
|
+
|
|
450
|
+
$this->messageBus->dispatch(new SendWelcomeEmail($user->getId()));
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Quick Reference
|
|
454
|
+
|
|
455
|
+
| Component | Purpose | File Location |
|
|
456
|
+
|-----------|---------|---------------|
|
|
457
|
+
| Controller | HTTP handlers | `src/Controller/` |
|
|
458
|
+
| Service | Business logic | `src/Service/` |
|
|
459
|
+
| Repository | Data access | `src/Repository/` |
|
|
460
|
+
| Event | Domain events | `src/Event/` |
|
|
461
|
+
| EventSubscriber | Event handlers | `src/EventSubscriber/` |
|
|
462
|
+
| Command | CLI commands | `src/Command/` |
|
|
463
|
+
| Voter | Authorization | `src/Security/Voter/` |
|
|
464
|
+
| Message | Async messages | `src/Message/` |
|
|
465
|
+
| MessageHandler | Message handlers | `src/MessageHandler/` |
|
|
466
|
+
| DTO | Data transfer | `src/DTO/` |
|