1. Go to this page and download the library: Download andydefer/php-records 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 / php-records example snippets
// ❌ On ne sait pas ce qu'il y a dans ce tableau
function updateUser(array $data): void
{
// $data['name'] ? $data['email'] ? $data['role'] ?
// Personne ne le sait vraiment.
}
// ✅ On sait exactement ce qu'on reçoit
function updateUser(UserRecord $user): void
{
// $user->name, $user->email, $user->role
// Le compilateur guide le développeur.
}
use AndyDefer\Records\AbstractRecord;
final class UserRecord extends AbstractRecord
{
public function __construct(
public readonly string $name,
public readonly string $email,
public readonly UserRole $role,
public readonly TypedCollection $tags = new TypedCollection('string'),
) {}
}
final class GoodRecord extends AbstractRecord
{
public function __construct(
public readonly TypedCollection $items, // ✅ TypedCollection<ItemRecord>
public readonly int $userId, // ✅ int
public readonly string $createdAt, // ✅ string ISO
) {}
}
use AndyDefer\Records\EmptyRecord;
final class FindByRecord extends AbstractRecord
{
public function __construct(
public readonly Recordable $filters = new EmptyRecord(),
public readonly ?int $limit = 100,
) {}
}
// Utilisation - pas de condition ternaire !
$filtersArray = $record->filters->toArray(); // [] si EmptyRecord
$record = new UserRecord(
name: 'John Doe',
email: '[email protected]',
role: UserRole::ADMIN,
createdAt: '2024-01-15T14:30:00Z',
);
// Insertion en base (conserve les null)
DB::table('users')->insert($record->toArray());
// Update (exclut les champs null)
DB::table('users')->where('id', 1)->update($record->toDatabase());
// Envoi à une API externe
Http::post('https://api.external.com/users', $record->toJson());
// Propriété en camelCase dans le Record
public readonly string $emailVerifiedAt;
// Devient 'email_verified_at' dans le tableau
$record->toArray(); // ['email_verified_at' => '2024-01-15T14:30:00Z']
use AndyDefer\Records\Collections\TypedCollection;
// ✅ Collection de strings
$tags = new TypedCollection('string');
$tags->add('developer', 'laravel', 'php');
// Collection acceptant plusieurs types scalaires
$mixed = new TypedCollection('int', 'float', 'string');
$mixed->add(42, 3.14, 'text');
// Collection acceptant Records et scalaires
$items = new TypedCollection(ProductRecord::class, 'string');
$items->add(new ProductRecord(name: 'Laptop'), 'Description');
final class DashboardDataRecord extends AbstractRecord
{
public function __construct(
public readonly UserRecord $currentUser,
public readonly TypedCollection $recentOrders, // TypedCollection<OrderRecord>
public readonly TypedCollection $tags, // TypedCollection<string>
) {}
}
// Collection de strings
$tags = new TypedCollection('string');
$tags->add('developer', 'laravel', 'php');
// Collection d'entiers
$ids = new TypedCollection('int');
$ids->add(1, 2, 3, 4, 5);
// Collection de Records
$products = new TypedCollection(ProductRecord::class);
$products->add(new ProductRecord(name: 'Laptop', price: 999));
// Collection de collections (imbriquée)
$nested = new TypedCollection(TypedCollection::class);
$nested->add($tags, $ids);
use AndyDefer\Records\Collections\Utility\StringTypedCollection;
$strings = new StringTypedCollection();
$strings->add(' HELLO ', 'world', 'PHP', '', ' test ');
use AndyDefer\Records\Collections\Utility\AbstractNumberTypedCollection;
// Méthodes disponibles dans IntTypedCollection, FloatTypedCollection et NumberTypedCollection
use AndyDefer\Records\Collections\TypedCollection;
use App\Records\ProductRecord;
final class ProductCollection extends TypedCollection
{
public function __construct()
{
parent::__construct(ProductRecord::class);
}
public function getTotalPrice(): float
{
return $this->sum(fn($product) => $product->price);
}
public function getInStock(): self
{
return $this->filter(fn($product) => $product->stock > 0);
}
public function filterByCategory(string $category): self
{
return $this->filter(fn($product) => $product->category === $category);
}
public function getFeatured(): self
{
return $this->filter(fn($product) => $product->isFeatured === true);
}
}
// Utilisation
$products = new ProductCollection();
$products->add(
new ProductRecord(name: 'Laptop', price: 999, stock: 5, category: 'electronics', isFeatured: true),
new ProductRecord(name: 'Mouse', price: 29, stock: 0, category: 'electronics', isFeatured: false),
new ProductRecord(name: 'Book', price: 19, stock: 10, category: 'books', isFeatured: true),
);
$totalValue = $products->getTotalPrice(); // 1047
$availableProducts = $products->getInStock(); // Laptop et Book
$electronics = $products->filterByCategory('electronics'); // Laptop et Mouse
$featured = $products->getFeatured(); // Laptop et Book
namespace AndyDefer\Records;
interface Recordable
{
public function toArray(): array;
public function toDatabase(): array;
public function toJson(): string;
}
// Accepter n'importe quel Record
function processRecord(Recordable $record): void
{
$data = $record->toArray();
// ...
}
// Ou spécifiquement un EmptyRecord pour les options
function findUsers(Recordable $filters = new EmptyRecord(): array
{
$filtersArray = $filters->toArray(); // [] si EmptyRecord
// ...
}
use AndyDefer\Records\Traits\Enumable;
enum UserRole: string
{
use Enumable;
case ADMIN = 'admin';
case USER = 'user';
case GUEST = 'guest';
}
enum TestUserStatus
{
use Enumable;
case ACTIVE;
case INACTIVE;
case SUSPENDED;
}
// Pure enum (non-backed)
TestUserStatus::values(); // ['ACTIVE', 'INACTIVE', 'SUSPENDED']
TestUserStatus::names(); // ['ACTIVE', 'INACTIVE', 'SUSPENDED']
TestUserStatus::isValid('ACTIVE'); // true
TestUserStatus::fromValue('ACTIVE'); // TestUserStatus::ACTIVE
// ✅ BON - Type explicite
public readonly TypedCollection $tags = new TypedCollection('string');
// ✅ BON
public readonly Recordable $filters = new EmptyRecord();
// ✅ Recommandé
enum UserRole: string
{
case ADMIN = 'admin';
}
// ⚠️ Acceptable mais moins pratique
enum UserStatus
{
case ACTIVE;
}
final class UserRecord extends AbstractRecord
{
// ✅ BON - Que des données
public function __construct(
public readonly string $name,
public readonly string $email,
) {}
}
// Conversion avant le Record
$tags = new TypedCollection('string');
foreach ($user->tags as $tag) {
$tags->add($tag);
}
return new UserRecord(
name: $user->name,
tags: $tags, // Déjà en TypedCollection
createdAt: $user->created_at->toISOString(), // Déjà en string
);
// ✅ BON
public readonly TypedCollection $tags = new TypedCollection('string');
// ✅ BON - Collection de Records
public readonly TypedCollection $items = new TypedCollection(ItemRecord::class);
public readonly UserRecord $user; // Un utilisateur
public readonly TypedCollection $users; // Plusieurs utilisateurs
// Vérifier que tous les produits sont en stock
if ($products->every(fn($p) => $p->stock > 0)) {
// Tous disponibles
}
// Vérifier qu'au moins un produit est en promotion
if ($products->some(fn($p) => $p->isOnSale)) {
// Appliquer réduction
}
use AndyDefer\Records\AbstractRecord;
final class UserCredentialsRecord extends AbstractRecord
{
public function __construct(
public readonly string $email,
public readonly string $password,
public readonly bool $rememberMe,
) {}
}
// Utilisation
$credentials = new UserCredentialsRecord(
email: '[email protected]',
password: 'secret',
rememberMe: true,
);
// Insertion
DB::table('login_attempts')->insert($credentials->toArray());
use AndyDefer\Records\AbstractRecord;
use AndyDefer\Records\Collections\TypedCollection;
use App\Enums\UserRole;
final class UserListFilterRecord extends AbstractRecord
{
public function __construct(
public readonly ?UserRole $role = null,
public readonly ?bool $isActive = null,
public readonly ?string $search = null,
public readonly TypedCollection $excludedIds = new TypedCollection('int'),
public readonly ?DashboardFilterRecord $dashboardFilters = null,
) {}
}
// Utilisation
$filters = new UserListFilterRecord(
role: UserRole::ADMIN,
isActive: true,
excludedIds: (new TypedCollection('int'))->add(1, 2, 3),
);
final class UserService
{
public function updateUserField(UserCredentialsRecord $credentials): UserUpdateResultRecord
{
// On sait exactement ce qu'on reçoit
$user = User::where('email', $credentials->email)->first();
// Traitement...
return new UserUpdateResultRecord(
success: true,
userId: $user->id,
);
}
}
final class UserRepository
{
public function create(UserRecord $record): User
{
$id = DB::table('users')->insertGetId($record->toArray());
return User::find($id);
}
public function update(int $id, UserRecord $record): User
{
DB::table('users')->where('id', $id)->update($record->toDatabase());
return User::find($id);
}
}
final class PaymentGatewayService
{
public function createPayment(PaymentRequestRecord $request): PaymentResponseRecord
{
$response = Http::post(
'https://api.payment.com/v1/payments',
$request->toJson()
);
return new PaymentResponseRecord(
transactionId: $response->json('transaction_id'),
status: $response->json('status'),
);
}
}
final class DashboardContextRecord extends AbstractRecord
{
public function __construct(
public readonly UserContextRecord $user,
public readonly DashboardFilterRecord $filters,
public readonly string $timezone,
) {}
}
// Utilisation
$context = new DashboardContextRecord(
user: new UserContextRecord(id: 1, name: 'John'),
filters: new DashboardFilterRecord(dateRange: 'last-30-days'),
timezone: 'UTC',
);
final class OrderService
{
public function calculateTotal(OrderRecord $order): float
{
return $order->items->sum(fn($item) => $item->price * $item->quantity);
}
public function getExpensiveItems(OrderRecord $order, float $threshold): TypedCollection
{
return $order->items->filter(fn($item) => $item->price > $threshold);
}
public function getProductNames(OrderRecord $order): TypedCollection
{
return $order->items->map(fn($item) => $item->productName);
}
public function validateOrder(OrderRecord $order): bool
{
return $order->items->every(fn($item) => $item->quantity > 0)
&& $order->items->some(fn($item) => $item->price > 0);
}
}
final class ContentService
{
public function processContent(StringTypedCollection $strings): array
{
return $strings
->trim()
->filterEmpty()
->toLowercase()
->uniqueCaseInsensitive()
->slugify()
->wrap('**')
->join("\n");
}
public function extractEmails(StringTypedCollection $content): StringTypedCollection
{
return $content->matchingRegex('/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/');
}
}
// Utilisation
$content = new StringTypedCollection();
$content->add(' Hello World! ', '', ' PHP 8 ', 'Contact: [email protected]', 'HELLO WORLD');
$processed = $contentService->processContent($content);
// Retourne: "**hello-world**\n**php-8**\n**contact-john-example-com**"
$emails = $contentService->extractEmails($content);
// Retourne: ['[email protected]']
bash
composer
Record → Remplace les tableaux bruts par des structures typées et immutables
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.