Michael Dyrynda
Home Blog Podcasts
 
Eloquent ORM, route model binding, and repositories March 12th, 2015

Eloquent ORM

When most of us started interacting with databases in Laravel, we started using the Eloquent ORM.

The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table.

A simple model might look like this:

1<?php namespace App;
2 
3use Illuminate\Database\Eloquent\Model;
4 
5class Post extends Model {
6 
7 public function scopePublished($query)
8 {
9 return $query->whereNotNull('published_at');
10 }
11 
12}

For a lot of CRUD-based applications, with simple operations and perhaps for several of your applications, using this approach will likely be good enough. You can make the calls directly in your controllers and achieve results easily enough.

1<?php namespace App\Http\Controllers;
2 
3use App\Post;
4 
5class PostsController extends Controller {
6 
7 /**
8 * List all published posts.
9 *
10 * @return Response
11 */
12 public function index()
13 {
14 $posts = Post::published()->get();
15 
16 return view('posts.index', compact('posts'));
17 }
18 
19 /**
20 * Display a specific post
21 *
22 * @param int $id
23 *
24 * @return Response
25 */
26 public function show($id)
27 {
28 $post = Post::find($id);
29 
30 return view('posts.show', compact('post'));
31 }
32 
33}

Route Model Binding

Then you might learn about route model binding. What this allows you to do is bind a parameter in your route - something like {post} in the route /blog/{post} - directly to its corresponding Eloquent model.

1<?php namespace app\Providers;
2 
3use Illuminate\Routing\Router;
4use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
5 
6/**
7 * app/Providers/RouteServiceProvider.php
8 */
9class RouteServiceProvider extends ServiceProvider {
10 
11 // Boilerplate omitted for brevity
12 public function boot(Router $router)
13 {
14 parent::boot($router);
15 
16 $router->model('post', '\App\Post');
17 }
18 
19}

This means that any time you access a route that has a {post} parameter in it, Laravel will perform a find query using the variable as the parameter and inject an instance of App\Post directly into your controller. If a matching record can't be found, you'll get a NotFoundHttpException.

Using route model binding is incredibly powerful and allows you to make your controller methods really lightweight. You know that your show method will always get a valid Post model and if one can't be found, Laravel will render your application's 404 page.

1<?php namespace app\Http\Controllers;
2 
3use App\Post;
4 
5class PostsController extends Controller {
6 
7 /**
8 * Show a single post
9 *
10 * @param Post $post
11 *
12 * @return Response
13 */
14 public function show(Post $post)
15 {
16 return view('posts.show', compact('post'));
17 }
18 
19}

Repositories

One of the more advanced methods of interacting with a database you might come across is the repository pattern. The repository pattern allows you to define your interactions with the underlying persistence layer in a way that keeps your dependency on any given persistence mechanism (database, API) abstracted behind a common interface. You can create methods that are meaningful such as getAllPublishedPosts or getPublishedPostsPaginated. These are easy to understand at a quick glance, without needing to look at exactly how that data is being obtained.

1<?php namespace App\Repositories;
2 
3use App\Post;
4 
5class PostRepository {
6 
7 /**
8 * @var Post
9 */
10 private $model;
11 
12 /**
13 * Instantiate the post repository
14 *
15 * @param Post $model
16 */
17 public function __construct(Post $model)
18 {
19 $this->model = $model
20 }
21 
22 /**
23 * Get all published posts, ordered by most recently published.
24 *
25 * @return Collection
26 */
27 public function getAllPublishedPosts()
28 {
29 return $this->model->published()->latest('published_at')->get();
30 }
31 
32 /**
33 * Get all published posts, ordered by most recently published, and paginated.
34 *
35 * @return Collection
36 */
37 public function getPublishedPostsPaginated($paginate = 10)
38 {
39 return $this->model->published()->latest('published_at')->simplePaginate($paginate);
40 }
41 
42}
1<?php namespace App\Http\Controllers;
2 
3use App\Repositories\PostRepository;
4 
5class PostsController extends Controller {
6 
7 /**
8 * @var PostRepository
9 */
10 protected $postRepository;
11 
12 
13 /**
14 * Instantiate the posts controller
15 *
16 * @param PostRepository $postRepository
17 */
18 public function __construct(PostRepository $postRepository)
19 {
20 $this->postRepository = $postRepository;
21 }
22 
23 /**
24 * Show all posts
25 *
26 * @return Response
27 */
28 public function index()
29 {
30 $posts = $this->postRepository->getPublishedPostsPaginated();
31 
32 return view('posts.index', compact('posts'));
33 }
34 
35}

Conclusion

The question now is; when do I choose the Eloquent ORM, route model binding, or repository pattern?

The answer? It depends! It depends entirely on your use case and what you're comfortable with.

Using the Eloquent ORM directly within your controller methods (or even within route closures) is perfectly acceptable for a lot of smaller CRUD-based applications. Your first todo or blog app probably isn't going to need the full flexibility afforded to you by the repository pattern.

Route model binding is a good way to easily load a given model directly into your route. If all you want it to do is a simple Model::find($id), then using Route::model binding is fine. If it's a simple condition like Model::with('relationship')->where('condition', 'value')->first() you could probably comfortably use Route::bind.

If you find that your bindings are getting large and/or complex, or you're using the same query in multiple places, then it might be time to drop it into a repository method.

You can, of course, use any, all, or a combination of these methods. You may only need a simple Route::model to access your show/edit routes and return a model, but elsewhere in your application, you may need more complex lookups - for example, you might want to getAllPublishedPosts and have some conditions around that to display in your index route.

Before you start implementing the complexities of the repository pattern, ask yourself if the overhead is necessary. Do you need to spend the extra time writing and implementing your repositories where a simple call to the ORM or use of route model binding will suffice?

Remember one thing: try and keep things as simple as possible for as long as possible.

Further reading

I'm a real developer ™
Michael Dyrynda

@michaeldyrynda

I am a software developer specialising in PHP and the Laravel Framework, and a freelancer, blogger, and podcaster by night.

Proudly hosted with Vultr

Syntax highlighting by Torchlight