12

I am trying to learn the repository pattern, and seem to have got myself a tad confused with how I can use this repository pattern when eager loading relationships and keep db logic out of my controller.

A quick overview of my repository / application structure.

app/
  Acme/
    Repositories/
      RepositoryServiceProvider.php
      Product/
        EloquentProduct.php
        ProductInterface.php
      Category/
        EloquentCategory.php
        CategoryInterface.php

Example ProductInterface.php

<?php namespace GD\Repositories\Product;

interface ProductInterface
{
    public function all();

    public function find($id);

    public function findBySlug($slug);
}

Example CategoryInterface.php

<?php namespace GD\Repositories\Category;

interface CategoryInterface
{
    public function all();

    public function find($id);

    public function findBySlug($slug);
}

Ok, so the easy part is using DI to inject model dependencies into the controller.

listing all categories with associated products is more difficult as I am no longer working with an eloquent model. I am working with an interface which has not exposed all the eloquent methods.

This won't work without me implementing a with method within my EloquentCategory class...

public function show($slug)
{
  return Response::json($this->category->findBySlug($slug)->with('products'), 200);
}

Should I create a separate service class to glue the two repositories together? e.g. allowing the following

public function __construct(ShopService $shop)
{
  $this->shop = $shop;
}

public function show($slug)
{
  return Response::json( $this->shop->getProductsInCategory($slug), 200 );
}

Or alternatively, should I be implementing the with method in my Category Repository?

public function with($relation)
{
  return Category::with($relation);
}

Finally, is my understanding of the usage of the Repository Pattern Correct?

Gravy
  • 12,264
  • 26
  • 124
  • 193

3 Answers3

19

You are over thinking, repository is just a link/bridge between your controller and model and hence controller uses repository class instead of the model directly and in that repository you may declare your methods using the model from there, for example:

<?php namespace GD\Repositories\Category;

interface CategoryInterFace{

    public function all();

    public function getCategoriesWith($with);

    public function find($id);
}

Now implement the interface in the repository class:

<?php namespace GD\Repositories\Category;

use \EloquentCategory as Cat; // the model
class CategoryRepository implements CategoryInterFace {
    public function all()
    {
        return Cat::all();
    }

    public function getCategoriesWith($with)
    {
        return Cat::with($with)->get();
    }

    public function find($id)
    {
        return Cat::find($id):
    }
}

To use it in your controller:

<?php

use GD\Repositories\Category\CategoryInterFace;

class CategoryController extends BaseController {

    public function __construct(CategoryInterFace $category)
    {
        $this->cat = $category;
    }

    public function getCatWith()
    {
        $catsProd = $this->cat->getCategoriesWith('products');
        return $catsProd;
    }

    // use any method from your category
    public function getAll()
    {
        $categories = $this->cat->all();

        return View::make('category.index', compact('categories'));
    }

}

Note: Omitted the IoC binding of the repository because this is not your problem and you know that.

Update: I've written an article here: LARAVEL – USING REPOSITORY PATTERN.

Praveen Srinivasan
  • 1,562
  • 4
  • 25
  • 52
The Alpha
  • 143,660
  • 29
  • 287
  • 307
  • 1
    +1.... i have a tiny bit of question. suppose i am assiginig a model instance via `$this->model = somemodel()` via controller constructor. if i use the repository pattern, i will create interface, model and a service provider to inject. i can easily change the interface. __but__ i can also just change the model in the constructor (as i said above) and use it.... what's the actual benefit of using the interface? (if it is too broad, i will write a question and will hope you will answer it) :) – itachi Feb 16 '14 at 07:00
  • I think you have missed the question... or I really am over thinking. I know how to get `all` from my repository. I have done this already in my example. What I want to know is how to use the IOC to `eager load` when using interfaces. In your answer, how does it give me the ability to do this... `$categories = $this->cat->with('products')->get();` because `with` does not exist in the `CategoryInterface` should I implement that also? or should I create a `glue` repository which uses both? – Gravy Feb 16 '14 at 08:37
  • @itachi - Start a new question and I will answer for you. – Gravy Feb 16 '14 at 08:45
  • @Gravy, You can't use `with` in your repository because it's not declared but you may use it from your repository using `Cat::with('products')->get()` and put it in another method, i.e. `getCatWithProducts()`. Check the updated answer. – The Alpha Feb 16 '14 at 16:06
1

There is a really easy way to do this and it is explored in depth in this link

http://heera.it/laravel-repository-pattern#.U6XhR1cn-f4

I was looking for the exact solution and it is working well so far

so the idea for you would be to declare this in your repository code

public function __construct(\Category $category)
{
    $this->category = $category;
} 
public function getAllUsers()
{
    return $this->category->all();
}

public function __call($method, $args)
{
    return call_user_func_array([$this->category, $method], $args);
}

forcing the Model to be called when some functions are missing

Germain
  • 11
  • 2
  • Thank you for your answer. In this site we do appreciate answers citing their sources. But links alone can be broken later, so could you add at least some elements on the content. – Serge Ballesta Jun 21 '14 at 22:00
0

in UnitController.php controller

<?php

namespace App\Http\Controllers\General;

use App\Contracts\General\UnitInterface;
use App\Traits\ApiResponser;
use App\Http\Controllers\Controller;

class UnitController extends Controller
{
    use ApiResponser;

    protected $unit;

    public function __construct(UnitInterface $unit)
    {
        $this->unit = $unit;
    }

    public function unitList()
    {
        $units = $this->unit->all();
        return $this->set_response(['units' => $units],  200, 'success', ['Unit list']);
    }
    
}

in UnitInterface.php interface

<?php
namespace App\Contracts\General;
interface UnitInterface
{
    public function all();
}

in UnitRepository.php repository

<?php
namespace App\Repositories\General;
use App\Models\General\Unit;
use App\Contracts\General\UnitInterface;

class UnitRepository implements UnitInterface
{
    public function all()
    {
        return Unit::all();
    }
}

in RepositoriesServiceProvider.php a custom service provider binding Interface & Concrete class (Repository)

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;

class RepositoriesServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind('App\Contracts\General\UnitInterface', 'App\Repositories\General\UnitRepository');
    }
}

in app.php

<?php
return [
    'providers' => [
        // Custom Service Providers...
        App\Providers\RepositoriesServiceProvider::class,
    ],
];