gargi258 gargi258 - 27 days ago 9
PHP Question

DI, ServiceProvider, abstract parent and Laravel 5.3

I have some problem and little misunderstanding Laravel SP (ServiceProvider). I have abstract class Repository and her Interface:

abstract class Repository implements RepositoryInterface {

private $model;
private $parser;

public function __construct() {
$this->model = new $this->model_name();
} }


interface RepositoryInterface {

public function create(array $attributes);
public function update($id, array $attributes);
public function delete($id);

public function all();
public function find($id);
public function filter(array $parameters, $query=null);
public function query(array $parameters, $query=null); }


and some child UserRepository for example:

class UserRepository extends Repository implements UserRepositoryInterface {

protected $model_name = "App\Models\User";

public function __construct() {
parent::__construct();
}

public function activation($user_id) {
return "user";
}

public function deactivation($user_id) {
return "user";
} }


and simple ModelParser class:

class ModelParser {

protected $parameters;
protected $model;

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


This work fine, but I would pass
ModelParser
as DI in my construct of abstract
Repository
with parameter
$model
. I dont have idea. How should I do it ?

I use it like this:

class UserController extends Controller {

private $repository;

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

Answer

Well it's kinda complicated since your ModelParser requires a $model as it's parameter. And because this $model may vary depends on its repository, it will be too complicated if we're trying to resolve it using Laravel service container binding.

There's an easier approach, we can make the ModelParser class's constructor receive an optional $model parameter. Then we can add an additional method to set this $model property like so:

namespace App\Models;

class ModelParser
{
    protected $parameters;
    protected $model;

    // Make $model parameter optional by providing default value.
    public function __construct($model = null) {
        $this->model = $model;
    }

    // Add setter method for $model.
    public function setModel($model)
    {
        $this->model = $model;

        return $this;
    }
}

And now you can inject the ModelParser into your abstract Repository class. Laravel will easily resolve this ModelParser parameter

namespace App\Models;

use App\Models\ModelParser;
use App\Models\RepositoryInterface;

abstract class Repository implements RepositoryInterface
{
    private $model;
    private $parser;

    // Pass ModelParser instance to your constructor!
    public function __construct(ModelParser $parser)
    {
        $this->model = new $this->model_name();

        // Set the parser's model property.
        $this->parser = $parser->setModel($this->model);
    }

    // Rest of your code.
}

And if you're extending the abstract Repository class, you still have to pass this ModelParser to the constructor like so:

namespace App\Models;

use App\Models\ModelParser;
use App\Models\UserRepositoryInterface;

class UserRepository extends Repository implements UserRepositoryInterface
{
    protected $model_name = "App\Models\User";

    public function __construct(ModelParser $parser)
    {
        parent::__construct($parser);
    }
}

Actually, if you're not planning to pass another parameter or perform something else during the class instantiation, you can simply remove the __construct() method from UserRepository and rely on its parent (the abstract Repository).

Hope this help!