app/Models you define ± 1 model (e.g. Smurf) per table (e.g. smurfs)
hasOne() and belongsTo()
hasMany() and belongsTo()
belongsToMany() at both sides
Smurf::all() // returns an Eloquent collection of Smurf objects
Smurf::find(666) // returns a Smurf object
Smurf::count() // returns an integer
Smurf::where('age', '>', 315)
Smurf::select('id', 'age')
$smurfs = Smurf::where('age', '>', 315)->where('color', 'blue')->select('id', 'age')->orderBy('age', 'desc');
$smurfs->get() // returns an Eloquent collection of Smurf objects
$smurfs->count() // returns an integer
$smurfs->first() // returns a Smurf object
$smurfs->find(666) // returns a Smurf object
$smurfs->value('age') // returns a value
$smurfs->pluck('age') // returns a plain old array
$smurfs->get()->count() // Better: $smurfs->count()
$smurfs->get()->where('age', '>', 320) // Better: fix this in the Builder part
Smurf::all()->find(666) // Better: Smurf::find(666)
$smurfs->get()->contains(88); // Better: $smurfs->where('id', 88)->exists();
Smurf::find(666)->friends // executes a query and returns an Eloquent collection
Smurf::find(666)->friends() // only returns a Builder object ...
if ($post->is($anotherPost)) {
// ...
}
if ($post->isNot($anotherPost)) {
// ...
}
if ($post->author()->is($user)) {
// ...
}
$books = Book::all(); // collection of 25 books
foreach ($books as $book) {
echo $book->author->name; // generating a DB query each time
}
with() supports eager loading and adds a question to prefetch the related model objects in the Builder object:
$books = Book::with('author')->get(); // generates 2 queries
foreach ($books as $book) {
echo $book->author->name; // no DB query
}
$lazyProduct = Product::find(1);
$eagerProduct = Product::with('brand')->find(1);
dump($lazyProduct); // internal relations field empty
dump($eagerProduct); // internal relations field with 'brand'
// no difference
dump($lazyProduct->brand->name);
dump($eagerProduct->brand->name);
Book::with(['author', 'publisher'])->get();
Book::with('author.contacts')->get();
Book::with(['author' => ['contacts', 'publisher']])->get();
Book::with('author:id,name')->get(); // include internally required PK/FK columns
use App\Models\Book;
use Illuminate\Contracts\Database\Eloquent\Builder;
// you do not need to to this when the benefit is insignificant
$books = Book::with(['reviews' => function (Builder $query) {
$query->where('rating', '>', 4.0)->orderBy('created_at', 'asc');
}])->get();
// not to be confused with withWhereHas, only querying the books with rating > 4.0
// withWhereHas = whereHas + with
$books = Book::withWhereHas(['reviews' => function (Builder $query) {
$query->where('rating', '>', 4.0);
}])->get();
ProductController,Product::all() is passed to a product overview Blade template
$product, it shows
{{ $product->id }}
{{ $product->name }}
{{ $product->price }}{{ $product->brand->name }} {{ $product->categories->pluck('name')->implode(' | ') }}
Product::all()?
Product::select(['id', 'name', 'price', 'brand_id'])->with(['brand:id,name', 'categories:id,name'])->get()
protected $with to the model class
class Book extends Model
{
/**
* The relationships that should always be loaded.
*
* @var array
*/
protected $with = ['author'];
/**
* Get the author that wrote the book.
*/
public function author(): BelongsTo
{
return $this->belongsTo(Author::class);
}
}$books = Book::without('author')->get();
$books = Book::withOnly('genre')->get();$books = Book::all();
if ($someCondition) {
$books->load('author', 'publisher');
}
if ($someOtherCondition) {
$books->loadMissing('author'); // only when not already loaded
}AppServiceProvider
use Illuminate\Database\Eloquent\Model;
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}
This will throw a LazyLoadingViolationException when a lazy load is attempted.
AppServiceProvider
public function boot(): void
{
Model::automaticallyEagerLoadRelationships();
}
This is a beta feature in Laravel 12 !
$book->author for the first time,
Laravel will automatically eager load the author relationship for
all books in the collection, so that access to $book->author (on another $book of the collection) will not trigger a DB query.
$books->withRelationshipAutoloading();
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
#[Scope]
public function active(Builder $query): void
{
return $query->where('active', 1);
}
#[Scope]
public function ofType(Builder $query, string $type): void
{
return $query->where('type', $type);
}
}
use App\Models\User;
$users = User::ofType('admin')->active()->orderBy('created_at')->get();
$users = User::ofType('admin')->orWhere->active()->get(); // orWhere specifically for scopes
app/Models/Scopes directory:
php artisan make:scope AncientScope
apply() method of the Scope interface:
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', '<', now()->minus(years: 2000));
}
}
#[ScopedBy] attribute
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AncientScope::class])]
class Book extends Model
Book::all() will only return books created before 2000 years ago.
$user->first_name), typically for data transformation $user->full_name) while there is no such column or relationprotected function attributeName() returning an instance of Illuminate\Database\Eloquent\Casts\Attribute
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected function firstName(): Attribute
{
return Attribute::make(
get: fn ($value) => ucfirst($value),
set: fn ($value) => strtolower($value),
);
}
protected function fullName(): Attribute
{
return Attribute::make(
get: fn ($value, $attributes) => ($attributes['first_name'] . ' ' . $attributes['last_name']),
);
}
}
firstName() in camel case but attribute call first_name in snake case$user = User::find(1);
$firstName = $user->first_name;
$user->first_name = 'Bart';
echo($user->full_name);
$user->save();
php artisan make:class Support/Address
and write the constructor, getters and setters …
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
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) => [ // return array with database column keys
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
],
);
}
}
->) is invoked$user = User::find(1);
$address = $user->address;
$address->zip = 9000; // changes the zip of address inside $user
$user->save(); // updates zip
Attribute::make()->withoutObjectCaching();$user = User::find(1);
$firstName = $user->first_name;
$firstName = 'Sven';
$user->save(); // unchanged
Attribute::make()->shouldCache();class User extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_admin' => 'boolean',
'options' => 'array', // handy when this column contains JSON
'date_of_birth' => 'datetime:Y-m-d',
// auto-convert to Carbon objects (date format is only for serialization)
// created_at and updated_at are already known as datetime
'directory' => AsStringable::class, // cast to fluent string
];
}
}