Initial commit
This commit is contained in:
237
skills/make-collection/README.md
Normal file
237
skills/make-collection/README.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# Framework Make Collection
|
||||
|
||||
Génère une classe Collection typée avec traits Atournayre.
|
||||
|
||||
## Vue d'ensemble
|
||||
Cette skill crée une classe Collection type-safe pour gérer des ensembles d'entités selon les principes Elegant Objects.
|
||||
|
||||
## Caractéristiques
|
||||
|
||||
### Classe Collection générée
|
||||
- Classe `final`
|
||||
- Type-safe (collection d'objets typés)
|
||||
- Interfaces Atournayre complètes
|
||||
- Traits pour fonctionnalités de base
|
||||
- Factory statique `asList()`
|
||||
- Méthode `toLog()` pour logging
|
||||
|
||||
## Utilisation
|
||||
|
||||
```bash
|
||||
Use skill framework:make:collection
|
||||
```
|
||||
|
||||
Vous serez invité à fournir le nom de l'entité.
|
||||
|
||||
## Exemple d'utilisation
|
||||
|
||||
```bash
|
||||
EntityName: Product
|
||||
```
|
||||
|
||||
Génère :
|
||||
```php
|
||||
// src/Collection/ProductCollection.php
|
||||
final class ProductCollection implements AsListInterface, ToArrayInterface, CountInterface, ...
|
||||
{
|
||||
use Collection;
|
||||
use Collection\ToArray;
|
||||
use Collection\Countable;
|
||||
|
||||
public static function asList(array $collection): self
|
||||
{
|
||||
return new self(PrimitiveCollection::of($collection));
|
||||
}
|
||||
|
||||
public function toLog(): array
|
||||
{
|
||||
return [
|
||||
'count' => $this->count()->value(),
|
||||
'items' => $this->collection->map(fn (Product $item) => $item->toLog()),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Structure créée
|
||||
|
||||
```
|
||||
src/
|
||||
└── Collection/
|
||||
└── {EntityName}Collection.php
|
||||
```
|
||||
|
||||
## Prérequis
|
||||
- L'entité doit exister dans `src/Entity/{EntityName}.php`
|
||||
- Framework `atournayre/framework` installé
|
||||
|
||||
## Interfaces implémentées
|
||||
|
||||
- **AsListInterface** - Factory `asList()`
|
||||
- **ToArrayInterface** - Conversion en array
|
||||
- **CountInterface** - Comptage d'éléments
|
||||
- **CountByInterface** - Comptage conditionnel
|
||||
- **AtLeastOneElementInterface** - Vérification présence
|
||||
- **HasSeveralElementsInterface** - Vérification multiple
|
||||
- **HasNoElementInterface** - Vérification vide
|
||||
- **HasOneElementInterface** - Vérification unique
|
||||
- **HasXElementsInterface** - Vérification nombre exact
|
||||
- **LoggableInterface** - Support logging
|
||||
|
||||
## Méthodes disponibles via traits
|
||||
|
||||
```php
|
||||
// Création
|
||||
$products = ProductCollection::asList([$product1, $product2]);
|
||||
|
||||
// Comptage
|
||||
$products->count(); // Number
|
||||
$products->hasNoElement(); // bool
|
||||
$products->hasOneElement(); // bool
|
||||
$products->hasSeveralElements(); // bool
|
||||
$products->hasAtLeastOneElement(); // bool
|
||||
$products->hasXElements(5); // bool
|
||||
|
||||
// Comptage conditionnel
|
||||
$activeCount = $products->countBy(fn (Product $p) => $p->isActive());
|
||||
|
||||
// Conversion
|
||||
$array = $products->toArray();
|
||||
|
||||
// Logging
|
||||
$log = $products->toLog();
|
||||
```
|
||||
|
||||
## Enrichissement (principe YAGNI)
|
||||
|
||||
**IMPORTANT** : N'ajouter que les méthodes **explicitement demandées**.
|
||||
|
||||
### Exemple : filtrage
|
||||
```php
|
||||
public function active(): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->filter(fn (Product $p) => $p->isActive())
|
||||
);
|
||||
}
|
||||
|
||||
public function inStock(): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->filter(fn (Product $p) => $p->stock() > 0)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Exemple : calculs
|
||||
```php
|
||||
public function totalPrice(): float
|
||||
{
|
||||
return $this->collection
|
||||
->map(fn (Product $p) => $p->price())
|
||||
->reduce(fn (float $sum, float $price) => $sum + $price, 0.0);
|
||||
}
|
||||
|
||||
public function averagePrice(): float
|
||||
{
|
||||
if ($this->hasNoElement()) {
|
||||
return 0.0;
|
||||
}
|
||||
return $this->totalPrice() / $this->count()->value();
|
||||
}
|
||||
```
|
||||
|
||||
### Exemple : tri
|
||||
```php
|
||||
public function sortedByName(): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->sort(fn (Product $a, Product $b) =>
|
||||
$a->name() <=> $b->name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function sortedByPriceDesc(): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->sort(fn (Product $a, Product $b) =>
|
||||
$b->price() <=> $a->price()
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Exemple : recherche
|
||||
```php
|
||||
public function findById(Uuid $id): ?Product
|
||||
{
|
||||
return $this->collection
|
||||
->filter(fn (Product $p) => $p->id()->equals($id))
|
||||
->first();
|
||||
}
|
||||
|
||||
public function findByName(string $name): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->filter(fn (Product $p) => $p->name() === $name)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage dans le code
|
||||
|
||||
### Depuis un repository
|
||||
```php
|
||||
final class ProductRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function findAllAsCollection(): ProductCollection
|
||||
{
|
||||
return ProductCollection::asList($this->findAll());
|
||||
}
|
||||
|
||||
public function findActiveAsCollection(): ProductCollection
|
||||
{
|
||||
return ProductCollection::asList(
|
||||
$this->createQueryBuilder('p')
|
||||
->where('p.isActive = true')
|
||||
->getQuery()
|
||||
->getResult()
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dans un service
|
||||
```php
|
||||
final readonly class ProductService
|
||||
{
|
||||
public function calculateTotalStock(ProductCollection $products): int
|
||||
{
|
||||
return $products->collection
|
||||
->map(fn (Product $p) => $p->stock())
|
||||
->reduce(fn (int $sum, int $stock) => $sum + $stock, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dans un contrôleur
|
||||
```php
|
||||
public function index(ProductRepository $repository): Response
|
||||
{
|
||||
$products = $repository->findAllAsCollection();
|
||||
|
||||
return $this->render('product/index.html.twig', [
|
||||
'products' => $products->active()->sortedByName(),
|
||||
'total' => $products->count()->value(),
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
## Principes Elegant Objects appliqués
|
||||
- Classe finale
|
||||
- Factory statique
|
||||
- Type-safety
|
||||
- Immutabilité (nouvelles instances pour transformations)
|
||||
- Pas de méthodes génériques anticipées (YAGNI)
|
||||
- LoggableInterface pour observabilité
|
||||
192
skills/make-collection/SKILL.md
Normal file
192
skills/make-collection/SKILL.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
name: framework:make:collection
|
||||
description: Génère classe Collection typée avec traits Atournayre
|
||||
license: MIT
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Framework Make Collection Skill
|
||||
|
||||
## Description
|
||||
Génère une classe Collection typée pour gérer des ensembles d'entités avec les traits et interfaces Atournayre.
|
||||
|
||||
La Collection offre des méthodes pour manipuler des ensembles d'objets de manière type-safe et respectant les principes Elegant Objects.
|
||||
|
||||
## Usage
|
||||
```
|
||||
Use skill framework:make:collection
|
||||
|
||||
Vous serez invité à fournir :
|
||||
- Le nom de l'entité (ex: Product, User, Order)
|
||||
```
|
||||
|
||||
## Templates
|
||||
- `Collection/UtilisateurCollection.php` - Template de classe Collection
|
||||
|
||||
## 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 framework `atournayre/framework`
|
||||
|
||||
## Outputs
|
||||
- `src/Collection/{EntityName}Collection.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. Générer la classe Collection depuis le template :
|
||||
- Remplacer `{EntityName}` par le nom fourni
|
||||
- Remplacer `{entityName}` par la version camelCase
|
||||
4. Afficher le fichier créé
|
||||
|
||||
## Patterns appliqués
|
||||
|
||||
### Classe Collection
|
||||
- Classe `final`
|
||||
- Implémente interfaces Atournayre :
|
||||
- AsListInterface
|
||||
- ToArrayInterface
|
||||
- CountInterface
|
||||
- CountByInterface
|
||||
- AtLeastOneElementInterface
|
||||
- HasSeveralElementsInterface
|
||||
- HasNoElementInterface
|
||||
- HasOneElementInterface
|
||||
- HasXElementsInterface
|
||||
- LoggableInterface
|
||||
- Utilise traits Atournayre :
|
||||
- Collection
|
||||
- Collection\ToArray
|
||||
- Collection\Countable
|
||||
- Méthode statique `asList(array $collection)`
|
||||
- Méthode `toLog()` pour logging
|
||||
|
||||
## Exemple
|
||||
|
||||
```bash
|
||||
Use skill framework:make:collection
|
||||
|
||||
# Saisies utilisateur :
|
||||
EntityName: Product
|
||||
|
||||
# Résultat :
|
||||
✓ src/Collection/ProductCollection.php
|
||||
```
|
||||
|
||||
Fichier généré :
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Collection;
|
||||
|
||||
use App\Entity\Product;
|
||||
use Atournayre\Contracts\Collection\AsListInterface;
|
||||
use Atournayre\Contracts\Collection\AtLeastOneElementInterface;
|
||||
use Atournayre\Contracts\Collection\CountByInterface;
|
||||
use Atournayre\Contracts\Collection\CountInterface;
|
||||
use Atournayre\Contracts\Collection\HasNoElementInterface;
|
||||
use Atournayre\Contracts\Collection\HasOneElementInterface;
|
||||
use Atournayre\Contracts\Collection\HasSeveralElementsInterface;
|
||||
use Atournayre\Contracts\Collection\HasXElementsInterface;
|
||||
use Atournayre\Contracts\Collection\ToArrayInterface;
|
||||
use Atournayre\Contracts\Log\LoggableInterface;
|
||||
use Atournayre\Primitives\Collection as PrimitiveCollection;
|
||||
use Atournayre\Primitives\Traits\Collection;
|
||||
|
||||
final class ProductCollection implements AsListInterface, ToArrayInterface, CountInterface, CountByInterface, AtLeastOneElementInterface, HasSeveralElementsInterface, HasNoElementInterface, HasOneElementInterface, HasXElementsInterface, LoggableInterface
|
||||
{
|
||||
use Collection;
|
||||
use Collection\ToArray;
|
||||
use Collection\Countable;
|
||||
|
||||
public static function asList(array $collection): self
|
||||
{
|
||||
return new self(PrimitiveCollection::of($collection));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toLog(): array
|
||||
{
|
||||
return [
|
||||
'count' => $this->count()->value(),
|
||||
'items' => $this->collection->map(fn (Product $item) => $item->toLog()),
|
||||
];
|
||||
}
|
||||
|
||||
// UNIQUEMENT les méthodes EXPLICITEMENT demandées par l'utilisateur
|
||||
// PAS d'anticipation de besoins futurs
|
||||
// PAS de méthodes génériques (add, remove, filter, map, etc.)
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Création d'une collection
|
||||
|
||||
```php
|
||||
$products = ProductCollection::asList([
|
||||
$product1,
|
||||
$product2,
|
||||
$product3,
|
||||
]);
|
||||
```
|
||||
|
||||
### Méthodes disponibles (via traits)
|
||||
|
||||
```php
|
||||
// Comptage
|
||||
$count = $products->count(); // Atournayre\Primitives\Number
|
||||
$hasElements = $products->hasNoElement(); // bool
|
||||
$hasOne = $products->hasOneElement(); // bool
|
||||
$hasSeveral = $products->hasSeveralElements(); // bool
|
||||
$hasAtLeastOne = $products->hasAtLeastOneElement(); // bool
|
||||
$hasX = $products->hasXElements(5); // bool
|
||||
|
||||
// Conversion
|
||||
$array = $products->toArray(); // array
|
||||
|
||||
// Comptage personnalisé
|
||||
$activeCount = $products->countBy(fn (Product $p) => $p->isActive());
|
||||
```
|
||||
|
||||
### Ajout de méthodes métier (YAGNI)
|
||||
|
||||
N'ajouter que les méthodes **explicitement demandées** :
|
||||
|
||||
```php
|
||||
final class ProductCollection implements ...
|
||||
{
|
||||
// ... traits ...
|
||||
|
||||
public function active(): self
|
||||
{
|
||||
return new self(
|
||||
$this->collection->filter(fn (Product $p) => $p->isActive())
|
||||
);
|
||||
}
|
||||
|
||||
public function totalPrice(): float
|
||||
{
|
||||
return $this->collection
|
||||
->map(fn (Product $p) => $p->price())
|
||||
->reduce(fn (float $sum, float $price) => $sum + $price, 0.0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Respect du principe YAGNI : pas de méthodes génériques anticipées
|
||||
- Seules les méthodes explicitement demandées doivent être ajoutées
|
||||
- Les traits fournissent déjà beaucoup de fonctionnalités
|
||||
- La collection est type-safe (typage sur l'entité)
|
||||
- LoggableInterface permet le logging automatique
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Collection;
|
||||
|
||||
use App\Entity\Utilisateur;
|
||||
use Atournayre\Contracts\Collection\AsListInterface;
|
||||
use Atournayre\Contracts\Collection\AtLeastOneElementInterface;
|
||||
use Atournayre\Contracts\Collection\CountByInterface;
|
||||
use Atournayre\Contracts\Collection\CountInterface;
|
||||
use Atournayre\Contracts\Collection\HasNoElementInterface;
|
||||
use Atournayre\Contracts\Collection\HasOneElementInterface;
|
||||
use Atournayre\Contracts\Collection\HasSeveralElementsInterface;
|
||||
use Atournayre\Contracts\Collection\HasXElementsInterface;
|
||||
use Atournayre\Contracts\Collection\ToArrayInterface;
|
||||
use Atournayre\Contracts\Log\LoggableInterface;
|
||||
use Atournayre\Primitives\Collection as PrimitiveCollection;
|
||||
use Atournayre\Primitives\Traits\Collection;
|
||||
|
||||
final class UtilisateurCollection implements AsListInterface, ToArrayInterface, CountInterface, CountByInterface, AtLeastOneElementInterface, HasSeveralElementsInterface, HasNoElementInterface, HasOneElementInterface, HasXElementsInterface, LoggableInterface
|
||||
{
|
||||
use Collection;
|
||||
use Collection\ToArray;
|
||||
use Collection\Countable;
|
||||
|
||||
public static function asList(array $collection): self
|
||||
{
|
||||
return new self(PrimitiveCollection::of($collection));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toLog(): array
|
||||
{
|
||||
return [
|
||||
'count' => $this->count()->value(),
|
||||
'items' => $this->collection->map(fn (Utilisateur $item) => $item->toLog()),
|
||||
];
|
||||
}
|
||||
|
||||
// UNIQUEMENT les méthodes EXPLICITEMENT demandées par l'utilisateur
|
||||
// PAS d'anticipation de besoins futurs
|
||||
// PAS de méthodes génériques (add, remove, filter, map, etc.)
|
||||
}
|
||||
Reference in New Issue
Block a user