Laravel Eloquent: de belangrijkste functies van de ORM

Laravel Eloquent: de belangrijkste functies van de ORM
Eloquent, de ORM die met het PHP-framework Laravel meekomt, biedt een elegante en bijzonder efficiënte manier om met je database te praten. Websites worden door al het maatwerk steeds complexer, en dus wordt ook het databaseschema complexer. Laravel Eloquent houdt dat werk eenvoudig: je schrijft leesbare, goed gestructureerde code die je maanden later nog begrijpt. Die combinatie van gemak en kracht is een van de redenen waarom Laravel zo populair is.

In dit artikel behandel ik de belangrijkste functies van Eloquent ORM.

Wat is Eloquent ORM?

Eloquent is de ORM van Laravel: de laag waarmee je je database benadert zonder zelf SQL te schrijven. De bekendste functies zijn soft deletes, automatische timestamps, de Active Record-implementatie, meerdere databaseverbindingen naast elkaar, eager loading, model-observers en model-events. Relaties zijn in Eloquent gewoon methoden op je modelklassen. Omdat ze een query builder teruggeven, knoop je er andere methoden aan vast en bouw je zo krachtige queries op.

Hoe werkt Eloquent ORM?

Eloquent leunt op het Active Record-patroon: elk model uit je MVC-structuur komt overeen met één tabel in de database. Je legt de relaties tussen die tabellen vast en werkt daarna met objecten in plaats van met queries. SQL met de hand schrijven kost tijd en levert lange, foutgevoelige strings op, met Eloquent doe je de gangbare database-operaties zonder die queries. Records invoegen, bijwerken, verwijderen en tussen verbindingen synchroniseren wordt daarmee een kwestie van een paar regels code.

Aan de slag met Eloquent

Laravel heeft een ingebouwde command line interface: Artisan. Die draait op de Console-component van Symfony en is je belangrijkste gereedschap tijdens de ontwikkeling.

Stel voordat je verdergaat je databaseverbinding in, in config/database.php.

Controleer daarna of Laravel al geïnstalleerd is. Zo niet, dan haal je Composer op via getcomposer.org en installeer je het project daarmee.

Wil je zien welke Artisan-commando’s beschikbaar zijn, voer dan dit uit:

bash
php artisan list

Je krijgt de volledige lijst met per commando een korte omschrijving. Heb je uitleg over één commando nodig, zet er dan help voor:

bash
php artisan help migrate

Eloquent-modellen aanmaken

Voordat je iets anders doet, maak je een model aan. Het model is wat met je database praat: je bevraagt er je tabellen mee en je vult ze ermee via seeders en factories. Modellen staan doorgaans in App\Models, puur een kwestie van ordening, je mag ze ergens anders zetten zolang de autoloading in composer.json klopt. Elk Eloquent-model erft van Illuminate\Database\Eloquent\Model. Het basiscommando is make:model:

bash
php artisan make: model Student

De minimale definitie van een model ziet er zo uit:

php
class User extends Model {}

Je laat de bijbehorende migratie meteen meegenereren door -m of --migration aan datzelfde commando toe te voegen.

bash
php artisan make:model Student--migration
php artisan make:model Flight -m

Seeders, factories, controllers en andere klassen genereer je op dezelfde manier, met de bijpassende optie van make:model:

bash
php artisan make:model Flight --factory
php artisan make:model Flight -f

php artisan make:model Flight --seed
php artisan make:model Flight -s

php artisan make:model Flight --controller
php artisan make:model Flight -c

Die opties combineren mag ook, zodat je alles in één keer aanmaakt.

bash
php artisan make:model Flight -mfsc

Houd daarbij een paar conventies aan:

  • Tabelnaam: Eloquent leidt de tabelnaam af uit de naam van je model, in het meervoud. Heet het model Student, dan verwacht Eloquent de tabel students. Bestaat de modelnaam uit meerdere woorden, dan scheid je die in de tabelnaam met een underscore.
  • Primaire sleutel: Eloquent gaat uit van een kolom id. Heet die bij jou anders, overschrijf dan $primaryKey op je model.
  • Timestamps: created_at en updated_at worden automatisch bijgehouden. Wil je dat niet, zet dan $timestamps op false. Het formaat van die datums stel je in met $dateFormat.

Records bijwerken en verwijderen

Bijwerken

Een record werk je bij door het op te halen, het attribuut te wijzigen en save() aan te roepen. updated_at gaat daarbij vanzelf mee: die hoef je dus niet met de hand te zetten.

php
$student = Student::find(1);

$student->email = ‘xyz@example.com';

$student>save();

Bulkupdates kunnen ook: je werkt dan in één keer elk model bij dat aan een bepaalde query voldoet.

Een bestaand record verwijderen

Verwijderen doe je met delete() op de instantie:

php
$student = Student::find(1);

$student>delete();

Verwijderen op primaire sleutel:

php
Student::destroy(1);

Student::destroy([1, 2, 3]);

Student::destroy(1, 2, 3);

En verwijderen op basis van een query kan net zo goed.

php
$affectedRows = Student::where('votes', '>', 100)->delete();

Gerelateerde modellen

Modellen hangen vaak van elkaar af: staat de waarde van het ene model vast zodra je het andere kent, dan spreken we van gerelateerde modellen. Wil je bijvoorbeeld een nieuwe reactie aan een bericht koppelen, dan hoef je post_id niet zelf in te vullen. Je slaat de reactie op via het bovenliggende model.

php
$comment = new Comment(['message' => 'A new comment.']);

$post = Post::find(1);

$comment = $post->comments()->save($comment);

Modellen aan elkaar koppelen

Met associate() zet je de foreign key op het model en koppel je het aan zijn parent. Modellen met meerdere relaties koppel je op precies dezelfde manier.

Model-events

Bij elke stap in de levenscyclus van een model, of je nu opslaat, bijwerkt of verwijdert, vuurt Eloquent een event af: een model-event. Beschikbaar zijn onder meer saving, saved, creating, created, updating, updated, deleting, deleted, restoring en restored. Sla je een nieuw record op, dan vuren creating en created, bestond het record al, dan vuren updating en updated.

Een opslagactie afbreken vanuit een event

Geeft de listener false terug, dan wordt de actie afgebroken. Dat werkt voor elk event: creating, updating, saving of deleting.

php
Student::creating(function($student)
{
    if ( ! $student>isValid()) return false;
});

Event listeners registreren

Zoals overal heeft een event een service provider nodig om geregistreerd te worden. Dat is hier EventServiceProvider: de plek waar je je model-events aan hun listeners bindt.

Bijvoorbeeld:

php
public function boot(DispatcherContract $events)
{
    parent::boot($events);

Student::creating(function($student)
    {
        //
    });
}

Model-observers

Met een observer bundel je de afhandeling van je model-events in één klasse. Elke methode van die klasse hoort bij een model-event.

php
class StudentObserver {

public function saving($model)
    {
        //
    }

public function saved($model)
    {
        //
    }

}

Een observer registreer je met de methode observe():

php
User::observe(new UserObserver);

Model-URL’s genereren

Geef je een model mee aan route() of action(), dan zet Laravel de primaire sleutel van dat model in de URI.

php
Route::get('student/{student}', 'StudentController@show');

action('StudentController@show', [$student]);

Hier komt dus het id van de student in de URL terecht. Wil je een andere eigenschap gebruiken, overschrijf dan getRouteKey() op je model.

php
public function getRouteKey()
{
    return $this->slug;
}

Meer functies van Laravel Eloquent

Omzetten naar array of JSON

Bouw je een API, dan is het antwoord vrijwel altijd JSON en moet je je modellen met hun relaties omzetten naar JSON of naar een array. Ook daar heeft Laravel Eloquent een antwoord op: met toArray() zet je een model inclusief zijn relaties om naar een array.

php
$student = Student::with('roles')->first();

return $student->toArray();

Naar JSON gaat het net zo, met toJson():

php
return Student::find(1)->toJson();

Geef je een model rechtstreeks terug uit een route, dan hoeft dat zelfs niet: zodra een model naar een string wordt omgezet, wordt het JSON. Je kunt Eloquent-objecten dus zo uit je routes teruggeven.

php
Route::get('student', function()
{
    return Student::all();
});

Gevoelige kolommen als een wachtwoord of een personal_id horen daar niet in thuis. Zet ze in de eigenschap $hidden van je model, dan laat Eloquent ze weg.

Attribute casting

Wil je het datatype van een attribuut omzetten, dan kun je voor elk attribuut een mutator schrijven, maar dat kost tijd en nodigt uit tot fouten. Handiger is casting: je zet het attribuut in de eigenschap $casts van je model. Naast boolean kun je onder meer casten naar integer, float, double, real, object, string en array.

Een voorbeeld:

php
protected $casts = [
    'is_student' => 'boolean',
];

In dit voorbeeld staat is_student met een ander datatype in de database, maar krijg je er bij het uitlezen altijd een boolean uit terug.

De cast naar array is vooral handig bij kolommen met geserialiseerde JSON. Serialiseren betekent hier dat een object als string in de kolom staat. Zet je op zo’n kolom de array-cast, dan zet Eloquent die string bij het uitlezen automatisch om naar een PHP-array.

php
protected $casts = [
    'options' => 'array',
];

Datummutators

Carbon is een uitbreiding van de PHP-klasse DateTime, met een hoop handige methoden erbij. Eloquent zet created_at en updated_at automatisch om naar Carbon-instanties. Wil je die automatische conversie aanpassen, dan overschrijf je getDates() op je model, geef je daar een lege array terug, dan schakel je haar helemaal uit. Een voorbeeld:

php
public function getDates()
{
    return ['created_at'];
}

Behandelt Eloquent een kolom als datum, dan heb je vier opties om hem te vullen: een UNIX-timestamp, een datumstring (Y-m-d), een datum met tijd, of een DateTime- of Carbon-instantie.

Accessors en mutators

Accessors en mutators zijn de getters en setters van Eloquent: ze formatteren een attribuut op het moment dat je het uitleest of instelt. Het verschil zit in de richting: een accessor werkt bij het ophalen van data, een mutator bij het wegschrijven ervan.

Een accessor definieer je als de methode getFooAttribute() op je model. Let op de naamgeving: de methodenaam staat in camelCase, ook als de kolomnaam volledig in kleine letters staat.

php
class User extends Model {

public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

}

Een mutator schrijf je op dezelfde manier, met setFooAttribute(), en ja, ook daar geldt de camelCase.

php
class User extends Model {

public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }

}

Soft deletes

Een soft delete haalt het record niet uit je database, maar zet er een timestamp deleted_at op. Je schakelt het in door de trait SoftDeletes in je model te gebruiken.

php
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model {

use SoftDeletes;

protected $dates = ['deleted_at'];

}

De kolom deleted_at voeg je in je migratie toe met softDeletes(). Een migratie is niets anders dan je database beheren in PHP in plaats van in SQL.

php
$table->softDeletes();

Soms wil je die soft deleted records tóch in je resultaten zien. Gebruik dan withTrashed() in je query.

Wil je alleen de soft deleted records terugkrijgen, gebruik dan onlyTrashed().

php
$student = Student::onlyTrashed()->where('account_id', 1)->get();

Moet zo’n record na al dat werk weer actief worden, dan gebruik je restore().

php
$student->restore();

restore() werkt ook rechtstreeks op een query.

php
Student::withTrashed()->where('account_id', 1)->restore();

Wil je een record na de soft delete definitief uit je database halen, gebruik dan forceDelete().

php
$student->posts()->forceDelete();

En om te controleren of een model soft deleted is, gebruik je trashed().

php
if ($student->trashed())
{
    //Todo
}

Conclusie

Laravel is een van de bekendste PHP-frameworks, en Eloquent maakt het praten met je database er bijzonder eenvoudig in. In dit artikel heb ik de belangrijkste functies van Laravel Eloquent behandeld. Er zit nog veel meer in, en daarover lees je hier verder. Volg de link en ontdek waartoe Laravel Eloquent allemaal in staat is.


LaravelPHP

Damien Flandrin Webdeveloper sinds 2010, maker van Gekkode en Email Impact. Elk artikel wordt vóór publicatie getest op een echt project. Contact
Nieuwsbrief

Nieuwe tests, tutorials en projecten, per e-mail.

Reproduceerbare tests, geversioneerde code, gedateerde resultaten. Nooit spam.