Getting into an Eloquent State of Mind

Tag: Laravel v5.0+

I'm a bit of a bouldering fanatic these days. When I first started about a year ago, I was a typical clumsy beginner; butt hanging out, feet scrambling noisily against the wall, searching blindly for toeholds, grasping and gasping for that handhold just out of reach.

Lately I've noticed a change. Where I used to stretch as far as I could for a handle, I now shift my body and grab it easily. Where I used to see a jug impossibly out of reach, I now reach up with a leg and make a heel hook. Routes that initially took me weeks to conquer I glide up effortlessly as a warm up. I didn't just get stronger; I changed.

What changed? I understand my body differently. Handles can be grasped in different ways. Legs aren't only for climbing straight up the wall like a ladder.

I look at the way I learned (am learning) Eloquent the same way. Being an Active Record pattern, it is easy to look at it as just a way to help write your sql statements. That is like the beginner climber who sees the handles on the wall as just a fancy ladder. It is, in a way, but clearly you'll never get good at it thinking that way.

Over at my Quick Tips newsletter I've been showing a few functions and uses of the Eloquent Model class that I think are both under-utilized and important for refactoring into cleaner code. Being a short weekly email, though, I don't really get to delve deeper than "here's a better way".

Many developers mix Eloquent and the underlying QueryBuilder and never think of the difference between the two. They want to produce sql statements, and use these classes to substitute writing them long-hand or to allow them to make reusable parts. Just like the "fancy ladder" at the climbing gym, however, there's a different way of looking at Eloquent

The ORM isn't just grabbing datarows; it is creating Model instances and populating them. Those are two very different ideas. Look at this, for example:

$user = User::find(1);
dump($user->id);

If there is no matching row in the table, this will throw an error. We want to take the id from row number 1, but there is no row number 1, so how can we get its id? It blows up on us. Compare to this:

$user = User::findOrNew(1);
dump($user->id);

This works fine; $user->id is null.

In the second case, we are saying, "Go get me a Model instance. See if you can populate it with a certain row from the db, but even if you can't, I want the model." I'm not thinking about the database, I'm thinking about my classes. I can clean up my code because I can safely and consistently pass the result into some function or class that is expecting an Eloquent\Model instance and let it deal with the id being populated or not, in the same way I would inject any of my other classes. I'm thinking in objects now.

// thinking about databases
$post = Post::create(['title' => 'The title', 'user_id' => 1]);

// thinking about objects
$post = Post::create(['title' => 'The title']);
User::find(1)->posts()->save($post);

In the second case, I am thinking, "I have a User class instance. It has a Collection of zero-to-many Post instances, to which I want to add this new Post instance".

Admittedly, all of this seems very much like we are over-thinking everything. Most simple examples do. When we start building up more complex code and classes is where we see the benefit; when we are deep inside some class function we have injected a User instance into and we realize we have a collection of posts we can manipulate, we will start to feel like we are "gliding up the wall" in Eloquent.

Hope that helps!

Read it Monday, Use it by Friday!

Laravel Quick Tips Weekly Newsletter. Short, immediately helpful bits you'll use in your own codebase before the next one arrives.

Join us now and get a FREE PDF of the first fix months of Laravel Quick Tips!

No Spam, Unsubscribe Anytime

Contact me