Maintenant que l'on a fini la partie travaux pratiques et que les notions de base sont bien assimilées, je vous propose de rentrer un peu plus en profondeur sur certains fonctionnements de Laravel. On va commencer par revenir sur les models Eloquent avec les scope et les casts.
Scopes
Les scopes permettent de réutiliser des éléments dans les Query Builder et d'éviter la répétition. Pour créer un scope il suffit de créer une méthode commençant par scope
.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}
Les scopes auront systématiquement une instance de Builder en premier paramètre. Les autres paramètres seront utilisés lorsque la méthode associée au scope sera utilisée.
Ensuite, vous pouvez utilisez le scope en utilisant des méthodes correspondant au nom de vos scopes.
$users = User::popular()->ofType('admin')->orderBy('created_at')->get();
Il est aussi possible de définir des scope globaux qui vont s'appliquer par défaut mais ils sont a utiliser avec précautions car il peut être ensuite difficile de retirer ce scope.
Soft delete
Le Soft Delete permet, lorsque l'on supprime un enregistrement, de ne pas faire une suppression au niveau de la base de données, mais simplement de sauvegarder sa date de suppression en base de données pour pouvoir garder les informations si on a besoin de revenir dessus. Ce scope peut être ajouté via un trait
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}
Avec ce trait, lorsque vous supprimez un Model
Laravel va plutôt mettre à jour la date de suppression via le champs deleted_at
.
Si ce champs n'existe pas vous pouvez créer une migration pour le créer :
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('posts', function (Blueprint $table) {
$table->softDeletes();
});
Ensuite, lorsque vous récupérez des données provenant de cette table Laravel va systématiquement ajouter une condition pour filtrer les enregistrements qui ont une date de suppression. Vous pouvez, si vous le souhaitez, rajouter une scope pour inclure les données supprimées :
Post::withTrashed()->get();
Cast & Mutateur
Les accesseurs, mutateurs et casts permettent de transformer les valeurs des attributs et Eloquent quand vous récupérez ou définissez les valeurs.
Lorsque laravel récupère des informations provenant de la base de données il les stocke dans le la propriété attributes
du model. Ensuite, lorsque l'on récupère les informations au travers d'une propriété, Laravel va utiliser une méthode magique pour récupérer la clé qui correspond dans ce tableau d'attributs.
Mais en plus de cette récupération il est capable de faire une certaine logique pour transformer la donnée à nous renvoyer. On a un exemple de cette situation avec les dates de création et de modification.
dump($post); // attributes contiendra created_at sous forme de chaine
dump($post->created_at); // sera un objet DateTime
Attribute casting
Cette conversion peut être paramétré au travers d'une propriété $cast
qui permet d'associer un type particulier à un attribut.
<?php
namespace App\Models;
use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'options' => Json::class,
'is_admin'=> 'boolean',
];
}
Pour plus d'information sur l'attribute casting je vous renvoie sur la documentation.
Accesseur
Un accesseur transforme les données des attributs Eloquent lors d'un accès. Pour définir un accesseur il suffit de créer une méthode protégée sur le modèle qui correspond au nom de l'attribut que l'on souhaite modifier (en utilisant le format camelCase).
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// Ajoute une majuscule au prénom automatiquement
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
);
}
}
Il est aussi possible de récupérer les informations provenant de plusieurs attributs pour concevoir générer la valeur.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
);
}
On notera que, dans ce cas-là, Laravel va automatiquement mettre en cache la valeur retournée lors du premier accès afin de ne pas générer un nouvel objet à chaque fois que l'on accède à la propriété adresse.
Mutateur
En plus des accesseurs on a la possibilité de définir des mutateurs de la même manière grâce à un autre paramètre dans les attributs.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Interact with the user's first name.
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
set: fn (string $value) => strtolower($value),
);
}
}
Comme avec les accesseurs, il est possible de modifier plusieurs attributs à la fois en retournant un tableau plutôt qu'une simple valeur en retour.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function address(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => new Address(
$attributes['address_line_one'],
$attributes['address_line_two'],
),
set: fn (Address $value) => [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
],
);
}