Initial commit
This commit is contained in:
218
skills/make-urls/README.md
Normal file
218
skills/make-urls/README.md
Normal 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
|
||||
222
skills/make-urls/SKILL.md
Normal file
222
skills/make-urls/SKILL.md
Normal file
@@ -0,0 +1,222 @@
|
||||
---
|
||||
name: framework:make:urls
|
||||
description: Génère classe Urls + Message CQRS + Handler
|
||||
license: MIT
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Framework Make Urls Skill
|
||||
|
||||
## Description
|
||||
Génère une classe Urls pour la génération d'URLs d'une entité, avec le pattern CQRS (Message + MessageHandler).
|
||||
|
||||
La classe Urls encapsule la logique de génération d'URLs pour une entité, le Message représente la query CQRS, et le Handler orchestre la récupération de l'entité et la création des URLs.
|
||||
|
||||
## Usage
|
||||
```
|
||||
Use skill framework:make:urls
|
||||
|
||||
Vous serez invité à fournir :
|
||||
- Le nom de l'entité (ex: Product, User, Order)
|
||||
```
|
||||
|
||||
## Templates
|
||||
- `Urls/UtilisateurUrls.php` - Template de classe Urls
|
||||
- `MessageHandler/UtilisateurUrlsMessage.php` - Template de message CQRS
|
||||
- `MessageHandler/UtilisateurUrlsMessageHandler.php` - Template de handler
|
||||
|
||||
## Variables requises
|
||||
- **{EntityName}** - Nom de l'entité en PascalCase (ex: Utilisateur, Product)
|
||||
- **{entityName}** - Nom de l'entité en camelCase (ex: utilisateur, product)
|
||||
- **{namespace}** - Namespace du projet (défaut: App)
|
||||
|
||||
## Dépendances
|
||||
- Requiert que l'entité existe dans `src/Entity/{EntityName}.php`
|
||||
- Requiert que le repository existe dans `src/Repository/{EntityName}Repository.php`
|
||||
- Requiert que l'interface repository existe dans `src/Repository/{EntityName}RepositoryInterface.php`
|
||||
|
||||
## Outputs
|
||||
- `src/Urls/{EntityName}Urls.php`
|
||||
- `src/MessageHandler/{EntityName}UrlsMessage.php`
|
||||
- `src/MessageHandler/{EntityName}UrlsMessageHandler.php`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Demander le nom de l'entité (EntityName)
|
||||
2. Vérifier que l'entité existe dans `src/Entity/{EntityName}.php`
|
||||
- Si non : arrêter et demander de créer l'entité d'abord
|
||||
3. Vérifier que le repository existe
|
||||
- Si non : arrêter et demander de créer l'entité avec repository d'abord
|
||||
4. Générer les 3 classes depuis les templates :
|
||||
- Remplacer `{EntityName}` par le nom fourni
|
||||
- Remplacer `{entityName}` par la version camelCase
|
||||
5. Afficher les fichiers créés
|
||||
|
||||
## Patterns appliqués
|
||||
|
||||
### Classe Urls
|
||||
- Classe `final readonly`
|
||||
- Constructeur privé
|
||||
- Factory statique `new()`
|
||||
- Propriétés : UrlGeneratorInterface + entité
|
||||
- Méthodes pour générer URLs spécifiques
|
||||
|
||||
### Message CQRS
|
||||
- Extends AbstractQueryEvent
|
||||
- Implements QueryInterface
|
||||
- Classe `final`
|
||||
- Constructeur privé avec factory `new()`
|
||||
- Propriété publique `id`
|
||||
- Méthode getter `id()`
|
||||
|
||||
### MessageHandler
|
||||
- Classe `final readonly`
|
||||
- Attribut #[AsMessageHandler]
|
||||
- Constructeur avec repository + UrlGeneratorInterface
|
||||
- Méthode `__invoke()` retournant Urls
|
||||
|
||||
## Exemple
|
||||
|
||||
```bash
|
||||
Use skill framework:make:urls
|
||||
|
||||
# Saisies utilisateur :
|
||||
EntityName: Product
|
||||
|
||||
# Résultat :
|
||||
✓ src/Urls/ProductUrls.php
|
||||
✓ src/MessageHandler/ProductUrlsMessage.php
|
||||
✓ src/MessageHandler/ProductUrlsMessageHandler.php
|
||||
```
|
||||
|
||||
Fichiers générés :
|
||||
|
||||
```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,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage dans l'entité
|
||||
|
||||
L'entité doit implémenter la méthode `urls()` :
|
||||
|
||||
```php
|
||||
public function urls(): ProductUrls
|
||||
{
|
||||
/** @var ProductUrls $urls */
|
||||
$urls = ProductUrlsMessage::new(
|
||||
id: $this->id->toRfc4122(),
|
||||
)->query($this->dependencyInjection()->queryBus());
|
||||
|
||||
return $urls;
|
||||
}
|
||||
```
|
||||
|
||||
## Enrichissement avec URLs spécifiques
|
||||
|
||||
```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 delete(): string
|
||||
{
|
||||
return $this->urlGenerator->generate(
|
||||
'product_delete',
|
||||
['id' => $this->product->id()->toRfc4122()],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Pattern CQRS : séparation query (Message) / handler
|
||||
- UrlGeneratorInterface injecté pour génération d'URLs
|
||||
- Repository utilisé pour récupérer l'entité par ID
|
||||
- Classe Urls peut être enrichie avec méthodes spécifiques au besoin
|
||||
- Respecte le principe d'immutabilité (readonly)
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\MessageHandler;
|
||||
|
||||
use Atournayre\Common\AbstractQueryEvent;
|
||||
use Atournayre\Contracts\CommandBus\QueryInterface;
|
||||
|
||||
final class UtilisateurUrlsMessage 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\MessageHandler;
|
||||
|
||||
use App\Repository\UtilisateurRepositoryInterface;
|
||||
use App\Urls\UtilisateurUrls;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
#[AsMessageHandler]
|
||||
final readonly class UtilisateurUrlsMessageHandler
|
||||
{
|
||||
public function __construct(
|
||||
private UtilisateurRepositoryInterface $utilisateurRepository,
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(UtilisateurUrlsMessage $message): UtilisateurUrls
|
||||
{
|
||||
$utilisateur = $this->utilisateurRepository->find($message->id());
|
||||
|
||||
return UtilisateurUrls::new(
|
||||
urlGenerator: $this->urlGenerator,
|
||||
utilisateur: $utilisateur,
|
||||
);
|
||||
}
|
||||
}
|
||||
27
skills/make-urls/templates/Urls/UtilisateurUrls.php
Normal file
27
skills/make-urls/templates/Urls/UtilisateurUrls.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Urls;
|
||||
|
||||
use App\Entity\Utilisateur;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
final readonly class UtilisateurUrls
|
||||
{
|
||||
private function __construct(
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
private Utilisateur $utilisateur,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function new(
|
||||
UrlGeneratorInterface $urlGenerator,
|
||||
Utilisateur $utilisateur,
|
||||
): self {
|
||||
return new self(
|
||||
urlGenerator: $urlGenerator,
|
||||
utilisateur: $utilisateur,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user