Dependency injection not working with League\Route and League\Container

冷暖自知 提交于 2019-12-11 06:15:01

问题


I'm building a web application right now and I'm facing a problem with my controller.

I want to send to my controller my League\Plate\Engine (registred in my Container) but I keep having the same error : Argument 3 passed to App\Controller\Main::index() must be an instance of League\Plates\Engine, array given

Here is my files :
dependencies.php

use League\Container\Container;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Yajra\Pdo\Oci8;
use League\Container\ReflectionContainer;

$container = new Container();

// Active auto-wiring
$container->delegate(
    new ReflectionContainer
);

// Others dependencies
// ...

// Views
$container->add('view', function () {
    $templates = new League\Plates\Engine();

    $templates->addFolder('web', __DIR__ . '/templates/views/');
    $templates->addFolder('emails', __DIR__ . '/templates/emails/');

    // Extension
    //$templates->loadExtension(new League\Plates\Extension\Asset('/path/to/public'));
    //$templates->loadExtension(new League\Plates\Extension\URI($_SERVER['PATH_INFO']));

    return $templates;
});

return $container;

routes.php

use League\Route\RouteCollection;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

$route = new RouteCollection($container);

// Page index
$route->get('/', 'App\Controller\Main::index');

// Others routes...

return $route;

Main.php

namespace App\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use League\Plates\Engine;

class Main
{
    public function index(ServerRequestInterface $request, ResponseInterface $response, Engine $templates) {
        //return $response->getBody()->write($this->templates->render('web::home'));
        return $response;
    }
}

Thank you in advance


EDIT
I've made a progress. I extended the Main class to extends the abstract class BaseController which looks like this :

namespace App\Controller;

use League\Plates\Engine;

 class BaseController
{
    protected $templates;

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

The first error goes away, but another one show up. In the Main class, I would like to use the view object that I instanciate in the container, but the object passed to the constructor is an empty one :

Main.php

class Main extends BaseController
{
    public function index(ServerRequestInterface $request, ResponseInterface $response) {
        echo '<pre>'.print_r($this->templates,1).'</pre>'; // Return an empty Plate Engine object
        return $response->getBody()->write($this->templates->render('web::home'));
        //return $response;
    }
}

And this doesn't explain why the first error shows up


EDIT 2
After some digging, I finally make it works, but I sense that something is wrong. I replaced in the container the term view by the namespace of the Engine class :

$container->add('League\Plates\Engine', function () {
    // The same as before
});

In Main.php I've updated the index function like this :

public function index(ServerRequestInterface $request, ResponseInterface $response) {
        $body = $response->getBody();
        $body->write($this->templates->render('web::home'));
        return $response->withBody($body);
    }

And the page doesn't throw a 500 error and the html file is displayed correctly.

But, what if I want to change the template engine by Twig for example ? This would mean I'll need to change all the call to $container->get('League\Plate\Engine'); by $container->get('What\Ever'); ? That's not very practical! I probably missed something! And the problem will rise again when I'll want to use my PDO object... or every other object.


回答1:


Ok so I solved my problem by registering my Controllers classes in the container itself.

For example, for displaying the index page, the Main class call the index function. In my container, I call

$container->add('App\Controller\Main')
    ->withArgument($container->get('view'));

To summary :

bootstap.php (called by index.php)

require __DIR__ . '/../../vendor/autoload.php';

$dotenv = new \Dotenv\Dotenv(__DIR__ . '/../');
$dotenv->load();

$config = new Config(__DIR__ . '/../config/');

$container = require __DIR__ . '/../dependencies.php';

$route = require __DIR__ . '/../routes.php';

$response = $route->dispatch($container->get('request'), $container->get('response'));
$container->get('emitter')->emit($response);

dependencies.php

$container = new Container();

// activate auto-wiring
$container->delegate(
    new ReflectionContainer
);

// Others dependencies...

// Views
$container->add('view', function () {
    $templates = new League\Plates\Engine();

    $templates->addFolder('web', __DIR__ . '/templates/views/');
    $templates->addFolder('emails', __DIR__ . '/templates/emails/');

    // Extension
    //$templates->loadExtension(new League\Plates\Extension\Asset('/path/to/public'));
    //$templates->loadExtension(new League\Plates\Extension\URI($_SERVER['PATH_INFO']));

    return $templates;
});

// THIS IS THE TRICK
$container->add('App\Controller\Main')
    ->withArgument($container->get('view'));
// others controllers...

return $container;

routes.php

$route = new RouteCollection($container);

// Page index
$route->get('/', 'App\Controller\Main::index');

// Others routes...

return $route;

I have no idea why or how this work !




回答2:


This works, because you are registering with the container your controller class, and telling the container to dependency inject your View class in the constructor..

The line you added, here, is doing that:

$container->add('App\Controller\Main') ->withArgument($container->get('view'));

The Hello World documentation example explains it perfectly.

http://container.thephpleague.com/3.x/constructor-injection/



来源:https://stackoverflow.com/questions/44387343/dependency-injection-not-working-with-league-route-and-league-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!