How to eager load relations for a model with Spatie’s Laravel Options

Published February 15th, 2024

I really like using Spatie’s Laravel Options package to create arrays of labels and values for use in UI elements – checkboxes, select, radio. I love its simplicity and fluent approach to creating simple lists, but also appending additional model attributes too. Just feels so nice.

And it works wonderfully for Models… for Enums… with the same syntax.

How simple and eloquently expressive is this?

1Options::forModels(Category::class)->sort();
Copied!

Or from an Enum:

1Options::forEnum(CategoryType::class)->sort()
Copied!

So easy to read, write and use!

Today, I needed to add an additional attribute of a related model. Hello, n+1.

1Options::forModels(Category::with(['categoryGroup']))
2 ->append(fn(Category $category) => [
3 'category_group' => $category->categoryGroup->name,
4 ])->sort();
Copied!

If my list of options contains 20 records, that’s now 21 queries: the original lookup, plus one related lookup per item. Ouch.

But never fear: the Spatie team are superbly awesome, and the forModels method can accept a number of different sources.

Most of the time I simply pass the class name – if I want the options, I want them all.

1Options::forModels(Category::class)
2 ->append(fn(Category $category) => [
3 'category_property' => $category->some_other_category_property,
4 ])->sort()
Copied!

But you can also pass a Collection of models (or a Builder instance) – both have uses depending on where you’re getting your Models from - maybe a straight database lookup, or maybe you have them through another part of your app (a cache, route model binding, etc).

So instead of just the class - Category::class - we can construct our Builder to use the with method to get our categoryGroup relation to be eagerly loaded.

1Options::forModels(Category::with(['categoryGroup']))
2 ->append(fn(Category $category) => [
3 'category_group' => $category->categoryGroup->name,
4 ])->sort()
Copied!

And now all I have is 2 queries: one for the Category lookup and one for the Category Group lookup.

Perfect!

Super simple, and in all of my time working with Laravel Options package, this is honestly the first time I have needed to eager load a relation, so made me pause and read the docs. Because docs are so good.

You may be interested in...