Laravel 4

Laravel 4 : chapitre 15 : Les bases de données 3/3

Maintenant que nous savons mettre en place la structure d’une base de données et intervenir sur les enregistrements que nous reste-t-il à voir ? Et bien Laravel nous propose un ORM vraiment efficace nommé Eloquent. C’est quoi un ORM ? C’est un « Object Relational Mapper ». Mapper parce qu’on va « mapper » des objets PHP sur les tables et les colonnes d’une base. Relational parce qu’on va aussi pouvoir gérer les relations de notre base. Mais tout cela va devenir plus clair avec des exemples. Vous n’avez pas besoin d’un ORM pour gérer une base mais vous le ferez avec beaucoup plus de facilité avec lui Wink

Je pars du principe que vous avez une base nommée laravel telle que décrite lors du chapitre précédent avec deux tables : auteurs et livres. Vous avez aussi correctement configuré le fichier app/config/database.php pour accéder à la base.

Créer un modèle avec Eloquent

Voyons comment créer un modèle avec Eloquent. Par exemple pour notre table auteurs. Créez un fichier avec ce code et enregistrez-le dans app/models/Auteur.php :

<?php
class Auteur extends Eloquent {}

Et voilà vous avez créé un modèle et vous pouvez maintenant l’utiliser Surprised. Bon il a quand même fallu prendre quelques précautions, j’ai nommé la classe Auteur, autrement dit le nom de la table avec le « s » en moins. En effet la table peut contenir plusieurs auteurs mais la classe s’adresse à un auteur à chaque fois. D’autre part il est impératif que la table contienne une clé nommée primaire « id », ce qui est notre cas.

Extraire des données

Il ne nous reste plus qu’à utiliser notre nouvelle classe. Dans une route entrez ce code :

$auteur = Auteur::find(1);
var_dump($auteur);

On demande l’enregistrement avec l’index 1. Voyons ce que nous avons en retour :

object(Auteur)[118]
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
public 'timestamps' => boolean true
protected 'attributes' =>
array (size=4)
'id' => int 1
'nom' => string 'Flaubert' (length=8)
'prenom' => string 'Gustave' (length=7)
'naissance' => string '1821-12-12' (length=10)
protected 'original' =>
array (size=4)
'id' => int 1
'nom' => string 'Flaubert' (length=8)
'prenom' => string 'Gustave' (length=7)
'naissance' => string '1821-12-12' (length=10)
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=0)
empty
protected 'dates' =>
array (size=0)
empty
public 'exists' => boolean true

C’est maintenant que nous comprenons mieux la notion de mappage. Nous obtenons un objet que nous pouvons exploiter pour extraire des informations. Ce qui est bien pratique aussi c’est que tout ce que nous avons vu au chapitre précédent concernant la gestion des enregistrements reste valable. Ainsi nous pouvons écrire :

$auteur = Auteur::where('nom', '=', 'Saulers')->get();
var_dump($auteur[0]->nom);

Ce qui donne :

string 'Saulers' (length=7)

Eloquent nous renvoie une collection qui implémente l’interface IteratorAggregate. Nous pouvons donc agir comme si nous avions un tableau en retour. En effet notre requête est susceptible de renvoyer plusieurs lignes, contrairement au cas précédent où on appelait juste une ligne en précisant son index. Il devient alors facile de traiter les lignes lorsqu’elles nous arrivent sous cette forme :

$auteurs = Auteur::where('nom', '>', 'q')->get();
foreach ($auteurs as $auteur) var_dump($auteur->nom);

Ce qui donne :

string 'Raspail' (length=7)
string 'Sibran' (length=6)
string 'Saulers' (length=7)

Insérer des données

Nous allons voir qu’il est aussi très facile d’insérer de nouvelles données. Mais auparavant je dois vous donner encore une petite indication. Par défaut Eloquent gère deux colonnes dans chaque table de type DateTime nommées respectivement updated_at et created_at. Ces colonnes sont automatiquement mises à jour à chaque création ou modification. Mais vous n’êtes pas obligé de les avoir. Si ces informations ne vous intéressent pas il suffit de compléter le modèle ainsi :

<?php
class Auteur extends Eloquent
{
    public $timestamps = false;
}

C’est ce que nous allons faire dans notre cas parce que nous n’avons pas prévu ces colonnes dans nos table. Sachez juste que cette possibilité existe et noue l’utiliserons dans un article ultérieur sur la création d’un blog. Maintenant voilà le code pour faire une insertion :

$auteur = new Auteur;
$auteur->nom = 'Frain';
$auteur->prenom = 'Irène';
$auteur->naissance = '1950-06-22';
$auteur->save();

On vérifie dans la base :

img55

Apparemment tout se passe bien. Vous pouvez aussi passer un tableau au constructeur :

$auteur = new Auteur(array(
    'nom' => 'Frain',
    'prenom' => 'Irène',
    'naissance' => '1950-06-22'
));
$auteur->save();

Modifier et supprimer des données

Pour modifier une donnée existante la syntaxe est tout aussi simple, voici un exemple :

$auteur = Auteur::find(8);
$auteur->prenom = 'Claire';
$auteur->save();

img56Pour supprimer des données c’est tout aussi simple :

$auteur = Auteur::find(8);
$auteur->delete();

Les relations

Par définition dans une base de données relationnelles nous avons des… relations. Celles-ci peuvent être explicites dans la base ou implicites (comme c’est le cas dans notre exemple où on a juste introduit une ligne dans la table des livres pour préciser l’id de l’auteur mais sans demander à MySQL de vérifier l’intégrité de cette relation. On aurait pu le faire en précisant que nous utilisons le moteur InnoDB et en déclarant cette ligne comme clé étrangère).

Eloquent a une façon très élégante de gérer les relations. Nous allons prendre comme exemple celle qui existe entre nos deux tables. Mais auparavant il nous faut créer aussi un modèle pour la table des livres (app/models/Livre.php) :

class Livre extends Eloquent
{
    public $timestamps = false;
}

Il existe 3 sortes de relations : « un vers un », « un vers plusieurs » et « plusieurs vers plusieurs ». Notre cas est le plus classique avec une relation « un vers plusieurs », en effet un auteur peut écrire plusieurs livres et on admet qu’un livre n’est écrit que par un auteur (bon on oublie les co-auteurs Tongue Out).

Pour que Eloquent reconnaisse automatiquement la clé étrangère dans une table il faut qu’elle réponde à une certaine syntaxe : le nom de la table suivi d’un soulignement puis de « id ». Manque de chance j’ai fait juste l’inverse dans la table des livres. On va corriger ça :

img57

Big Boss Cette norme n’est pas une contrainte absolue parce que d’une part on peut préciser le nom de la clé étrangère comme paramètre des méthodes relationnelles, d’autre part elle n’est pas toujours respectée par Eloquent (par exemple pour belongsTo c’est le nom de la méthode qui sert de référence). Pour avoir une vue d’ensemble du sujet référez vous au chapitre 33 de ce blog.

Maintenant nous pouvons utiliser toute la puissance et l’élégance d’Eloquent. Nous modifions la classe Auteur pour déclarer la relation :

class Auteur extends Eloquent
{
    public $timestamps = false;
    public function livres()
    {
        return $this->hasMany('Livre');
    }
}

Et maintenant ça devient facile :

$livres = Auteur::find(2)->livres;
foreach($livres as $livre) var_dump($livre->titre);

Résultat :

string 'Secouons le cocotier' (length=20)
string 'Les Royaumes de Borée' (length=22)

On peut mettre en œuvre toutes les possibilités de manipulation de données. Par exemple si on ne veut que le premier livre :

$livre = Auteur::find(2)->livres()->first();
var_dump($livre->titre);

Résultat :

string 'Secouons le cocotier' (length=20)

Inverse d’une relation

Considérons à nouveau notre relation en la regardant dans l’autre sens. Nous avons dit qu’un auteur peut écrire plusieurs livres. Nous avons traduit cela en précisant dans la classe Auteur ce code :

public function livres()
{
    return $this->hasMany('Livre');
}

On pourrait aussi dire qu’un livre appartient à un auteur. Cette fois au lieu de renseigner la relation dans la classe Auteur on le fait dans la classe Livre :

<?php
class Livre extends Eloquent
{
    public $timestamps = false;
    public function auteur()
    {
        return $this->belongsTo('Auteur');
    }
}

Testons cela :

$auteur = Livre::find(1)->auteur;
var_dump($auteur->nom);

Résultat :

string 'Raspail' (length=7)

Chargement dynamique

Avec la relation précédente considérez ce code :

foreach(Livre::all() as $livre)
echo '"'.$livre->titre.'" a été écrit par '.$livre->auteur->nom.'<br>';

Et son résultat :

"Secouons le cocotier" a été écrit par Raspail
"Pêcheurs de Lune" a été écrit par Avril
"Les Royaumes de Borée" a été écrit par Raspail
"Le Salon des artistes" a été écrit par July

Le résultat correspond à notre attente mais… nous ne sommes pas très efficace parce que nous multiplions les requêtes. Il y en a une pour pour récupérer tous les livres et ensuite une pour chaque chaque livre pour trouver l’auteur. Imaginez si nous avons des milliers de livres ! Heureusement il existe une solution :

foreach(Livre::with('auteur')->get() as $livre)
echo '"'.$livre->titre.'" a été écrit par '.$livre->auteur->nom.'<br>';

Cette fois on a une seule requête dans la boucle qui prend la forme :

SELECT * FROM auteurs WHERE id IN (2,3,7);

Il y aurait encore beaucoup à dire sur Eloquent. je vous renvoie encore une fois à la documentation ou sa version Française. Vous pouvez également consulter le chapitre 33 de ce blog qui est consacré à ce sujet.

Print Friendly, PDF & Email

13 commentaires

  • christoff

    Je me pose la question quand faut-il utiliser le Query builder ou l’ORM Eloquent ?
    Chacun a des avantages et des inconvénients

    Avec l’ORM Eloquent, on a un code plus compacte et plus lisible, il n’y a pas besoin de décrire une méthode dans le modèle qui est presque vide.
    Par contre, il faut bien respecter certaines conventions de nommage : Singulier ou pluriel selon le nom des tables ou du modèle, nom des champs qui contiennent des id.

    Personnellement j’utilise l’ORM Eloquent pour des requêtes simples.

    Par contre je préfère utiliser le Query Builder, pour des requêtes plus complexes. Le code est peut-être plus long, mais je passe moins de temps à trouver la solution.
    Par exemple : des jointure de tables + sélection d’élément dans 2 tables différentes + ORDERBY sur une donnée. Je n’ai pas trouvé comment le faire avec Eloquent car j’obtenais des erreurs malgré un code scrupuleusement conforme à la documentation officielle. Autre exemple : trouver l’enregistrement précédant ou l’enregistrement suivant d’un champ qui est ordonné. Je n’ai pas pu le faire avec l’ORM.

    Dans ces deux cas j’ai été obligé de créer une méthode dans un des modèles. Pour que la méthode puisse être appelée depuis le controller, j’ai été obligé de lui donner l’attribut « public static function(){} ». Est-ce que c’est la bonne façon de faire ?

    Ce qui est également très déroutant pour un novice comme moi, c’est que les résultats peuvent avoir des formats très différents : des tableaux indexés ou associatifs, des objets ORM, une valeur unique. J’ai l’habitude de faire d’abord un return sur ma requête pour voir sous quel format se présentent les données dans le navigateur. C’est seulement après que je return les résultats sur une vue blade.

    • bestmomo

      Clairement Eloquent n’est pas fait pour les opérations complexes. On passe effectivement plus de temps à trouver comment faire, quand on y arrive ! Donc il ne faut pas hésiter d’utiliser le Query builder dès que ça ne devient plus évident avec Eloquent.

      Le fait de créer une méthode statique te permet de faire un appel statique. C’est plus pratique.

      C’est vrai que les formats retournés sont un peu déroutants au départ 😉

  • ChDUP

    Hello
    J’ai une table « matchs » avec ces entrées : id / team_id_1 / team_id_2
    Du coup, les relations avec la table « teams » ne peut pas fonctionner à cause des intitulés _1 et _2.
    Chaque match a 2 teams, que j’ai besoin d’identifier comme team_1 et team_2.
    Chaque team est présente dans plusieurs matchs, parfois en team_1 parfois en team_2
    Comment gérer ce cas au mieux ?
    merci

  • maxgosset

    bonjour , me revoilà de nouveau avec un nouveau probleme
    j’ai en fait une page modification sur laquelle on choisit le contact à modifier avec par la suite un formulaire qui indique les données deja inscrites dans la table
    jusque là tout va bien , tout s’affiche parfaitement
    mais une fois sur la page de verification du nouveau formulaire
    il me met une erreur « Creating default object from empty value » et je ne comprend vraiment pas
    voici mon code de la page vérification

    $id = Input::get(‘id’);
    $contact =contact::find($id);
    $contact->nom =Input::get(‘nom’);
    $contact->save();

    si quelqu’un pouvait m’aider ce serait vraiment cool
    merci

  • leir

    Un grand merci pour ce tuto qui tombe à pique dans mon apprentissage de Laravel. Je me demandais justement si j’allais devoir créer une classe complète en déclarant tous les attributs pour chacune de mes tables, j’ai ma réponse ^^. Merci encore !

Laisser un commentaire