PHP code example of andydefer / laravel-repository
1. Go to this page and download the library: Download andydefer/laravel-repository library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
andydefer / laravel-repository example snippets
use AndyDefer\DomainStructures\Abstracts\AbstractRecord;
final class UserRecord extends AbstractRecord
{
public function __construct(
public readonly ?string $name = null,
public readonly ?string $email = null,
public readonly ?UserStatus $status = null,
) {}
}
use AndyDefer\Repository\Records\FindByRecord;
// Tri simple
$findBy = new FindByRecord(
filters: new UserFiltersRecord(status: UserStatus::ACTIVE),
limit: 10,
sortBy: new SortColumns('name:asc'),
columns: new SelectColumns(['id', 'name', 'email']),
);
// Tri multi-colonnes
$findBy = new FindByRecord(
filters: new UserFiltersRecord(status: UserStatus::ACTIVE),
limit: 10,
sortBy: new SortColumns('name:asc|created_at:desc|id:asc'),
columns: new SelectColumns(['id', 'name', 'email']),
);
use AndyDefer\Repository\Records\PaginateRecord;
$paginate = new PaginateRecord(
perPage: 15,
page: 1,
sortBy: new SortColumns('created_at:desc'),
filters: new UserFiltersRecord(status: UserStatus::ACTIVE),
columns: new SelectColumns(['id', 'name', 'email']),
);
// Tri multi-colonnes sur plusieurs champs
$paginate = new PaginateRecord(
perPage: 15,
page: 1,
sortBy: new SortColumns('category_id:asc|price:desc'),
filters: new UserFiltersRecord(status: UserStatus::ACTIVE),
columns: new SelectColumns(['id', 'name', 'email']),
);
use AndyDefer\Repository\ValueObjects\SortColumns;
// Tri simple
$sort = new SortColumns('name:asc');
$sort->toArray(); // ['name' => 'asc']
// Tri multi-colonnes (syntaxe à barre verticale)
$sort = new SortColumns('name:asc|created_at:desc|id:asc');
$sort->toArray(); // ['name' => 'asc', 'created_at' => 'desc', 'id' => 'asc']
// Depuis un tableau associatif
$sort = SortColumns::fromArray(['name' => 'asc', 'created_at' => 'desc']);
// Vérifications
$sort->hasColumn('name'); // true
$sort->getDirection('name'); // 'asc'
$sort->count(); // 3
use AndyDefer\Repository\ValueObjects\SelectColumns;
// Créer avec des colonnes spécifiques
$columns = new SelectColumns(['id', 'name', 'email']);
// Sélectionner toutes les colonnes
$allColumns = SelectColumns::all();
// Ajouter des colonnes (retourne une nouvelle instance)
$extended = $columns->add('created_at', 'updated_at');
// Vérifier si une colonne existe
if ($columns->has('email')) {
// ...
}
// Obtenir le nombre
$count = $columns->count(); // 3
// Convertir en tableau
$array = $columns->toArray(); // ['id', 'name', 'email']
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
final class User extends Model
{
protected $fillable = ['name', 'email', 'status'];
}
namespace App\Records;
use AndyDefer\DomainStructures\Abstracts\AbstractRecord;
final class UserRecord extends AbstractRecord
{
public function __construct(
public readonly ?string $name = null,
public readonly ?string $email = null,
public readonly ?UserStatus $status = null,
) {}
}
namespace App\Records;
use AndyDefer\DomainStructures\Abstracts\AbstractRecord;
final class UserFiltersRecord extends AbstractRecord
{
public function __construct(
public readonly ?string $name = null,
public readonly ?string $email = null,
public readonly ?UserStatus $status = null,
) {}
}
namespace App\Repositories;
use AndyDefer\Repository\AbstractRepository;
use AndyDefer\DomainStructures\Abstracts\AbstractRecord;
use App\Models\User;
use App\Records\UserRecord;
use App\Records\UserFiltersRecord;
use Illuminate\Database\Eloquent\Builder;
final class UserRepository extends AbstractRepository
{
public function __construct()
{
parent::__construct(User::class, UserRecord::class);
}
protected function applyFilters(Builder $query, AbstractRecord $filters): void
{
if (!$filters instanceof UserFiltersRecord) {
return;
}
if ($filters->name !== null) {
$query->where('name', 'like', '%' . $filters->name . '%');
}
if ($filters->email !== null) {
$query->where('email', 'like', '%' . $filters->email . '%');
}
if ($filters->status !== null) {
$query->where('status', $filters->status);
}
}
}
use App\Repositories\UserRepository;
use App\Records\UserRecord;
use App\Records\UserFiltersRecord;
use AndyDefer\Repository\Records\FindByRecord;
use AndyDefer\Repository\Records\PaginateRecord;
use AndyDefer\Repository\ValueObjects\SortColumns;
class UserService
{
public function __construct(
private readonly UserRepository $repository,
) {}
// Créer un utilisateur
public function createUser(string $name, string $email): User
{
return $this->repository->create(new UserRecord(
name: $name,
email: $email,
status: UserStatus::ACTIVE,
));
}
// Trouver un utilisateur
public function findUser(int $id): ?User
{
return $this->repository->find($id);
}
// Mettre à jour un utilisateur (uniquement les champs non-nuls)
public function updateUser(int $id, string $name): User
{
return $this->repository->update($id, new UserRecord(name: $name));
}
// Supprimer un utilisateur
public function deleteUser(int $id): bool
{
return $this->repository->delete($id);
}
// Lister avec filtres et tri multiple
public function listActiveUsersByStatusAndName(): array
{
$filters = new UserFiltersRecord(status: UserStatus::ACTIVE);
$findBy = new FindByRecord(
filters: $filters,
limit: 50,
sortBy: new SortColumns('status:asc|name:asc'),
);
return $this->repository->findBy($findBy)->all();
}
// Paginer les résultats avec tri multiple
public function getPaginatedUsers(int $page = 1): LengthAwarePaginator
{
$paginate = new PaginateRecord(
perPage: 15,
page: $page,
sortBy: new SortColumns('created_at:desc|id:asc'),
);
return $this->repository->paginate($paginate);
}
// Compter les enregistrements
public function countActiveUsers(): int
{
$filters = new UserFiltersRecord(status: UserStatus::ACTIVE);
return $this->repository->count($filters);
}
// Vérifier l'existence
public function userExists(string $email): bool
{
$filters = new UserFiltersRecord(email: $email);
return $this->repository->exists($filters);
}
// Suppression groupée
public function deleteInactiveUsers(): int
{
$filters = new UserFiltersRecord(status: UserStatus::INACTIVE);
return $this->repository->deleteBulk($filters);
}
}
// ✅ BON
final class UserRecord extends AbstractRecord { ... }
final class PostRecord extends AbstractRecord { ... }
// ❌ MAUVAIS
final class UserPostRecord extends AbstractRecord { ... }
// ✅ BON - Pour les filtres complexes
final class UserFiltersRecord extends AbstractRecord { ... }
// ✅ BON - Pour les cas simples, réutiliser le Record principal
$filters = new UserRecord(status: UserStatus::ACTIVE);
// ✅ BON
public function __construct(
public readonly ?string $name = null, // Optionnel
public readonly string $email, // Requis
) {}
// ❌ MAUVAIS
public function __construct(
public readonly ?string $name,
public readonly string $email,
) {}
protected function applyFilters(Builder $query, AbstractRecord $filters): void
{
// Vérification du type si utilisation d'un Record de filtres dédié
if (!$filters instanceof UserFiltersRecord) {
return;
}
// Utiliser when() pour les conditions complexes
$query->when($filters->name ?? null, fn($q, $name) =>
$q->where('name', 'like', '%' . $name . '%')
);
$query->when($filters->status ?? null, fn($q, $status) =>
$q->where('status', $status)
);
}
// ✅ BON - Quand vous avez déjà des données brutes
$data = [
'name' => 'John Doe',
'email' => '[email protected]',
'status' => 'active',
];
$user = $repository->createRaw($data);
// ✅ BON - Pour créer avec des valeurs null explicites
$data = [
'name' => 'User Without Email',
'email' => null,
'status' => 'active',
];
$user = $repository->createRaw($data);
// ✅ BON - Tri simple
$sort = new SortColumns('name:asc');
// ✅ BON - Tri multi-colonnes pour des tris complexes
$sort = new SortColumns('category_id:asc|price:desc|id:asc');
// ✅ BON - Format lisible pour les tris multi-colonnes
$sortString = 'status:asc|created_at:desc|name:asc';
$sort = new SortColumns($sortString);
final class UserRepositoryTest extends IntegrationTestCase
{
private UserRepository $repository;
protected function setUp(): void
{
parent::setUp();
$this->repository = new UserRepository();
}
public function test_create_persiste_utilisateur(): void
{
$record = new UserRecord(name: 'John', email: '[email protected]');
$user = $this->repository->create($record);
$this->assertDatabaseHas('users', [
'id' => $user->id,
'name' => 'John',
'email' => '[email protected]',
]);
}
public function test_create_raw_accepte_null(): void
{
$data = [
'name' => 'User With Null Email',
'email' => null,
'status' => 'active',
];
$user = $this->repository->createRaw($data);
$this->assertDatabaseHas('users', [
'id' => $user->id,
'name' => 'User With Null Email',
'email' => null,
]);
}
public function test_find_by_with_multiple_sort_columns(): void
{
// Créer des utilisateurs
$this->repository->create(new UserRecord(name: 'User A', email: '[email protected]'));
$this->repository->create(new UserRecord(name: 'User A', email: '[email protected]'));
$this->repository->create(new UserRecord(name: 'User B', email: '[email protected]'));
$findBy = new FindByRecord(
sortBy: new SortColumns('name:asc|id:desc'),
);
$results = $this->repository->findBy($findBy);
$this->assertSame('User A', $results[0]->name);
$this->assertSame('User A', $results[1]->name);
$this->assertSame('User B', $results[2]->name);
}
}
final class OrderRepository extends AbstractRepository
{
public function __construct()
{
parent::__construct(Order::class, OrderRecord::class);
}
protected function applyFilters(Builder $query, AbstractRecord $filters): void
{
if (!$filters instanceof OrderFiltersRecord) {
return;
}
// Filtre de plage de dates
if ($filters->fromDate !== null) {
$query->whereDate('created_at', '>=', $filters->fromDate);
}
if ($filters->toDate !== null) {
$query->whereDate('created_at', '<=', $filters->toDate);
}
// Filtre de plage de montants
if ($filters->minAmount !== null) {
$query->where('total', '>=', $filters->minAmount);
}
if ($filters->maxAmount !== null) {
$query->where('total', '<=', $filters->maxAmount);
}
// Filtre de statut
if ($filters->status !== null) {
$query->where('status', $filters->status);
}
// Filtre de recherche textuelle
if ($filters->search !== null) {
$query->where(function ($q) use ($filters) {
$q->where('order_number', 'like', '%' . $filters->search . '%')
->orWhere('customer_name', 'like', '%' . $filters->search . '%');
});
}
}
}
// Utilisation avec tri multi-colonnes
$filters = new OrderFiltersRecord(
fromDate: '2024-01-01',
toDate: '2024-12-31',
minAmount: 100,
status: OrderStatus::PAID,
search: 'ACME',
);
$paginate = new PaginateRecord(
perPage: 20,
page: 1,
sortBy: new SortColumns('status:asc|created_at:desc|total:desc'),
filters: $filters,
columns: new SelectColumns(['id', 'order_number', 'total', 'status', 'created_at']),
);
$orders = $repository->paginate($paginate);
// Modèle avec SoftDelete
final class Product extends Model
{
use SoftDeletes;
protected $fillable = ['name', 'price', 'quantity'];
}
// Utilisation
$product = $repository->create(new ProductRecord(name: 'Laptop', price: 999.99));
// Soft delete
$repository->delete($product->id);
// Le find normal ne le trouve pas
$found = $repository->find($product->id); // null
// findWithTrashed le trouve
$deleted = $repository->findWithTrashed($product->id); // Product instance
// Restauration
$repository->restore($product->id);
// Suppression définitive (hard delete)
$repository->forceDelete($product->id);
// Nettoyage de masse : supprimer définitivement tous les soft deleted
$filters = new ProductFiltersRecord(