Implementing the Repository Pattern with CakePHP Implementing the Repository Pattern with CakePHP

I must admit, my recent articles are becoming a bit obsessed around the repository pattern.� What can I say, I like it, it�s useful, and it�s not restrictive based on a language or a framework.

I�ve long professed how I dislike convoluted controllers.� CakePHP�s find method almost immediately causes this when used inside a controller.� More importantly, the code inside the find method is extremely unreadable.� This is almost more important than a large controller function!

This is where the repository pattern comes in.� At its most basic example (which some will consider overkill � you know who you are), I still think the repository pattern is clearer.

Here is an example using the regular find approach:


$user = $this->User->find('first', array('conditions' => array('id' => $id)));

Compared to a repository example:


$user = $this->UserRepository->GetById($id);

The code is almost identically; however, in the second example, it�s clear that if I were to �read� the code I am retrieving a user by id opposed to I�m finding the first user with the conditions of id being equal to the variable $id.

So if you are sold, let�s continue with a full suite example�


I must confess, I have been doing a lot of C# programming recently and some heavy unit testing.� With this I am going to bring along the interfacing techniques that I have been using with it.

Before you start following along, be sure you have a working CakePHP application up-and-running.

The full source code is available on GitHub: https://github.com/endyourif/CakePHPRepoTest/

At the root of our repository we need a model.� Here is a basic Model/UserModel.php:


<?php
App::uses('AppModel', 'Model');
class UserModel extends AppModel {
    public $name = 'User';
}

With the model out of the way, let�s start with the actual repository files.� Begin by creating a Repository folder in the root of your App folder.� I like to place my interfaces in the root folder and create a secondary folder called Impl inside the Repository folder that will contain my actual repository classes.� This helps keeps my folders are bit smaller, especially if you have 10 or 15 repositories.

Inside the Repository folder I have created to interfaces: IRepo and IUserRepo:


<?php
interface IRepo {
    public function GetById($id);
    public function GetAll();
}
interface IUserRepo {
    public function GetByEmail($email);
}

It�s important to note that you do not necessarily need an Interface for each repository you create, it�s only really necessary if your repository has additional functions than the IRepo.

Next, I�ve created a BaseRepo inside the Impl folder that implements the IRepo interface:


<?php
App::uses('IRepo', 'Repository');
abstract class BaseRepo implements IRepo {
    protected $Model;
    public function __construct($model) {
        $this->Model = $model;
    }
    public function GetById($id) {
        return $this->Model->find('first', array('conditions' => array('id' => $id)));
    }
    public function GetAll() {
        return $this->Model->find('all');
    }
}

I�ve made this an abstract class as it should not be instantiated directly.

It�s important to notice that my GetById function performs the exact same find function that I mentioned in the introduction.� This code has to exist somewhere and I would much prefer it inside this lower layer that I don�t need to visit often.

And finally, to complete the repository I created a UserRepo class inside of the Impl directory that implements the IUserRepo:


<?php
App::uses('BaseRepo', 'Repository/Impl');
App::uses('IUserRepo', 'Repository');
class UserRepo extends BaseRepo implements IUserRepo {
    public function GetByEmail($email) {
        return $this->Model->find('first', array('conditions' => array('email' => $email)));
    }
}

The repository layer is now completed at its most basic level.� Prior to using this code in production, it would be a good idea to add some type checking; otherwise, you might get some obvious SQL errors.� E.g. the GetById function should perform some basic checking to ensure $id is in fact a number.

The final piece to the puzzle is to implement the repository in a controller.� Here is a basic UsersController that I created inside of the Controller folder that executes the three repository functions:


<?php
App::uses('AppController' , 'Controller');
App::uses('UserRepo', 'Repository/Impl');
class UsersController extends AppController {
    var $UserRepository;
    public function __construct($request = null, $response = null) {
        parent::__construct($request, $response);
        $this->UserRepository = new UserRepo($this->User);
    }
    public function index() {
        $users = $this->UserRepository->GetAll();
        debug($users);
        $this->render(false);
    }
    public function view($id) {
        $user = $this->UserRepository->GetById($id);
        debug($user);
        $this->render(false);
    }
    public function find($email) {
        $user = $this->UserRepository->GetByEmail($email);
        debug($user);
        $this->render(false);
    }
}

I�ve overloaded the constructor and instantiated the UserRepository variable passing in the reference to $this->User.

If you wish to unit test this controller, you may wish to update the constructor to accept a third parameter that would default to a new instance of UserRepo.

Summary

I hope you enjoyed this simple example to get started with the repository layer using CakePHP.� If you like it, be sure to share it with your friends and scoff at them for not using it before!

I�ve placed the full source code on GitHub: https://github.com/endyourif/CakePHPRepoTest/

Published on Jun 3, 2013

Tags: CakePHP Tutorial | PHP | repository

Related Posts

Did you enjoy this article? If you did here are some more articles that I thought you will enjoy as they are very similar to the article that you just finished reading.

Tutorials

Learn how to code in HTML, CSS, JavaScript, Python, Ruby, PHP, Java, C#, SQL, and more.

No matter the programming language you're looking to learn, I've hopefully compiled an incredible set of tutorials for you to learn; whether you are beginner or an expert, there is something for everyone to learn. Each topic I go in-depth and provide many examples throughout. I can't wait for you to dig in and improve your skillset with any of the tutorials below.