Playing
01. Making Eloquent models 'LiveAware'

Episodes

0%
Your progress
  • Total: 11m
  • Played: 0m
  • Remaining: 11m
Join or sign in to track your progress

Transcript

00:00
Okay, so in this snippet, I'm going to talk to you about a pattern that I've been using for things in your application that can be live or not live.
00:09
So let's jump right in and just go ahead and create out a model here. I'm just going to create an article out which of course as you're writing it, it's probably not live. And then when you want to put it live, it is live.
00:21
So let's go ahead and generate a migration alongside this and let's go ahead and just really quickly fill this out with a title. Of course, you'll probably have other things in there as well. And let's go ahead and really importantly create that Boolean.
00:34
So the key thing here is the pattern I use is a trait to scope this to whether it's live or not. We're also going to talk about a couple of other things around this as well, just to really clean up your code and make your life a lot easier.
00:48
Now the first thing that I always do is set a default here of false. It's really important because if you're maybe manually creating items in your database, you're going to want this to be false by default. Second of all, if you forgot to add this anywhere specifically to true, it's a good idea to
01:03
always have that fallback at your database later. Okay, so let's go ahead and run phpArtisanMigrate here. I've just got a fresh Laravel installation. We just hop over to the database, over to articles, let's go ahead and create out an
01:16
article in here. So just say article1 and let's set this to false by default or of course that will always go to false because we specified it. Okay, so next, let's hop over to our article model and inside of here what we would normally
01:31
do is create some kind of query scope for this so we could only grab back live articles if we wanted to. Now this is absolutely fine but to avoid the risk of duplicating this many times, you may need to update this in the future, what I do is think, well, you know, let's extract
01:50
this out to a scope. So what I tend to have in a small project is an eloquent directory just in case I have observers in here as well and then I just go ahead and create a traits directory inside of here.
02:02
So let's go and create a new trait inside of here and I call this live aware. So this is aware of the fact that we can have this live or not. So let's go ahead and create this out, so live aware and of course this isn't a class, it's a trait and let's pull the namespace in for this.
02:20
So this is pretty basic. We just want to go ahead and create a live scope inside of here and into this we will get our builder, that's really simple. Let's go ahead and pull the namespace in for eloquent builder, you don't need to type in
02:32
that though and let's go ahead and return that builder and just say where live is true, pretty straightforward. Now you've got one place that you can update this if there are any other conditions to this being live.
02:45
So over in article then, of course what we need to do is say use live aware, of course you can change the name of that as well if you want to, pull that in and we are good to go. So inside of here, let's die dump on article get which of course will extract out all of
03:01
the articles in the database. If we go ahead and give this a refresh, sure enough we get a collection with one article in there. Now because we have that scope on, we can just say, well, I only want to get back live
03:11
articles, give that a refresh and sure enough we see nothing. Now that's pretty straightforward, you probably haven't learned anything through this but now we're going to look at some of the kind of complexities around this particularly when we're working with controllers.
03:25
So let's say we now have a article controller, so let's go ahead and make out an article controller inside of here and let's go ahead and open this up and let's create a show method in here which will of course show that particular article and we can use root model binding to nicely pull this in.
03:44
You'll probably have a slug passed into the URL but for now I'm just going to use the ID. So I'm not going to return a view here, I'm just going to die dump on the article so we can see this output and of course we want to deny access to any article which isn't
03:58
live in our database and there's a few ways that we can do this. So let's come over to web and create out this root resource very quickly just so we can see the three ways that you might do this and my opinion on the best one. So let's go ahead and say articles and of course use that article controller.
04:15
So let's go and just duplicate this tab over, come over to articles and one and sure enough we get that article even though it is not live. Now the first thing that you might want to do and I'd encourage putting some methods in here just in case is having an if statement inside of your controller.
04:32
Now as soon as I start to put if statements inside of my controller for things that I'm passing through using root model binding, I start to immediately think how can I extract this out so it's a lot easier and more importantly a lot safer to only show this article if it is live because doing this is first of all a little bit messy, it adds too much to your
04:52
controller and we want to keep things nice and clean inside of controllers. So what I'm going to do is I'm going to say article is not live and we're also going to create an is live method as well and the is not live is going to kind of piggyback off of this.
05:07
So if that's the case we're going to go ahead and abort the request with a 404 as we would expect. So let's start things off by just creating the is live method inside of here and these are the only three methods that we're going to have inside of here and of course we just
05:21
need to return this live and compare that to true. Now for is not live, we're not going to compare this to false, what I tend to do is piggyback off of is live. There's one main reason for this.
05:33
If I say is not live in here and I have some logic like false, if I then add another condition to is live, I'm going to have to update this method as well. So what I prefer to do is just have one method that determines if it's live and create a kind of faulty check in here piggybacking off that method that we've just created returning
05:54
whether this is faulty. So then if you had some other condition inside of here, this would still work. So it's a lot easier to maintain your app this way. Okay, so if you go over to the article controller, we're using that and sure enough if we come
06:06
over now and give that a refresh, we see the standard Laravel 404 page, perfect. Now like I said, I don't really like doing this too much. So one suggestion that I would have, this is of course the first way that we could do it.
06:20
The second way that we could do it is by creating a global scope for our article and there's a danger in doing this and I'll explain why that isn't just a second. Let's go ahead and create a static boot method out in here. This will overwrite the base boot method in the model.
06:36
So the first thing that we're going to do is say parent boot and then what we can do is just say add global scope and inside of here, we can either pass a new class or we can pass in an anonymous function. This will give back a builder and then from this of course because we get a builder instance
06:54
which is attached to our article or using our article, we can just use builder live and that will only ever return articles that are live regardless of where we are doing this. So let's go ahead and just pull the builder namespace into here, again you don't need
07:09
to type in that and let's come over to our article controller. Of course this is still not going to work but if we go ahead and get rid of this because we're using root model binding, that 404 will still be displayed. So we've just cleared up our controllers a little bit by adding this global scope.
07:26
Now the only problem with adding a global scope to a model is what happens if then we create an admin panel and we want to see all articles regardless of whether they are live or not. Well in this case if we reuse this model, we're not going to be able to see that.
07:42
So we're going to have to maybe add some kind of if statement in here to see if the current user is an admin and then not do this but things get hugely complicated when we start to do this. So the other method that I like to use is not doing this but creating a piece of middleware
07:59
and this works really nicely. So let's go over and just create some middleware and I'll show you how I do this because this is quite important for readability. Of course you can switch this up if you want.
08:09
What I tend to do is create an is directory inside of middleware and I say is live. So it makes it a lot easier to immediately see when we come over to our middleware directory is live. So I know exactly what this middleware is trying to do.
08:24
Now of course what we can do is if we just die dump on our request and pluck out article that will give us back the article that's being passed in using root model binding. Of course in the case where you're not using root model binding you're going to have to use them is live or is not live check.
08:42
So this only applies to root model binding. So if we just come over to our kernel, let's register this middleware down in our root middleware and let's just go ahead and grab one of these from our app, pull this down into here and I just say is.live and I reference that is live class.
09:01
So what we can now do is over in our article controller we can apply this middleware or you can do this at the root level, it's entirely up to you and I can just inside of here say this middleware because presumably inside of an article controller this will be a client or user facing controller and not an admin one so you would always want to apply the
09:23
is live middleware. So now that we've applied that let's give this a refresh. Sure enough we see that article instance that we are dying and dumping out inside of our middleware but doing this of course is not great because this middleware now can't be
09:35
used for other models that we are using the live aware trait on and the whole point of creating that trait is so we can easily reuse this between models. So what I tend to do is use middleware parameters. So in here I would create a model parameters the third argument inside of our article controller
09:55
we can now use a colon to pass through the model that we want to access and we know that this is article and this reads quite nicely as well, is live article. Of course you can switch this up depending on whether you think that's readable or not but the implementation remains the same.
10:09
So what we can now do is over here we have got that model name so we're just going to say die dump a model let's give that a refresh and sure enough we get the text article. What we can now do is create a statement in here and say request dynamically accessing that model variable and we're going to say is not live.
10:28
So if that is not live of course we're going to go ahead and abort the request with a 404 and sure enough we get the same thing. So there are a ton of different ways that you can do this but I found this really helps a lot particularly when you are creating an admin panel and you don't want to apply that
10:44
is live middleware over in your admin panel because presumably admins will be able to see all articles. So there we go a really simple pattern we've created the live aware trait for any models that are live or not.
10:59
We've gone ahead and checked out the first method of doing this using an if statement inside of here which I don't like at all. Second one is going ahead and applying this global scope which again can be a little bit tricky because you are tying this down to the model itself and the final solution which
11:15
I really prefer is creating a piece of middleware that requires in a parameter and goes ahead and does the relevant check which you can apply really nicely over to your user facing controllers. Thank you.
1 episode 11 mins

Overview

If your models share a 'live' property that you control, why not refactor this down to a single unit of functionality?

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!