Protone Media logo

Hey! Did you hear about Launcher? 🚀

It's an easy-to-use deployment tool to deploy your Laravel apps containerized with Docker. Launcher is remarkably easy, and still, you can fully customize it to your needs. Start launching your sites in just minutes using our free 14-day trial.

Database and Eloquent ORM: New features and improvements since the original Laravel 8 release (2/2)

This blog post follows up on last week's overview of the new Database and Eloquent features in Laravel 8 since the original release in September 2020. I already covered Collections, and next week is all about Jobs and Queues. Enjoy!

I got most code examples and explanations from the PRs and official documentation.

v8.41.0 Added Model::updateQuietly() (#37169)

Sometimes you may wish to "update" a given model without dispatching any events. You may accomplish this using the updateQuietly method, which uses the saveQuietly method under the hood:

$flight->updateQuietly(['departed' => false]);

As of v8.59, you may also use the createOneQuietly, createManyQuietly and createQuietly methods when using Model Factories:

Post::factory()->createOneQuietly();

Post::factory()->count(3)->createQuietly();

Post::factory()->createManyQuietly([
    ['message' => 'A new comment'],
    ['message' => 'Another new comment'],
]);

v8.41.0 Added Model key extraction to id on whereKey() and whereKeyNot() (#37184)

You can now use a Model instance with the whereKey and whereKeyNot methods:

$passenger->tickets()
    ->whereHas('airline', fn (Builder $query) => $query->whereKey($airline))
    ->get();

v8.42.0 Added withExists method to QueriesRelationships (#37302)

In addition to the withCount method, you may now use the withExists method to check the existence of a relationship:

// before:
$users = User::withCount('posts')->get();

$isAuthor = $user->posts_count > 0;

// after:
$users = User::withExists('posts')->get();

$isAuthor = $user->posts_exists;

// the column name can also be aliased:
$users = User::withExists([
    'posts as is_author',
    'posts as is_tech_author' => function ($query) {
        return $query->where('category', 'tech');
    },
    'comments',
])->get();

v8.42.0 Added loadExists on Model and Eloquent Collection (#37388)

This PR follows upon the withExists method in the example above. You may now use the loadExists method in both Model and Eloquent Collections:

$books = Book::all();

$books->loadExists(['author', 'publisher']);

v8.42.0 Added one-of-many relationship (inner join) (#37362)

One-to-one relations that are a partial relation of a one-to-many relation. You can retrieve the "latest" or "oldest" related model of the relationship:

/**
 * Get the user's most recent order.
 */
public function latestOrder()
{
    return $this->hasOne(Order::class)->latestOfMany();
}

/**
 * Get the user's oldest order.
 */
public function oldestOrder()
{
    return $this->hasOne(Order::class)->oldestOfMany();
}

/**
 * Get the user's largest order.
 */
public function largestOrder()
{
    return $this->hasOne(Order::class)->ofMany('price', 'max');
}

v8.43.0 Added eloquent strict loading mode (#37363)

You may instruct Laravel to always prevent the lazy loading of relationships. You should call this method within the boot method of your app's AppServiceProvider class.

Model::preventLazyLoading(! app()->isProduction());

v8.43.0 Added beforeQuery to base query builder (#37431)

This PR is a very advanced one! It allows you to change a "subselect"-builder, whereby the changes are also applied to the parent query builder. The preserved closures will be called before the query gets rendered. This way, you can keep "subquery"-builders alive and modify the subquery after applying it to the parent builder:

// 1. Add the subquery
$builder->beforeQuery(function ($query) use ($subQuery) {
    $query->joinSub($subQuery, ...);
});

// 2. Add constraints to the subquery
$subQuery->where('foo', 'bar');

// 3. Render the subquery, the constraints from 2. are applied
$builder->get();

v8.50.0 Added the possibility of having "Prunable" models (#37889)

You may want to periodically delete models that are no longer needed. With the Prunable trait Laravel will automatically remove obsolete model records from the database via a scheduled command.

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;

class Flight extends Model
{
    use Prunable;

    /**
     * Get the prunable model query.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function prunable()
    {
        return static::where('created_at', '<=', now()->subMonth());
    }
}

The Artisan CLI command:

php artisan model:prune

You may also define a pruning method on the model, which will be called before the model is deleted:

protected function pruning()
{
    // Delete additional resources associated 
    // with the model, for example: files

    Storage::disk('s3')->delete($this->filename);
}

v8.53.0 Added immutable date and datetime casting (#38199)

Ssupport for accessing dates as immutable. Returns a CarbonImmutable instance instead of a Carbon instance.

class User extends Model
{
    public $casts = [
        'date_field'     => 'immutable_date',
        'datetime_field' => 'immutable_datetime',
    ];
}

v8.57.0 Added a simple where helper for querying relations (#38499)

You may now use the whereRelation and whereMorphRelation methods to query for a relationship's existence with a single, simple where condition attached to the relationship query:

// Before:
User::whereHas('posts', function ($query) {
    $query->where('published_at', '>', now());
})->get();

// After
User::whereRelation('posts', 'published_at', '>', now())->get();

// Morph relation
Comment::whereMorphRelation('commentable', '*', 'public', true);

v8.59.0 Added Eloquent builder whereMorphedTo method to streamline finding models morphed to another model (#38668)

The new whereMorphedTo and orWhereMorphedTo methods are a shortcut for adding a where condition looking for models that are morphed to a specific related model, without the overhead of a whereHas subquery:

Feedback::whereMorphedTo('subject', $user)->get();

Feedback::whereMorphedTo('subject', User::class)->get();

v8.59.0 Added support for disallowing class morphs (#38656)

You may call the enforceMorphMap method in the boot method of your apps' AppServiceProvider class. This disallows morphs without a morph map on it.

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::enforceMorphMap([
    'post'  => Post::class,
    'video' => Video::class,
]);

v8.60.0 Added the valueOfFail() Eloquent builder method (#38707)

In addition to the value method, you may now use the valueOrFail method. This will throw a ModelNotFoundException if the model is not found.

// Before:
$votes = User::where('name', 'John')->firstOrFail('votes')->votes;

// Now:
$votes = User::where('name', 'John')->valueOrFail('votes');

v8.63.0 Added whereBelongsTo() Eloquent builder method (#38927)

The new whereBelongsTo method automatically determines the proper relationship and foreign key for the given model:

// Before:
$posts = Post::where('user_id', $user->id)->get();

// After:
$posts = Post::whereBelongsTo($user)->get();

Related posts

Want to stay up-to-date on Laravel news and packages?

Pascal Baljet on Twitter

Follow me on Twitter: @pascalbaljet

Pascal Baljet on Twitter

Subscribe to my YouTube channel

© 2013-2021 Protone Media B.V.