fbpx
Hero Illustration
0 Comments
Bali, Mitrais, PHP, Software Development

Repository Pattern Implementation in PHP 

“Let’s just focus on making the app work. It’s not worth the hassle of using a pattern, as it is time-consuming.”

Sometimes our clients have no idea how an app is programmed. They want it to run smoothly and align with their system. For example, imagine if we have successfully created an app to obtain data categories with class and functions to retrieve data.

class CategoryController
{
    public function index()
    {
        $category = 'select * from category';
        return $category;
    }
}

Everything runs perfectly smooth, with no bugs, and our client is pleased.

If the client wants to develop larger apps in the future, we must deal with a growing team, different types of people, and different ways of writing code. For example, if the client asks us to retrieve the data product and category, a different programmer might create a product class and implement the requested code.

class ProductController
{
    public function index()
    {
        $category = 'select * from category';
        $product = 'select * from product';
        return json_encode([
            'category' => $category,
            'product' => $product
        ]);
    }
}

There’s nothing wrong with the code above. However, the client sometimes asks to pick up the ten most frequently visited data categories, so your query will need an “order by.” Then, each programmer modifies the code as requested, and the code is changed as seen below:

class CategoryController
{
    public function index()
    {
        $category = 'select * from category order by visited_counter DESC limit 10';
        return $category;
    }
}  

class ProductController
{
    public function index()
    {
        $category = 'select * from category order by visited_counter DESC limit 10';
        $product = 'select * from product';
        $data = [
            'category' => $category,
            'product' => $product
        ];
        return json_encode($data);
    }
}

By looking at the above code, it is evident that something needs improvement, which enables all programmers involved to synergize without changing their code individually. Indeed, it looks simple, as it only replaces two functions. However, it would have been problematic if it had been implemented on many other pages. Therefore, we need a new approach to solve this issue. One of which is using a repository pattern.

What is Repository Pattern?

According to Edward Hieatt and Rob Mee, a repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. A more straightforward way includes creating one layer rather than writing the function at the controller right away.

Somebody might execute the one-layer implementation for the above case by creating a specific function, class, or interface to retrieve the above data category. So, let’s see how we make a function to retrieve the data.

lass CategoryRepository{

    public function getAll()
    {
        return 'select * from category order by visited_counter DESC limit 10';
    }
}

class ProductRepository{

    public function getAll()
    {
        return 'select * from product';
    }
}

Then, here is how we implement it in our controller.

class CategoryController
{
    public function index()
    {
        $category = new CategoryRepository();
        return $category->getAll();
    }
}  

class ProductController
{
    public function index()
    {
        $category = new CategoryRepository();
        $data_category = $category->getAll();

        $product = new ProductRepository();
        $data_product = $product->getAll();
       
        $data = [
            'category' => $data_category,
            'product' => $data_product
        ];

        return json_encode($data);
    }
}

We have implemented a repository pattern when creating a function to retrieve data categories, but it still leaves an issue. Since we are using a new Class() to call the repository, the controller class is based on the repository class. It could potentially lead to a new problem when a client wants to change the data source from the database to Cache.

class CategoryRepository
{
    public function getAll()
    {
        return 'select * from category order by visited_counter DESC limit 10';
    }

    public function getAllByCache(){
        if(Cache::has('category')){
            return Cache::get('category');
        }

        $category = $this->getAll();
        Cache::set('category', $category);
        return $category;
    }
}
class ProductController
{
    public function index($id)
    {
        $category = new CategoryRepository();
        $data_category = $category->getAllByCache();

        $category = new ProductRepository();
        $data_product = $product->getAll();
       
        $data = [
            'product' => $data_product,
            'category' => $data_category
        ];

        return json_encode($data);
    }
}

In fact, for every controller page that needs changes, we still have to alter the functions. As for the above case code, we have to change getAll to getAllByCache. So, maintaining the application with too many unorganized codes will be challenging.

We usually implement a repository pattern with the help of an interface as an innovative way to overcome the problem. The interface can reduce the dependencies between classes and push the implemented class to apply the same function as the interface.

This innovative way will be excellent for coding standardization. Therefore, other programmers who might want to retrieve data categories can see the function in the interface without checking how the category is retrieved. Instead of changing the getAll function into getAllByCache, creating a new class to retrieve data from the cache would be better. For example:

interface getCategoryInterface{
     public function getAll();
}

class CategoryRepository implements getCategoryInterface{

    /**
     * @override
     * @return void
     */
    public function getAll()
    {
        return 'select * from category order by visited_counter DESC limit 10';
    }
}

class CategoryCacheRepository implements getCategoryInterface{

    /**
     * @override
     * @return void
     */
    public function getAll()
    {
        if(Cache::has('category')){
            return Cache::get('category');
        }

        $category = $this->getAll();
        Cache::set('category', $category);

        return $category;
    }
}


class ProductController
{
    private $repository;

    public function __construct(getCategoryInterface $repository)
    {
        $this->repository = $repository;    
    }

    public function index()
    {
        $data_category = $this->repository->getAll();

        $product = new ProductRepository();
        $data_product = $product->getAll();
       
        $data = [
            'product' => $data_product,
            'category' => $data_category
        ];

        return json_encode($data);
    }
}

From the code above, we can see that the class implementing the getInterface; will have a similar function as the interface

If we don’t create a new function but make a new class with a different implementation, as seen in the above case, we need to generate categoryrepository and CategoryCacheRepository. The question is, how do we choose the class and implement it?

$category = new CategoryRepository();
 $product = new ProductController($category);
 echo $product->index();

With the above method, when we want to call the data with cache category, we will only need to change the repository class to choose. See the example below:

$category = new CategoryCacheRepository();
$product = new ProductController($category);
echo $product->index();

We do not change the function one by one on the controller, but we create a new class to implement the new function.

To conclude, with repository patterns, we can make codes more maintainable.

Reference

https://martinfowler.com/eaaCatalog/repository.html

Author: Itok Toni Laksono, Software Engineer – Analyst Programmer

Contact us to learn more!

Please complete the brief information below and we will follow up shortly.

    ** All fields are required
    Leave a comment