Christopher Santos
Christopher Santos

Reputation: 163

How to inject a repository on another repository class in Laravel 5 (5.2)

Let's say I have two repositories (UserRepository, RoleRepository) that implements a contract (UserRepositoryInterface, RoleRepositoryInterface).

What I wanted to do is to inject RoleRepository into UserRepository's constructor method and I've been trying to make this work but I'm getting this error:

Target [App\Contracts\UserRepositoryInterface] is not instantiable while building [App\Http\Controllers\UserController].

Here's what I got so far,

composer.json

"require": {
    "php": ">=5.5.9",
    "laravel/framework": "5.2.*",
    "tymon/jwt-auth": "^0.5.9",
    "zizaco/entrust": "dev-laravel-5"
},
"require-dev": {
    "fzaninotto/faker": "~1.4",
    "mockery/mockery": "^0.9.4",
    "phpunit/phpunit": "~4.0",
    "symfony/css-selector": "2.8.*|3.0.*",
    "symfony/dom-crawler": "2.8.*|3.0.*"
},
"autoload": {
    "classmap": [
        "database"
    ],
    "psr-4": {
        "App\\": "app/",
        "App\\Models\\": "app/Models/",
        "App\\Contracts\\": "app/Contracts/",
        "App\\Repositories\\": "app/Repositories/"
    }
},
"autoload-dev": {
    "classmap": [
        "tests/TestCase.php",
        "tests/MockData.php"
    ]
}

config/app.php

'providers' => [

    /*
     * Laravel Framework Service Providers...
     */
    ...

    /**
     * Third-party Service Providers
     */
    ...

    /*
     * Application Service Providers...
     */
    ...

    /**
     * Custom Service Providers...
     */
    App\Providers\RepositoryServiceProvider::class,

app/Providers/RepositoryServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        $models = [
            'Role',
            'User',
        ];

        foreach ($models as $model) {
            $this->app->bind(
                "App\Contracts\\{$model}Interface",
                "App\Repositories\\{$model}Repository"
            );
        }
    }
}

app/Controllers/UserController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Contracts\UserRepositoryInterface as UserRepository;

class UserController extends Controller
{

    /**
     * @var UserRepository
     */
    private $repo;

    /**
     * Class constructor
     *
     * @param UserRepository $repo
     */
    public function __construct(
        UserRepository $repo
    ) {
        $this->repo = $repo;
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function store(Request $request)
    {
        $data = $request->all();

        $user = $this->repo->store($data);

        return response()->json($user, 201);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function update(Request $request, $id)
    {
        $data = $request->all();

        $user = $this->repo->store($data, $id);

        return response()->json($data, 200);
    }

app/Contracts/RoleRepositoryInterface.php

<?php

namespace App\Contracts;

interface RoleRepositoryInterface
{
    /**
     * Attach role to user
     *
     * @param  static $user
     * @param  string $roleName
     *
     * @return static
     */
    public function role($user, $roleName);

}

app/Contracts/UserRepositoryInterface.php

<?php

namespace App\Contracts;

interface UserRepositoryInterface
{

    /**
     * Create new or update an existing user
     *
     * @param  array  $data
     * @param  int    $id
     *
     * @return bool|int|array
     */
    public function store(array $data, $id = null);
}

app/Repositories/RoleRepository.php

<?php

namespace App\Repositories;

use InvalidArgumentException;

use App\Contracts\RoleRepositoryInterface;
use App\Models\Role;

class RoleRepository extends Role implements RoleRepositoryInterface
{

    /**
     * Attach a role to user
     *
     * @param  static $user
     * @param  string $roleName
     *
     * @return static
     *
     * @throws \InvalidArgumentException
     */
    public function attachRole($user, $roleName)
    {
        if (!$user instanceof App\Models\User::class) {
            throw new InvalidArgumentException("{$user} must be an instance of App\Models\User");
        }

        // find role
        $role = $this->where('name', $roleName)->first();

        // attach role to user
        $user->attachRole($role);

        return $user;
    }
}

app/Repositories/UserRepository.php

<?php

namespace App\Repositories;

use App\Contracts\RoleRepositoryInterface as RoleRepository;
use App\Contracts\UserRepositoryInterface;
use App\Models\User;

class UserRepository extends User implements UserRepositoryInterface
{

    /**
     * @var RoleRepository
     */
    private $roleRepo;

    /**
     * Class constructor
     *
     * @param RoleRepository $roleRepo
     */
    public function __construct(RoleRepository $roleRepo)
    {
        $this->roleRepo = $roleRepo;
    }

    /**
     * Create new or update an existing user
     *
     * @param  array  $data
     * @param  int    $id
     *
     * @return bool|int|static
     */
    public function store(array $data, $id = null)
    {
        if (is_null($id)) {
            $user = $this->create($data);
            $userWithRole = $this->roleRepo->attachRole($user, $data['role']);

            return $user;
        } else {
            return $this->where('id', $id)
              ->update($data);
        }
    }
}

Any answers is greatly appreciated...

Upvotes: 4

Views: 4869

Answers (1)

Next Developer
Next Developer

Reputation: 1229

I think you have an error in your code, specifically in the RepositoryServiceProvider. It should be:

foreach ($models as $model) {
        $this->app->bind(
            "App\Contracts\\{$model}RepositoryInterface",
            "App\Repositories\\{$model}Repository"
        );
}

Note: the error is in the concatenation, you got "App\Contracts\\{$model}Interface", but it should be "App\Contracts\\{$model}RepositoryInterface"

Upvotes: 1

Related Questions