nilsframework/nils-orm

ORM léger pour NilsFramework — CRUD complet, relations, validation, eager loading et pagination.

Maintainers

Package info

gitlab.com/groupe11512549/nils-orm

Issues

pkg:composer/nilsframework/nils-orm

Statistics

Installs: 10

Dependents: 4

Suggesters: 0

Stars: 0

1.0.0 2026-04-19 16:26 UTC

This package is not auto-updated.

Last update: 2026-04-20 09:06:46 UTC


README

ORM léger pour NilsFramework — CRUD complet, relations, validation, eager loading et pagination.

📋 Table des matières

Installation

composer require nilsframework/nils-orm

nilsframework/nils-database est installé automatiquement comme dépendance. Si vous utilisez déjà nilsframework/nils et nilsframework/nils-database dans votre projet, nils-orm s'y intègre sans conflit.

Avec le framework complet

# Installation complète recommandée
composer require nilsframework/nils
composer require nilsframework/nils-database
composer require nilsframework/nils-orm

Standalone

# nils-orm seul — nils-database est tiré automatiquement
composer require nilsframework/nils-orm

Créer un modèle

use core\Modele;

class Groupe extends Modele
{
    protected static string $table      = 'tnt_groupes';
    protected static string $primaryKey = 'id';

    protected static array $guarded  = ['created_at', 'updated_at'];
    protected static array $hidden   = [];
    protected static array $visible  = [];

    protected static array $schema = [
        'nom' => [
            'type'     => 'string',
            'required' => true,
            'min'      => 2,
            'max'      => 100,
            'messages' => [
                'required' => "Le nom du groupe est requis.",
            ],
        ],
        'montant_cotisation' => [
            'type'     => 'float',
            'required' => true,
            'min'      => 1,
        ],
        'frequence_jours' => [
            'type'     => 'int',
            'required' => true,
        ],
        'date_debut' => [
            'type'     => 'date',
            'required' => true,
        ],
        'description' => [
            'type'     => 'string',
            'required' => false,
        ],
    ];

    public function participations(): array
    {
        return $this->hasMany(Participation::class, 'groupe_id');
    }

    public function cycles(): array
    {
        return $this->hasMany(Cycle::class, 'groupe_id', orderBy: 'numero ASC');
    }

    public function admin(): mixed
    {
        return $this->belongsTo(Admin::class, 'admin_id');
    }
}

Les champs présents en base de données mais absents du $schema sont acceptés sans erreur.

Schema de validation

Chaque champ du schema accepte les règles suivantes :

RègleTypeDescription
typestringstring, int, float, bool, date, datetime, enum
requiredboolChamp obligatoire
minint/floatLongueur min (string) ou valeur min (numérique)
maxint/floatLongueur max (string) ou valeur max (numérique)
emailboolValidation email
uniqueboolUnicité en base de données
hashboolHash automatique bcrypt
defaultmixedValeur par défaut si absent
valuesarrayValeurs autorisées pour enum
messagesarrayMessages d'erreur personnalisés
protected static array $schema = [
    'email' => [
        'type'     => 'string',
        'required' => true,
        'email'    => true,
        'unique'   => true,
        'messages' => [
            'required' => "L'email est requis.",
            'invalid'  => "L'email est invalide.",
            'unique'   => "Cet email est déjà utilisé.",
        ],
    ],
    'password' => [
        'type'     => 'string',
        'required' => true,
        'min'      => 8,
        'hash'     => true,
    ],
    'role' => [
        'type'   => 'enum',
        'values' => ['admin', 'membre', 'moderateur'],
    ],
    'actif' => [
        'type'    => 'bool',
        'default' => true,
    ],
    'date_naissance' => [
        'type' => 'date', // Y-m-d
    ],
    'created_at' => [
        'type' => 'datetime', // Y-m-d H:i:s
    ],
];

Visibilité des champs

$hidden — masquer des champs dans toArray()

protected static array $hidden = ['password', 'remember_token'];

$visible — n'afficher que ces champs dans toArray()

protected static array $visible = ['id', 'nom', 'email'];
// Si $visible est défini → seuls ces champs apparaissent (prioritaire sur $hidden)

$guarded — champs jamais envoyés en INSERT/UPDATE

protected static array $guarded = ['created_at', 'updated_at'];
// La clé primaire est toujours exclue automatiquement

$fillable — champs autorisés en écriture

protected static array $fillable = ['nom', 'email', 'password'];
// Si vide → tous les champs sauf $guarded et la PK sont autorisés

CRUD

get()

Récupère un élément par ID ou une liste filtrée.

// Par ID — retourne l'objet ou lève NotFoundException
$groupe = Groupe::get(1);

// Tous les enregistrements
$groupes = Groupe::get();

// Avec ORDER BY
$groupes = Groupe::get(null, 'created_at DESC');

// Avec LIMIT
$groupes = Groupe::get(null, 'created_at DESC', 5);

// Avec filtres
$groupes = Groupe::get(['admin_id' => 3]);

post()

Crée un nouvel enregistrement avec validation complète.

$groupe = Groupe::post([
    'admin_id'           => 1,
    'nom'                => 'Tontine 2024',
    'montant_cotisation' => 5000,
    'frequence_jours'    => 30,
    'date_debut'         => '2024-01-01',
    'description'        => 'Notre tontine annuelle',
]);

echo $groupe->id;  // ID généré automatiquement
echo $groupe->nom; // "Tontine 2024"

put()

Remplace entièrement un enregistrement existant.

$groupe = Groupe::put(1, [
    'nom'                => 'Nouveau nom',
    'montant_cotisation' => 10000,
    'frequence_jours'    => 15,
    'date_debut'         => '2024-06-01',
]);

patch()

Met à jour partiellement — valide uniquement les champs fournis.

// Un seul champ
$groupe = Groupe::patch(1, ['nom' => 'Nom modifié']);

// Plusieurs champs
$groupe = Groupe::patch(1, [
    'montant_cotisation' => 7500,
    'description'        => 'Description mise à jour',
]);

delete()

Supprime et retourne l'objet supprimé.

$groupe = Groupe::delete(1);
echo $groupe->nom; // "Tontine 2024" — l'objet est retourné avant suppression

Filtres avancés

// Égalité
$groupes = Groupe::get(['admin_id' => 5]);

// Valeur nulle
$groupes = Groupe::get(['description' => null]);
// → WHERE description IS NULL

// LIKE
$groupes = Groupe::get(['nom_like' => 'tontine']);
// → WHERE nom LIKE '%tontine%'

// Min / Max
$groupes = Groupe::get(['montant_cotisation_min' => 1000]);
// → WHERE montant_cotisation >= 1000

$groupes = Groupe::get(['montant_cotisation_max' => 10000]);
// → WHERE montant_cotisation <= 10000

// Combinés
$groupes = Groupe::get([
    'admin_id'               => 3,
    'nom_like'               => 'ton',
    'montant_cotisation_min' => 1000,
    'montant_cotisation_max' => 50000,
], 'created_at DESC');

Pagination

$result = Groupe::get([
    'admin_id'   => 3,
    'pagination' => true,
    'page'       => 1,
    'limit'      => 10,
], 'created_at DESC');

// Résultat
// [
//   'total' => 45,
//   'page'  => 1,
//   'limit' => 10,
//   'pages' => 5,
//   'items' => [ Groupe, Groupe, ... ]
// ]

foreach ($result['items'] as $groupe) {
    echo $groupe->nom;
}

Relations

hasOne

Ce modèle possède un seul autre modèle.

// Dans le modèle Admin
public function session(): mixed
{
    return $this->hasOne(AdminSession::class, 'admin_id');
}

// Utilisation
$admin   = Admin::get(1);
$session = $admin->session();

hasMany

Ce modèle possède plusieurs autres modèles.

// Dans le modèle Groupe
public function participations(): array
{
    return $this->hasMany(Participation::class, 'groupe_id');
}

public function cycles(): array
{
    return $this->hasMany(Cycle::class, 'groupe_id', orderBy: 'numero ASC');
}

// Utilisation
$groupe         = Groupe::get(1);
$participations = $groupe->participations();
$cycles         = $groupe->cycles();

belongsTo

Ce modèle appartient à un autre modèle.

// Dans le modèle Participation
public function groupe(): mixed
{
    return $this->belongsTo(Groupe::class, 'groupe_id');
}

public function utilisateur(): mixed
{
    return $this->belongsTo(Utilisateur::class, 'utilisateur_id');
}

// Utilisation
$participation = Participation::get(1);
$groupe        = $participation->groupe();
$utilisateur   = $participation->utilisateur();

belongsToMany

Relation N-N via table pivot.

// Dans le modèle Utilisateur
public function groupes(): array
{
    return $this->belongsToMany(
        Groupe::class,
        'tnt_participations', // table pivot
        'utilisateur_id',     // FK locale dans le pivot
        'groupe_id'           // FK liée dans le pivot
    );
}

// Utilisation
$utilisateur = Utilisateur::get(1);
$groupes     = $utilisateur->groupes();

Eager Loading

Charge les relations en même temps que l'objet principal.

// Charger un groupe avec ses participations et cycles
$groupe = Groupe::with(['participations', 'cycles'])->get(1);

// Les relations sont accessibles via toArray(true)
$data = $groupe->toArray(true);
// [
//   'id'             => 1,
//   'nom'            => 'Tontine 2024',
//   'participations' => [ [...], [...] ],
//   'cycles'         => [ [...], [...] ],
// ]

// Ou directement
$groupe->participations; // tableau de Participation
$groupe->cycles;         // tableau de Cycle

toArray()

$groupe = Groupe::get(1);

// Sans relations
$data = $groupe->toArray();
// ['id' => 1, 'nom' => 'Tontine 2024', ...]

// Avec relations chargées (eager ou lazy)
$groupe->chargerRelations(['participations', 'cycles']);
$data = $groupe->toArray(true);
// ['id' => 1, 'nom' => '...', 'participations' => [...], 'cycles' => [...]]

save()

Sauvegarde manuellement un objet — INSERT si pas de PK, UPDATE sinon.

// Modifier un attribut et sauvegarder
$groupe = Groupe::get(1);
$groupe->nom = "Nouveau nom";
$groupe->save();

// Créer manuellement
$groupe = new Groupe();
$groupe->hydrater([
    'admin_id'           => 1,
    'nom'                => 'Tontine manuelle',
    'montant_cotisation' => 5000,
    'frequence_jours'    => 30,
    'date_debut'         => '2024-01-01',
]);
$groupe->save();
echo $groupe->id; // ID généré

Licence

MIT — © NilsFramework