Laravel — Extending Query Builder

Laravel — Extending Query BuilderDennis SminkBlockedUnblockFollowFollowingMay 24Sometimes the findOrFail() (or any other default) method is not enough and you need to have a method that applies to multiple models at the same time.

This article describes how to achieve this.

????Let’s grab a real simple example in this guide.

Let’s say you have a News model and a Page model, both have the slug column in the database.

Sure, you could do something like this:News::where('slug', 'my-slug')->firstOrFail();But wouldn’t it be way nicer to have something like:News::findBySlugOrFail('my-slug');This is where extending the Laravel query builder comes in handy.

Start by creating a new file: app/Builder/MyBuilder.

php (You can name this anything you like to be more specific, like AppBuilder.

php or something like that.

Just make sure you name the class inside the same as the file name for the namespace to kick in properly ????)With these contents:<?phpnamespace AppBuilder;use IlluminateDatabaseEloquentBuilder;class MyBuilder extends Builder{}Before we continue adding methods inside this builder, lets prepare our models to start using this class instead of the default Model from Eloquent.

If you where to have a model like News (or Page) it would look somewhat like this:<?phpnamespace App;use IlluminateDatabaseEloquentModel;class News extends Model{ protected $fillable = [ 'slug', 'title', 'content' ];}We will have to start telling this model to use the newer query builder class.

We do this like so:<?phpnamespace App;use AppBuilderMyBuilder;class News extends MyBuilder{ protected $fillable = [ 'slug', 'title', 'content' ];}This will still work like before, everything is available like you’re used too.

We are now going to add new method to the MyBuilder.

php class.

So lets say, we want to have a findBySlug() method:<?phpnamespace AppBuilder;use IlluminateDatabaseEloquentBuilder;class MyBuilder extends Builder{ public function findBySlug($slug, $columns = ['*']) { return $this->where('slug', $slug)->first($columns); }}This will now allow you to do queries like this:News::findBySlug('my-slug');(And if you want, you can even pass in an array with columns you want to select)You can go absolutely wild with this, lets extend this even further:<?phpnamespace AppBuilder;use IlluminateDatabaseEloquentBuilder;class MyBuilder extends Builder{ public function findBySlug($slug, $columns = ['*']) { return $this->where('slug', $slug)->first($columns); } public function findBySlugOrFail($slug, $columns = ['*']) { return $this->where('slug', $slug)->firstOrFail($columns); }}This will make it possible to get a 404 if the first result is never found:News::findBySlugOrFail('my-slug');Basically each method from the eloquent class is available in the builder class; whereIn, whereHas, where, whereBetween, with, load etc etc etc.

Another exampleI have a project where the slugs are stored inside a separate table, and polymorphed to its models.

To achieve the same thing as above, I will have to query on a relationship inside a model, and this is how I did that:/** * Find a model by its slug.

* * @param string $slug * @param array $columns * * @return IlluminateDatabaseEloquentModel|IlluminateDatabaseEloquentCollection|static[]|static|null */public function findBySlug($slug, $columns = ['*']){ return $this->whereHas('slug', function ($query) use ($slug) { return $query->where('slug', $slug); })->first($columns);}/** * Find a model by its primary slug or throw an exception.

* * @param string $slug * @param array $columns * * @return IlluminateDatabaseEloquentModel|IlluminateDatabaseEloquentCollection|static|static[] * * @throws IlluminateDatabaseEloquentModelNotFoundException */public function findBySlugOrFail($slug, $columns = ['*']){ return $this->whereHas('slug', function ($query) use ($slug) { return $query->where('slug', $slug); })->firstOrFail($columns);}As you can see, there are many methods to write for your own logic to clean up controllers and your code at the same time.

And thats it!.You can now start writing your own query methods inside your builder.

Of course, you can also create multiple builders for specific use cases to separate code and clean it up more.

????Note: please bear in mind that I am a Dutch developer, English is not my native language so this article could contain any grammatical errors.

.. More details

Leave a Reply