Knitzilla is a knitting app that I created for my wife.
Originally it was only supposed to keep track of how many rows she knitted, but in time, it grew.
It is now at a stage where you can commit to several different knitting projects, and you can add and delete counters to each project.
Since I, in time, want my wife to be able to create a portfolio of all the knitting projects that she has finished I needed a system to put this in place.
Luckily for me, Laravel ships just such a feature out of the box.
To make my Project model able to support soft deletes I needed to mutate the user table in my database. This is easily done with a migration.
should fix that right up.
Now with my database updated, the only thing left is to tell my model that I want to use this feature.
Since all my database communication goes through the eloquent model, these things become a breeze.
Very nice indeed.
However, I need a new way to get all projects now, even the deleted ones. Wouldn't be much of a portfolio if you could never retrieve the actual items.
This is where it becomes a wee bit advanced.
If you have ever written a backend, you surely know what request middleware is, and you properly like it.
What if I told you that such pipelines are not reserved for requests? Laravel provides their abstraction of pipelines, free to use, if you know how to.
I will just show this with a pipeline with a single middleware function, but these can be as large or small as you would like them to be.
First of all, the abstract class we will be working with.
A few things to notice here:
filter_namefunction, I am using a function called class_basename with this context. This is a standard PHP function that extracts the name of the class which it is called with. The cool thing is that it returns the inheritor's name. This means that this function can be implemented in our base class, and reused in all the different filters we want to provide.
- This pattern is depending on a specific naming convention. It expects the filters is the snake case name of the query params from our request.
- This means a class name of
Deletedwould need to have
?deleted=truein the query params, and
With the last function in the class, we are telling everyone who extends this class that they have to implement an
handlefunction we decide if this specific piece if middleware needs to activate. If it don't, we just invoke the
$nextpiece of middleware.
If it does, however, we defer the next in the chain to the
With all the smart things already packed away in our
AbstractQueryablewe only need to add the invoke the necessary function on the query builder.
Now it all ties together, we can define a static method on our model and begin the pipeline.
The code here is pretty self-explanatory. We get the Pipeline from our app service container, and through it, we want to send a
Project::query()which returns a chainable query much like Entity frameworks fluent query builder.
thenReturn gives back the query builder and from there was some functionality I want to always be present.
I want all related models to the project, and I am only interested in the project connected to the id the function is invoked with.
Pipelines open up for some pretty neat functionality, and it gives a way to specify a single endpoint, and then have the user, through the pipeline define how the data should be i.e sorted, filtered, paginated and so on.
Maybe it would be nice to have a
?paginate=15&page=1 for the ones who want to paginate, and all, for the ones who want all (i wouldn't know why).
If you have read my other laravel posts, you know I like to test it.
So just for the eager learner, I have provided the code I used to test the deleted functionality of the pipeline I created.