Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repository pattern implementation with Laravel

Recently i start to study Laravel 4 and it's capabilities. I want to implement Repository pattern to move model logic there. And at this point i faced with a number of inconvenience or misunderstanding of how to organize it. General question I have goes something like this: is it possible to implement and apply this pattern in Laravel without headaches, and whether it's worth?

The question would be divided into several parts, which caused my confusion.

1) Laravel provides convenient way to bind model as controller parameter, e.g. i do it this way:

// routes.php
Route::bind('article', function($slug)
{
    return Article::where('slug', $slug)->first();
});

Route::get('articles/{article}', 'ArticlesController@getArticle');

// controllers/ArticlesController.php
class ArticlesController extends BaseController {

    public function getArticle(Article $article)
    {
        return View::make('article.show', compact('article'));
    }
}

If I want to use the Repository pattern, then I can't use this approach, since in this case the controller will clearly be aware of the existence of models Article? Whether it will be correct to re-write this example using Repository pattern this way:

// routes.php
Route::get('articles/{slug}', 'ArticlesController@getArticle');

// controllers/ArticlesController.php
class ArticlesController extends BaseController {

    private $article;

    public function __construct(ArticleRepository $article) {
        $this->article = $article;
    }

    public function getArticle($slug)
    {
        $article = $this->article->findBySlug($slug);

        return View::make('article.show', compact('article'));
    }
}

2) Suppose, my code above with the use of Repository is correct. Now I want to increment article views counter each time it will be showed, however, I want to make this processing in the Event. That is, the code is as follows:

// routes.php
Route::get('articles/{slug}', 'ArticlesController@getArticle');

// controllers/ArticlesController.php
class ArticlesController extends BaseController {

    private $article;

    public function __construct(ArticleRepository $article) {
        $this->article = $article;
    }

    public function getArticle($slug)
    {
        $article = $this->article->findBySlug($slug);
        Events::fire('article.shown');

        return View::make('articles.single', compact('article'));
    }
}

// some event subscriber
class ArticleSubscriber {

    public function onShown()
    {
        // why implementation is missed described bellow
    }

    public function subscribe($events)
    {
        $events->listen('article.shown', 'ArticleSubscriber@onShown');
    }

}

At this point I was puzzled again about how to implement event processing. I can't pass $article model directly to event, because, again, it's violates the principles of OOP and my subscriber will know about the existence of article model. So, i can't do so:

// controllers/ArticlesController.php
...
\Events::fire('article.shown', $article);
...

// some event subscriber
...
public function onShown(Article $article)
{
    $article->increment('views');
}
...

On the other hand I don't see any sense to introduce into subscriber repository ArticleRepository (or to inject it in subscriber's contructor), because first I should to find an article, and then update the counter, in the end, i will get extra query (cause previously in constructor i do the same) to the database:

// controllers/ArticlesController.php
...
Events::fire('article.shown', $slug);
...

// some event subscriber
...
private $article;

public function __construct(ArticleRepository $articleRepository)
{
    $this->article = $articleRepository;
}

public function onShown($slug)
{
    $article = $this->articleRepository->findBySlug($slug);
    $article->increment('views');
}
...

Moreover, after the Event handled (i.e. increased views count), it is necessary that the controller knew about the updated model, because in the view i want to display the updated views counter. It turns out that somehow I still need to return a new model from Event, but I would not want to Event has become a common method for processing a particular action (for this there are the repository) and return some value. In addition, you may notice that my last onShow() method again contrary to the rules of Repository pattern, but I don't understand how to put this logic to the repository:

public function onShown($slug)
{
    $article = $this->articleRepository->findBySlug($slug);
    // INCORRECT! because the Event shouldn't know that the model is able to implement Eloquent
    // $article->increment('views');
}

Can I somehow pass the found model back to the repository and to increase her counter (does it contradict this approach to Repository pattern?)? Something like this:

public function onShown($slug)
{
    $article = $this->articleRepository->findBySlug($slug);
    $this->articleRepository->updateViews($article);
}

// ArticleRepository.php
...
public function updateViews(Article $article) {
    $article->increment('views');
}
...

As a result, I will try to formulate all more compact:

  1. I'll have to refuse to pass models directly to controller and other comforts provided by DI, if i'll use Repository pattern?

  2. Is it possible to use the repository for keeping the state of the model and pass it between entities (e.g., from the filter to the controller from the controller to Event and back) avoiding obscene repeated calls to db and is this approach will be correct (model persistence)?

Such things, these are my questions. I would like to hear the answers, thoughts, comments. Maybe, I incorrect approach to apply the pattern? Now it causes more headaches than it solves issue of data mapping.

Also i've read some articles about Repository implementation:

  1. http://heera.it/laravel-repository-pattern#.VFaKu8lIRLe
  2. http://vegibit.com/laravel-repository-pattern

but it doesn't solve my misunderstanding

like image 845
likerRr Avatar asked Nov 04 '14 20:11

likerRr


People also ask

What is repository pattern in Laravel?

A repository can be defined as a layer of abstraction between the domain and data mapping layers, one that provides an avenue of mediation between both, via a collection-like interface for accessing domain objects.

Which design pattern is best in Laravel?

The Builder pattern is one of the main design patterns of Laravel. It separates the construction of a complex object from its representation so that the same construction process can create different representations. A builder is good for creating complex products.

What is difference between model and repository?

What this is saying, is a Model opens access to a database table. It also allows you to relate to other models to pull out data without having to write individual queries. A repository allows you to handle a Model without having to write massive queries inside of a controller.


1 Answers

@likerRr you asked:

Whether it will be correct to re-write this example using Repository pattern this way:

First of all, you should think why do we use Desing Patterns and specifically the Repository Pattern? We use the Repository pattern to implement the SOLID principles (all or few). The first thing is one should not access the data source/database in controllers. Doing this you are:

  1. violating the Single Responsibility Principle (S in SOLID). Your controller must not be aware of the data source. It is only responsible to respond to HTTP Request or meditate b/w your Application and HTTP.
  2. you are violating the Liskov Substitution Principle
  3. you are violating the Dependency Inversion Principle.

So, that's why you should use not only the Repository Patterns but implement the SOLID Principles. how to do then? Wrap your data source access in somewhere else and the Repository is the best place. Suppose you are getting a user using the following code:

User::where('id', $id)->where('company_id', $companyId)->get();

If you write this code in all your controllers where you needed, you can not do the following:

  1. You can not change your data source without changing that line
  2. You can not test your code
  3. and eventually this will be hard to maintain

2: Can I somehow pass the found model back to the repository and to increase her counter (does it contradict this approach to Repository pattern?)

You are doing right in your snippet. Actually, you want to gain both the ease provided by the Laravel and the benefits of Patterns. You probably know that you have to sacrifice something for another thing. Drivers driving on the easy to drive roads can not become good drivers. So, I will suggest you follow the design patterns and SOLID Principles and leave the "ease" provided by the Laravel. Otherwise, this so-called "ease" will create so many troubles for you and you couldn't even maintain your project and all would get vanished.

Last Things about Using Events:

Events are nothing but Observer Pattern. In your case, there seems no need to use the Observer Patterns, so you should avoid it.

The best candidate for the observer patterns/Events would be that you are charging your client and after your successful charge, you want to send the currently charged amount detail along with the previous heavy calculation via email. As this will take time and you don't want to show the user a page reload while all this heavy processing is being done. So you can charge the user, fire an event, redirect the user to your site with a success message and let the event handler do the heavy lifting and your user can do other stuff.

You can ask any other questions if you want to!

like image 67
MKJ Avatar answered Sep 19 '22 10:09

MKJ