Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 17:58:52 +08:00
commit eac17f89fa
40 changed files with 4048 additions and 0 deletions

218
skills/make-urls/README.md Normal file
View File

@@ -0,0 +1,218 @@
# Framework Make Urls
Génère classe Urls + Message CQRS + Handler pour génération d'URLs.
## Vue d'ensemble
Cette skill crée un ensemble de classes respectant le pattern CQRS pour gérer la génération d'URLs d'une entité.
## Caractéristiques
### Classes générées
- **Urls** - Classe finale readonly encapsulant la génération d'URLs
- **UrlsMessage** - Query CQRS pour récupérer les URLs
- **UrlsMessageHandler** - Handler orchestrant récupération entité + génération URLs
## Utilisation
```bash
Use skill framework:make:urls
```
Vous serez invité à fournir le nom de l'entité.
## Exemple d'utilisation
```bash
EntityName: Product
```
Génère 3 fichiers :
```php
// src/Urls/ProductUrls.php
final readonly class ProductUrls
{
private function __construct(
private UrlGeneratorInterface $urlGenerator,
private Product $product,
) {}
public static function new(
UrlGeneratorInterface $urlGenerator,
Product $product,
): self {
return new self(
urlGenerator: $urlGenerator,
product: $product,
);
}
}
// src/MessageHandler/ProductUrlsMessage.php
final class ProductUrlsMessage extends AbstractQueryEvent implements QueryInterface
{
private function __construct(
public string $id,
) {}
public static function new(string $id): self
{
return new self(id: $id);
}
public function id(): string
{
return $this->id;
}
}
// src/MessageHandler/ProductUrlsMessageHandler.php
#[AsMessageHandler]
final readonly class ProductUrlsMessageHandler
{
public function __construct(
private ProductRepositoryInterface $productRepository,
private UrlGeneratorInterface $urlGenerator,
) {}
public function __invoke(ProductUrlsMessage $message): ProductUrls
{
$product = $this->productRepository->find($message->id());
return ProductUrls::new(
urlGenerator: $this->urlGenerator,
product: $product,
);
}
}
```
## Structure créée
```
src/
├── Urls/
│ └── {EntityName}Urls.php
└── MessageHandler/
├── {EntityName}UrlsMessage.php
└── {EntityName}UrlsMessageHandler.php
```
## Prérequis
- L'entité doit exister dans `src/Entity/{EntityName}.php`
- Le repository doit exister dans `src/Repository/{EntityName}Repository.php`
- L'interface repository doit exister
- Symfony Messenger configuré
- Atournayre packages installés (AbstractQueryEvent, QueryInterface)
## Usage recommandé
### Dans l'entité
```php
final class Product implements HasUrlsInterface
{
public function urls(): ProductUrls
{
/** @var ProductUrls $urls */
$urls = ProductUrlsMessage::new(
id: $this->id->toRfc4122(),
)->query($this->dependencyInjection()->queryBus());
return $urls;
}
}
```
### Ajout de méthodes d'URLs
```php
final readonly class ProductUrls
{
private function __construct(
private UrlGeneratorInterface $urlGenerator,
private Product $product,
) {}
public static function new(
UrlGeneratorInterface $urlGenerator,
Product $product,
): self {
return new self(
urlGenerator: $urlGenerator,
product: $product,
);
}
public function show(): string
{
return $this->urlGenerator->generate(
'product_show',
['id' => $this->product->id()->toRfc4122()],
UrlGeneratorInterface::ABSOLUTE_URL
);
}
public function edit(): string
{
return $this->urlGenerator->generate(
'product_edit',
['id' => $this->product->id()->toRfc4122()],
UrlGeneratorInterface::ABSOLUTE_URL
);
}
public function api(): string
{
return $this->urlGenerator->generate(
'api_product_get',
['id' => $this->product->id()->toRfc4122()],
UrlGeneratorInterface::ABSOLUTE_URL
);
}
}
```
### Utilisation dans templates Twig
```twig
{# product/show.html.twig #}
<a href="{{ product.urls.edit }}">Modifier</a>
<a href="{{ product.urls.delete }}">Supprimer</a>
{# API link #}
<code>{{ product.urls.api }}</code>
```
### Utilisation dans contrôleurs
```php
public function show(Product $product): Response
{
return $this->render('product/show.html.twig', [
'product' => $product,
'editUrl' => $product->urls()->edit(),
]);
}
```
## Architecture CQRS
### Flow
1. Entité appelle `ProductUrlsMessage::new(id)`
2. Message envoyé au QueryBus
3. Handler intercepte le message
4. Handler récupère l'entité via repository
5. Handler crée et retourne ProductUrls
6. URLs disponibles dans l'entité
### Avantages
- Séparation des responsabilités
- Testabilité
- Injection de dépendances propre
- Pas de service locator dans l'entité
## Principes Elegant Objects appliqués
- Classes finales
- Constructeurs privés
- Factory statiques
- Immutabilité (readonly)
- Encapsulation de la logique d'URLs
- Pas de getters bruts