Try Depot: Bring ultra-fast, remote Docker builds directly to your Laravel workflow

Eloquent Subquery Enhancements in Laravel 6.0

Published on by

Eloquent Subquery Enhancements in Laravel 6.0 image

If you’ve been following my work for any length of time, you know that I am a big fan of pushing more work in our Laravel applications to the database layer. By doing more work in the database we can often reduce the number of database queries we make, reduce the amount of memory our applications use, and reduce the amount of time required by Eloquent to process our models. This can result in some pretty significant performance wins.

One excellent way to push more work to the database is by using subqueries. Subqueries allow you to run nested queries within another database query. This can be a powerful way to retrieve ancillary model data, without making any additional database queries, when it’s not possible to do via a relationship. You can also use subqueries in order by statements, where statements, and other database clauses.

During my Laracon US 2019 talk, I made reference to a couple of query builder macros I’ve been using that make it easier to use subqueries in Laravel. I’ve since submitted three pull requests to Laravel to add these to the core framework.

Here’s an overview of each:

“Select” subqueries

Pull request #29567 adds support for subqueries to both the select() and addSelect() query builder methods.

For example, let’s imagine that we have a table of flight destinations and a table of flights to destinations. The flights table contains an arrived_at column which indicates when the flight arrived at the destination.

Using the new subquery select functionality in Laravel 6.0, we can select all of the destinations and the name of the flight that most recently arrived at that destination using a single query:

return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();

Notice how we’re using Eloquent to generate the subquery here. This makes for a nice, expressive syntax. That said, you can also do this using the query builder:

return Destination::addSelect(['last_flight' => function ($query) {
$query->select('name')
->from('flights')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1);
}])->get();

“Order by” subqueries

In addition, pull request #29563 makes it possible to use subqueries in the query builder’s orderBy() method. Continuing our example above, we can use this to sort the destinations based on when the last flight arrived at that destination.

return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();

As with selects, you can also use the query builder directly to create the subquery. For example, maybe you want to order users based on their last login date:

return User::orderBy(function ($query) {
$query->select('created_at')
->from('logins')
->whereColumn('user_id', 'users.id')
->latest()
->limit(1);
})->get();

“From” subqueries

Finally, pull request #29602 makes it possible to use subqueries within the query builder’s from() method. These are sometimes called derived tables.

For example, maybe you want to calculate the average total donations made by users in your application. However, in SQL it’s not possible to nest aggregate functions:

AVG(SUM(amount))

Instead, we can use a from subquery to calculate this:

return DB::table(function ($query) {
$query->selectRaw('sum(amount) as total')
->from('donations')
->groupBy('user_id');
}, 'donations')->avg('total');

You probably won’t need to use this every day, but when you do need it, it’s indispensable.

One breaking change to be aware of if you’re using Eloquent outside of Laravel is a signature change to the table() method on the Illuminate/Database/Capsule/Manager object. It’s been changed from table($table, $connection = null) to table($table, $as = null, $connection = null).

Learn more

If you’re interested in learning more about subqueries and other advanced database techniques, be sure to follow my blog, and also watch my Laracon US 2019 talk.

At Laracon I also announced a new video course I’m working on called Eloquent Performance Patterns. My goal with this course is to teach Laravel developers how to drastically improve the performance of their Laravel applications by pushing more work to the database layer, all while still using the Eloquent ORM. Be sure to join the mailing list for that if you’re interested!

Jonathan Reinink photo

Web designer and developer. Laravel contributor. Author of Inertia.js. Coauthor of Tailwind CSS. Be sure to check out my upcoming Eloquent Performance Patterns video course!

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Laravel Code Review

Get expert guidance in a few days with a Laravel code review

Visit Laravel Code Review
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
Inertia.js v3.0.0 Is Here with Optimistic Updates, useHttp, and More image

Inertia.js v3.0.0 Is Here with Optimistic Updates, useHttp, and More

Read article
Laravel Boost v2.4.0 Adds Security Audits and a Laravel Best Practices Skill image

Laravel Boost v2.4.0 Adds Security Audits and a Laravel Best Practices Skill

Read article
Building Transaction-Safe Multi-Document Operations in Laravel image

Building Transaction-Safe Multi-Document Operations in Laravel

Read article
Ship AI with Laravel: Building Your First Agent with Laravel 13's AI SDK image

Ship AI with Laravel: Building Your First Agent with Laravel 13's AI SDK

Read article
OG Kit: Generate Dynamic Open Graph Images with HTML and CSS image

OG Kit: Generate Dynamic Open Graph Images with HTML and CSS

Read article
Prism Workers AI — A Cloudflare Workers AI Provider for Prism PHP image

Prism Workers AI — A Cloudflare Workers AI Provider for Prism PHP

Read article