When to step outside of Eloquent's comfort zone
Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.
And this is Aaron.
I feel like there's kind of a pattern in the advice that we give. And it's advice based on our experience and maybe seeing what other people do. But it's this idea of kind of making the most of what the framework gives you but also not being tied in by that. So, I thought today, building on that topic, I could give a specific example we can talk through and some specific areas that this might come up in a project. Sound good?
Yes, let's do it.
You sound so excited, Aaron. I love it. Eloquent, this is the way that we interact with the database in Laravel projects and I think it's actually one of the nice selling features of using a framework is having a handy way to do that. And it's probably the default way I would say it should be of accessing a database but there are some examples, specific examples I can give where I think it's good to not be completely focused on Eloquent and to remember like, "Hey, we can access the database in more direct ways." Like, maybe we can use the DB builder or, in even in rare circumstances, maybe we can write a little bit of SQL and why we might do that.
Okay.
So, the first one I thought of, and this comes up a lot, not like every project, but it comes up enough that it's a pattern that jumps to my mind, which is you're fetching a table of records. I don't know, people. Let's say we're fetching a table of people, users. Okay? But I want to sort those users by a related record, which is like the company they belong to. And there's not a way that I'm aware of to do that in Eloquent so I've seen this tackled other ways where people fetch all the users, then they sort the collection in the controller, and then they pass that to the view and they're not leveraging the database to do some of that work. I don't know, I'm going to tee this up for you, Aaron. I feel like I've been talking a lot. First of all, do you agree that's a scenario we bump into, Eloquent can't do it. And what would you do in that scenario?
Yeah. The idea I guess, you're saying is to sort it by a joined records column?
Yeah, you used a key word there I noticed.
Okay. I'm still trying to follow where you're going so keep going.
All right. The point is, in Eloquent I cannot say $users->orderBy('users.company.name') or something like that. Like, if I could invent a feature that would be something nice to have. I can order by any record or any field on the user's table, I cannot order a Eloquent query result based on a related table. I can eager load those records, but I can't sort by it. Following?
Well, you've kind of said something that I'm not sure I fully understand because I want to make sure that we're still talking about using our tools properly. But it sounded like you're saying that if I do some sort of join or order, I can't use Eloquent at all. I don't follow it.
All right. So, as typical you can't read my mind and I really don't like that about you, Aaron, but let's work with it. So you can, I could start user::whereWhatever(), and then you can do a join based on that. The thing is, as soon as you use that method now I'm no longer in Eloquent land. And what I mean by that is the result I get back is not going to be an Eloquent collection of user models, it's now going to be like a DB builder result that comes back. So, if I really want a collection of users back I can't purely stay in Eloquent land to get that if I want to sort by a related table or relationship on that record. Am I doing a little better explaining it to you now?
Yeah, I think that makes sense. I mean, it's a pretty nuanced thing because now you got my brain going because I'm like, "Hmm, I wonder if there's a way that I could use a sub-select and then..." But I think I follow what you're saying, is there are times when... Let me see if I can rephrase it a different way. It sounds like we're saying start out with the Eloquent model, do the standard things you know. As your requirement gets more complexity, you may find that you have to reach out to something else that's available to us on the Eloquent model. But it takes us down a lower level and then we start working with more generics where maybe we're with the Eloquent builder, I think that's what it's called. But it's kind of like a database builder now and it's a little bit lower level and things like that. Okay, I'm following.
Right. So, you did a nice job of summarizing the point I was trying to make. Which is, this is still a tool the framework gives us, this DB builder. It's not as high-level a tool as Eloquent is, and it might require a little bit more work on our part to use the data it returns. Because I'm not getting any user back, I'm just getting like this DB record back, but it doesn't mean we should rule it out. You probably don't want to reach for it all the time but it's there and ultimately Eloquent is using it under the hood anyways. But it's just another tool in the toolkit when you need to reach for it. That's kind of the point I'm making.
What's nice about it then is you can start out with all of the built-in guardrails and definitions and stuff of the Eloquent model and then just drop down to only the specifics that you need. The alternative, you know, which I think maybe you're kind of leading towards next, was you can actually go directly to like the DB facade or something like that and start writing almost lower-level queries. There's various different ways to do it, but if we're going to not have user models anyway I guess, why would we not just go to that database then, right?
Yeah. I mean, I think that's kind of a call that you make as a team. I don't think we can give one universal answer to that. But to me it's like if I could do 90% of the job with the tool the rest of my application is using, I'm going to do that. I'm not going to say like, "Well, because this whole scenario can't be accomplished with that tool, I'm going to completely eject and do the whole thing differently." So, it's sort of like taking advantage of what you can to the point that you can't.
I think the other thing that we should probably bring up too is there is a giant manual for Eloquent.
There is.
It's a whole section of the manual in Laravel talks about Eloquent and lots of different... I mean, it's pages and pages. And that sounds boring to read, but if you know me I love that sort of thing. But the reason I bring that up is because the other thing is remember there's a whole bunch of guardrails and tools and stuff inside of Eloquent that protect us from a lot of the silly things that we might do. You could argue that if you know what you're doing you don't need that, or sometimes it makes it worse for us. I get that, but for the most part, it's going to protect us from some stuff so we want to make sure we don't maybe go down the wrong route right away. Take some time to read that manual, go on Stack Overflow, ask some people, "Is there an optimal way to accomplish this with Eloquent before I start moving down a level?"
Yeah. And as you were talking it connected to something off air we were discussing, which is if you drop to DB, things like maybe an observer or other things aren't going to fire too. It's like you kind of have to factor that in too. Is it's not just the developer experience of using this tool, but like it could actually affect other functionality in the app so it's an excellent point.
I should say you meant lower your code level to DB not drop a DB.
Yeah, that's an unfortunate choice of words. One more thing I just want to throw out on this topic. Sometimes Eloquent has a very nice expressive way of doing something that, depending on how much data you have or how your database is structured, will not perform well. And I've run into this the hard way, you know through personal experience. And like a specific example I'm thinking of is, there's a really nice whereHas() and you can pass it even additional query parameters in there. Like, "I want to get all the users that have a company where the owner signed up before this date." Like, you can express that in a very eloquent way, but depending on the query it generates it might not perform to your satisfaction. And maybe you can throw an index on it or whatever to make it faster, but sometimes you can't. Like, on a project I'm thinking about we literally couldn't, there were so many records. We rewrote part of that query using the DB builder just for performance reasons. And again, I can just sense what you're going to say, Aaron. But the caveat is, don't go nuts with this, but when you face a real problem just know this a tool in the toolbox to pull out to try to make performance better. That's one option. Things have cycles in life, you know, fashion. Styles come and go, but they kind of come back.
I think I have two of them. I have a bicycle.
All right. Okay. But technology kind of doesn't. Like the only thing I can maybe think of is like vinyl records came back in but that's like a pretty niche thing. But generally speaking, technology marches forward, new things replace old things. But in my family, my kids are obsessed with older technologies. And now some of it like, oh, classic video games. I can get behind that, but floppy disks? Like, my son wanted me to put a floppy disk drive in his computer because he just thought it was cool. And I'm like, "In what universe is a floppy disk cool?"
This is all from people who didn't have to experience the pain of actually using these things for real. Knowing that your college dissertation is on a floppy disk and you have to walk around with two of them because one of them might not work. Well, don't get me started, man.
And this is no exaggeration. My son the other day said, "Dad, do you think dial-up still works?" And I'm like, "We don't even have like copper phone wires in this house. I don't know how you're going to experience dial-up up but it's not going to be here." And just this weird stuff. Like, my 13-year-old son, we were in somebody else's car and it was kind of an older car. And he's like, "You have knobs, this is cool." Like, all of the cars now they have like push button things and it's like digital displays. There was a knob and he thought that was interesting.
Well, I mean, I understand that because I want some... I miss the good old days of being able to reach out and not have to look at the screen when you're driving the speed limit down the road.
Yeah. There were some advantages, I get it. But it's just weird to hear coming from young kids that never experienced it in the first place.
It's nice every once in a while to kind of shake off the cobwebs and get a new start and work on yourself.
And one of the ways you could do that is by joining the Mastering Laravel community. Head over to masteringlaravel.io click on community to see what it's all about.