#Is it possible to use method injection in a controller

23 messages · Page 1 of 1 (latest)

thorny junco
#

In my AppServiceProvider Register function I bind my interface as such

$this->app->bind(MyModelInterface::class, MyModel::class);

And in my resource controller access it like this

MyModelController {
   ...

   public function show(MyModelInterface $myModel){
       return $myModel;
   }
}

I get an empty array (it simply does not resolve). Is there a way to allow the container to inject the right instance here? Is it bad practice? What are the recommended way if not.

I already tried using the constructor instead for the injection and still end up with the same result []

strong current
#

Is MyModelInterface an actual php interface ?

thorny junco
#

Yes it is. It resolves anywhere else in my application properly given I call app(MyModelInterface::class) I get the right instance

worn wedge
strong current
#

service provider?

thorny junco
#

@worn wedge It is a resource route bound to a controller so it has to receive the Model in it's show, update or delete functions. Since it is in a package, the user implementing the package in a project could override MyModel using the same interface hence the attempt at injecting the right type. You could replace AppServiceProvider by MyPackagerServiceProvider which is doing the bindings if the client has overriden that model.

strong current
#

Oh i get what you meant @worn wedge

#

Hmm i think you're getting some concepts mixed up here

worn wedge
#

That‘s never going to work, basically.

thorny junco
#

Ok so ServiceProviders appart. Let's say my controller should receive my package model as a resource for show,update,delete but the user could override that model using the same interface. How could I use my package controller without the user having to create a new controller? There is no way I can inject there based on the interface only and expect it to be resolved?

strong current
#

It wouldn't know which implementation to resolve

#

there could be a bunch of classes that implement that interface

thorny junco
#

Hence the binding?

#

In the service container. Probly where I'm mixed up sorry

strong current
#

Hmm i see what you're going for

worn wedge
#

I think a better approach would be to use a repository or service class. You can still let the user define what model class to use (similar to Cashier), but then your repository/service class does the actual fetching:

class FooController extends Controller
{
    protected $foos;

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

    public function show(string $id)
    {
        $foo = $this->foos->retrieve($id);

        // Return response...
    }

    public function store(StoreFooRequest $request)
    {
        $foo = $this->foos->create($request->validated());

        // Return response...
    }

    public function update(UpdateFooRequest $request, string $id)
    {
        $foo = $this->foos->retrieve($id);

        $this->foos->update($foo, $request->validated());

        // Return response...
    }

    public function destroy(string $id)
    {
        $foo = $this->foos->find($id);

        $this->foos->delete($foo);

        // Return response...
    }
}
class FooRepository
{
    public function retrieve(string $id): FooInterface
    {
        try {
            return (YourPackage::$fooModel)->findOrFail($id);
        } catch (ModelNotFoundException $e) {
            throw new FooNotFoundException($e->getMessage(), $e);
        }
    }

    public function create(array $data): FooInterface
    {
        return (YourPackage::$fooModel)->create($data);
    }

    public function update(FooInterface $foo, array $data): FooInterface
    {
        $foo->update($data);

        return $foo->fresh();
    }

    public function delete(FooInterface $foo): bool
    {
        return $foo->delete();
    }
}

Then the package user can define what model they want to use in place of FooInterface

thorny junco
#

Ok this is actually quite the refactor based on all the controllers already resolving to the initial instance but I guess this is the right way to do it.

#

So I understand it doesn't work but not exactly why it doesn't work.

When attempting to resolve in the controller function, it simply resolves usually because it is explicitly defined as MyModel?

MyModelController{
   public function show(MyModel $myModel){
   }
}

But it will not check in the service container for any bindings if I specify an interface instead.

worn wedge
thorny junco
#

So in my boot function using Route::model('myModel', MyModelInferface::class) could work around this? (even though I suspect this is bad practice as well just trying to understand)

thorny junco
#

Alright thanks for your time @worn wedge @strong current