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:

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model {

    public function scopePublished($query)
    {
        return $query->whereNotNull('published_at');
    }

}

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.

<?php namespace App\Http\Controllers;

use App\Post;

class PostsController extends Controller {

    /**
     * List all published posts.
     *
     * @return Response
     */
    public function index()
    {
        $posts = Post::published()->get();

        return view('posts.index', compact('posts'));
    }

    /**
     * Display a specific post
     *
     * @param int $id
     *
     * @return Response
     */
    public function show($id)
    {
        $post = Post::find($id);

        return view('posts.show', compact('post'));
    }

}

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.

<?php namespace app\Providers;

use Illuminate\Routing\Router;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

/**
 * app/Providers/RouteServiceProvider.php
 */
class RouteServiceProvider extends ServiceProvider {

    // Boilerplate omitted for brevity
    public function boot(Router $router)
    {
        parent::boot($router);

        $router->model('post', '\App\Post');
    }

}

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.

<?php namespace app\Http\Controllers;

use App\Post;

class PostsController extends Controller {

    /**
     * Show a single post
     *
     * @param Post $post
     *
     * @return Response
     */
    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }

}

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.

<?php namespace App\Repositories;

use App\Post;

class PostRepository {

    /**
     * @var Post
     */
    private $model;

    /**
     * Instantiate the post repository
     *
     * @param Post $model
     */
    public function __construct(Post $model)
    {
        $this->model = $model
    }

    /**
     * Get all published posts, ordered by most recently published.
     *
     * @return Collection
     */
    public function getAllPublishedPosts()
    {
        return $this->model->published()->latest('published_at')->get();
    }

    /**
     * Get all published posts, ordered by most recently published, and paginated.
     *
     * @return Collection
     */
    public function getPublishedPostsPaginated($paginate = 10)
    {
        return $this->model->published()->latest('published_at')->simplePaginate($paginate);
    }

}
<?php namespace App\Http\Controllers;

use App\Repositories\PostRepository;

class PostsController extends Controller {

    /**
     * @var PostRepository
     */
    protected $postRepository;

    /**
     * Instantiate the posts controller
     *
     * @param PostRepository $postRepository
     */
    public function __construct(PostRepository $postRepository)
    {
        $this->postRepository = $postRepository;
    }

    /**
     * Show all posts
     *
     * @return Response
     */
    public function index()
    {
        $posts = $this->postRepository->getPublishedPostsPaginated();

        return view('posts.index', compact('posts'));
    }

}

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