loading...

Implement Repository Design Pattern in Laravel

nasterblue profile image Naster Blue ・3 min read

# The Repository Pattern

Use case

  • We have Post resource
  • Need to implement controller to support APIs for these features :

Get all posts
Paginate posts
Find posts with condition
Find post by id
Caching APIs to improve performance

Preparation

  • Post model (database driver can be MySQL/ PostgreSQL/ SQLite/ SQL Server)
<?php  

namespace App;  

use Illuminate\Database\Eloquent\Model;  

class Post extends Model  
{  
  protected $table = 'posts';  
}
  • Contract
<?php

namespace App\Http\Contracts;

interface RepositoryInterface
{
    /**
     * Retrieve all data of repository
     *
     * @param array $columns
     *
     * @return mixed
     */
    public function all($columns = ['*']);

    /**
     * Retrieve all data of repository, paginated
     *
     * @param null $limit
     * @param array $columns
     *
     * @return mixed
     */
    public function paginate($limit = null, $columns = ['*']);

    /**
     * Find data by multiple fields
     *
     * @param array $where
     * @param array $columns
     *
     * @return mixed
     */
    public function findWhere(array $where, $columns = ['*']);

    /**
     * Find data by id
     *
     * @param       $id
     * @param array $columns
     *
     * @return mixed
     */
    public function find($id, $columns = ['*']);

}

Implementation Details

Newbie/Beginner/Fresher/Intern level

<?php

namespace App\Http\Controllers;

use App\Http\Contracts\RepositoryInterface;
use App\Post;

class PostController extends Controller  implements RepositoryInterface
{
    protected $post;

    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    public function all($columns = ['*'])
    {
        return $this->post->all($columns);
    }

    public function paginate($limit = null, $columns = ['*'])
    {
        return $this->post->paginate($limit, $columns);
    }

    public function find($id, $columns = ['*'])
    {
        return $this->post->find($id, $columns);
    }

    public function findWhere(array $where, $columns = ['*'])
    {
        foreach ($where as $field => $value) {
            if (is_array($value)) {
                list($field, $condition, $val) = $value;
                $this->post = $this->post->where($field, $condition, $val);
            } else {
                $this->post = $this->post->where($field, '=', $value);
            }
        }
        return $this->post->get($columns);
    }
}

Database Repository/ Junior Level

<?php

namespace App\Http\Controllers;

use App\Http\Contracts\RepositoryInterface;
use App\Post;

class DbPostRepository implements RepositoryInterface
{
    private $post;

    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    public function all($columns = ['*'])
    {
        return $this->post->all($columns);
    }

    public function paginate($limit = null, $columns = ['*'])
    {
        return $this->post->paginate($limit, $columns);
    }

    public function findWhere(array $where, $columns = ['*'])
    {
        foreach ($where as $field => $value) {
            if (is_array($value)) {
                list($field, $condition, $val) = $value;
                $this->model = $this->post->where($field, $condition, $val);
            } else {
                $this->model = $this->post->where($field, '=', $value);
            }
        }
        return $this->post->get($columns);
    }

    public function find($id, $columns = ['*'])
    {
        return $this->post->find($id, $columns);
    }
}

class DbPostController extends Controller
{
    private $dbPostRepository;

    public function __construct(DbPostRepository $dbPostRepository)
    {
        $this->dbPostRepository = $dbPostRepository;
    }

    public function all($columns = ['*'])
    {
        return $this->dbPostRepository->all($columns);
    }

    public function paginate($limit = null, $columns = ['*'])
    {
        return $this->dbPostRepository->paginate($limit, $columns);
    }

    public function findWhere(array $where, $columns = ['*'])
    {
        return $this->dbPostRepository->findWhere($where, $columns);
    }

    public function find($id, $columns = ['*'])
    {
        return $this->dbPostRepository->find($id, $columns);
    }
}

Caching Repository/ Senior Level

<?php

namespace App\Http\Controllers;

use App\Http\Contracts\RepositoryInterface;
use Illuminate\Contracts\Cache\Repository as CacheRepository;

class CachePostRepository implements RepositoryInterface
{
    private $dbPostRepository;
    private $cacheRepository;

    public function __construct(DbPostRepository $dbPostRepository, CacheRepository $cacheRepository)
    {
        $this->dbPostRepository = $dbPostRepository;
        $this->cacheRepository = $cacheRepository;
    }

    public function getCacheMinutes()
    {
        return config('repository.cache.minutes', 30);
    }

    public function getCacheKey($method, $args = null)
    {
        $request = app('Illuminate\Http\Request');
        $args = serialize($args);
        $criteria = $this->serializeCriteria();
        $key = sprintf('%s@%s-%s', get_called_class(), $method, md5($args . $criteria . $request->fullUrl()));
        return $key;
    }

    public function all($columns = ['*'])
    {
        $key = $this->getCacheKey('all', func_get_args());
        $minutes = $this->getCacheMinutes();
        return $this->cacheRepository->remember($key, $minutes, function () use ($columns) {
            return $this->dbPostRepository->all($columns);
        });
    }

    public function paginate($limit = null, $columns = ['*'])
    {
        $key = $this->getCacheKey('paginate', func_get_args());
        $minutes = $this->getCacheMinutes();
        return $this->cacheRepository->remember($key, $minutes, function () use ($limit, $columns) {
            return $this->dbPostRepository->paginate($limit, $columns);
        });
    }


    public function findWhere(array $where, $columns = ['*'])
    {
        $key = $this->getCacheKey('findWhere', func_get_args());
        $minutes = $this->getCacheMinutes();
        return $this->cacheRepository->remember($key, $minutes, function () use ($where, $columns) {
            return $this->dbPostRepository->findWhere($where, $columns);
        });
    }

    public function find($id, $columns = ['*'])
    {
        $key = $this->getCacheKey('find', func_get_args());
        $minutes = $this->getCacheMinutes();
        return $this->cacheRepository->remember($key, $minutes, function () use ($columns) {
            return $this->dbPostRepository->all($columns);
        });
    }
}

class CachePostController extends Controller
{
    private $cachePostRepository;

    public function __construct(CachePostRepository $cachePostRepository)
    {
        $this->cachePostRepository = $cachePostRepository;
    }

    public function all($columns = ['*'])
    {
        return $this->cachePostRepository->all($columns);
    }

    public function paginate($limit = null, $columns = ['*'])
    {
        return $this->cachePostRepository->paginate($limit, $columns);
    }

    public function findWhere(array $where, $columns = ['*'])
    {
        return $this->cachePostRepository->findWhere($where, $columns);
    }

    public function find($id, $columns = ['*'])
    {
        return $this->cachePostRepository->find($id, $columns);
    }
}

Discussion

pic
Editor guide