Search code examples
laraveleloquentdependency-injectionrepository-patternlaravel-10

Laravel 10 Repository Pattern with multiple database connections


TL;DR;

How can I get multiple repositories with different DB connections when using Repository Pattern.

About

Hello, I'm building an API Server using Laravel 10.
I choose Repository Pattern for testing, but struggled with multiple DB connections issue.

What I want

  • Declare multiple repositories in the controller.
    Ex) $user_repo01, $user_repo02, ...
  • On controller's constructor, each repository is assigned with injected interface.
  • Each repository should have different DB connection.
    Ex) $user_repo01 is connected to 'db01' while $user_repo02 is connected to 'db02'

What I did

Added setConnection($connection) into my BaseRepository.
This calls Eloquent Model's setConnection($connection) to change the connection.
Also added getConnectionNames() to retrive connection's name.
On controller's constuctor, get injected interface and assign to repository. Before assigning, changed connection.

Problem

Calling '/user/test' should returns connection names by repository.

  • What I expected is
    {
        'user_repo01': 'db01',
        'user_repo02': 'db02',
        'user_repo03': 'db03',
    }
    
  • But the result is...
    {
        'user_repo01': 'db03',
        'user_repo02': 'db03',
        'user_repo03': 'db03',
    }
    

It seems injected interface is shared with all the repositories.
Not sure why, I just started applying Repository Pattern and I don't know much about it.
What I missed? How can I achieve what I want?

Codes

config/database.php
'connections' => [
    'db01' => [ ... ],
    'db02' => [ ... ],
    'db03' => [ ... ],
]
app/Repositories/Interfaces/BaseRepositoryInterface.php
interface BaseRepositoryInterface
{
    public function setConnection(string $connection);
    public function getConnectionName();
}
app/Repositories/BaseRepository.php
class BaseRepository implements BaseRepositoryInterface
{
    public function __construct(Model $model) {
        $this->model = $model;
    }

    public function setConnection(string $connection) {
        $this->model->setConnection($connection);

        return $this;
    }

    public function getConnectionName() {
        return $this->model->getConnectionName();
    }
}
app/Repositories/Interfaces/UserRepositoryInterface.php
interface UserRepositoryInterface extends BaseRepositoryInterface
{

}
app/Repositories/UserRepository.php
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
    public function __construct(User $model) {
        parent::__construct($model);
    }
}
app/Providers/RepositoryServiceProvider.php
class RepositoryServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
    }
}
app/Http/Controllers/UserController.php
class UserController extends Controller
{
    private $user_repo01;
    private $user_repo02;
    private $user_repo03;

    public function __construct(UserRepositoryInterface $user_repo) {
        $this->user_repo01 = $user_repo->setConnection('db01');
        $this->user_repo02 = $user_repo->setConnection('db02');
        $this->user_repo03 = $user_repo->setConnection('db03');
    }

    /**
     * /user/test
     */
    public function test() {
        return [
            'user_repo01' => $this->user_repo01->getConnectionName(),
            'user_repo02' => $this->user_repo02->getConnectionName(),
            'user_repo03' => $this->user_repo03->getConnectionName(),
        ];
    }

Solution

  • Self answered.
    Changed to returning instance via resolve(), also separated getInstance() from setConnection()

    app/Repositories/Interfaces/BaseRepositoryInterface.php

    Interface BaseRepositoryInterface
    {
        public function getInstance(?string $connection = null): self;
        public function setConnection(string $connection);
        public function getConnectionName(): ?string;
    }
    

    app/Repositories/BaseRepository.php

    class BaseRepository implements BaseRepositoryInterface
    {
        public function __construct(Model $model) {
            $this->model = $model;
        }
    
        public function getInstance(?string $connection = null): self {
            $new_instance = resolve($this::class);
    
            if($connection)
                $new_instance->setConnection($connection);
    
            return $new_instance;
        }
    
        public function setConnection(string $connection) {
            $this->model->setConnection($connection);
        }
    
        public function getConnectionName(): ?string {
            return $this->model->getConnectionName();
        }
    }
    

    app/Http/Constrollers/UserController.php

    class UserController extends Controller
    {
        private $user_repo01;
        private $user_repo02;
        private $user_repo03;
    
        public function __construct(UserRepositoryInterface $user_repo) {
            $this->user_repo01 = $user_repo->getInstance('db01');
            $this->user_repo02 = $user_repo->getInstance('db02');
            $this->user_repo03 = $user_repo->getInstance('db03');
        }
    
        /**
         * /user/test
         */
        public function test() {
            return [
                'user_repo01' => $this->user_repo01->getConnectionName(),
                'user_repo02' => $this->user_repo02->getConnectionName(),
                'user_repo03' => $this->user_repo03->getConnectionName(),
            ];
        }
    

    Result

    {
        'user_repo01': 'db01',
        'user_repo02': 'db02',
        'user_repo03': 'db03',
    }