How to access slim4's routeParser with the PHP-DI setup demonstrated in Slim-Skeleton?
Asked Answered
R

1

5

I've set up a new app based on the SlimPHP team's Slim Skeleton application. Inside of my route definitions, I want to be able to access the route parser as described in the Slim4 documentation. So, for example, I'd like to be able to edit the skeleton's app/routes.php file something like this:

    $app->get('/', function (Request $request, Response $response) {
        $routeParser = $app->getRouteCollector()->getRouteParser();  // this doesn't work
        $response->getBody()->write('Hello world! ' . $routeParser->urlFor('something'));
        return $response;
    });

It makes sense that $app->getRouteCollector()->getRouteParser() doesn't work, because $app isn't defined here. But I would think that we'd instead call $this->getRouteCollector()->getRouteParser();, but that gives the error: "Call to undefined method DI\\Container::getRouteCollector()".

It definitely seems that my confusion is about Dependency Injection, which is new for me and not coming naturally to me. I'd honestly love to define the $routeParser variable somewhere else (inside index.php?) so that I could access it in any route definition without having to call $app->getRouteCollector()->getRouteParser() every time. But at the moment I'd settle for anything that worked.

Rudolfrudolfo answered 6/1, 2020 at 22:16 Comment(3)
Does this answer your question? Accessing outside variable using anonymous function as paramsFiliation
@Filiation if it does, it does so in a way that I can't understand well enough to apply to my situation. Also note that the excellent answers from @odan and @Nima don't use use or pass anything by reference, so I don't think that points to the best way.Rudolfrudolfo
To be fair I'm not familiar with the Slim framework, so I'll retract my close vote as I assumed it was similar to a micro-framework such as Silex, where useing $app was common (in your case $app->get('/', function (Request $request, Response $response) use ($app) { ... }). It might not be a recommended way in Slim, can't say for sure.Filiation
H
8

Slim skeleton actually demonstrate an example of what you need to achieve. After creating the App instance in index.php, there is an assignment like this:

// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
$callableResolver = $app->getCallableResolver();

You can do the same:

$routeParser = $app->getRouteCollector()->getRouteParser();

And if you really need this instance of RouteParser to be available inside every route callback, you can put it in dependency container, something like:

$container->set(Slim\Interfaces\RouteParserInterface::class, $routeParser);

Then you can use PHP-DI auto-wiring feature to inject this RouteParser into controller constructor:

use Slim\Interfaces\RouteParserInterface;
class SampleController {
    public function __construct(RouteParserInterface $routeParser) {
        $this->routeParser = $routeParser;
        //...
    }
}

or if you need to call $container->get() inside any of your route callbacks:

$app->get('/', function (Request $request, Response $response) {
    $routeParser = $this->get(Slim\Interfaces\RouteParserInterface::class);
    $response->getBody()->write('Hello world! ' . $routeParser->urlFor('something'));
    return $response;
});
Hackbut answered 7/1, 2020 at 6:11 Comment(3)
I love this answer! It not only solves my problem, it helps me get a better sense of how the whole DI thing works. Thank you!Rudolfrudolfo
Question: if I'm not going to use the auto-wiring thing, it seems I can set a simple variable rather than a class, like this: $container->set('routeParser', $routeParser);. Then I can get that in my route callbacks simply with $this->get('routeParser'). Any downside of doing it this way?Rudolfrudolfo
I can set a simple variable rather than a class...you're not setting a class. In php, MyClass::class is the fully qualified name of that class (i.e containing its full namespace), it is a string. The first parameter to $container->set() should be a string as well, and yes, you can use any name you like (it does not have to be routeParser), but in that case you can't use auto wiring and that is the downside.Hackbut

© 2022 - 2024 — McMap. All rights reserved.