slimphp / Slim-Skeleton

Slim Framework 4 Skeleton Application
http://www.slimframework.com
MIT License
1.59k stars 479 forks source link

[Question] How To Get Route URI from Name #145

Closed eskies closed 4 years ago

eskies commented 4 years ago

Hello, I have some route defined like this

    $app->get('/login', \App\Controller\Homepage\Homepage::class.':loginpage')->setName('login-page');
    $app->post('/login', \App\Controller\Homepage\Homepage::class.':loginpost')->setName('login-post');

    $app->group('/init', function (Group $group) {
        $group->get('/database', \App\Migration\MigrationRunner::class.":createDB")->setName('init-database');
        $group->get('/sysadmin', \App\Migration\UserRunner::class.":createSysadmin")->setName('init-sysadmin');
        $group->get('/sysprm', \App\Migration\UserRunner::class.":createSysPermission")->setName('init-sysprm');
    });

    $app->get('/', \App\Controller\Homepage\Homepage::class.':homepage')->setName('homepage')->add(CustomMiddleware::class.":cekLogin");

How to get URI route base on setName()? I want to get it from Controller. trying to use it like slim v3

$this->router->pathFor('homepage')

But it says Undefined property

l0gicgate commented 4 years ago

Use the route context object.

<?php
user Slim\Routing\RouteContext;

$app->get('/', function ($request, $response) {
    $routeContext = RouteContext::fromRequest($request);
    $routeParser = $routeContext->getRouteParser();
    $relativeUrl = $routeParser->relativeUrlFor('homepage');
    $url = $routeParser->urlFor('homepage');
    $fullUrl = $routeParser->fullUrlFor('homepage');
});

https://github.com/slimphp/Slim/blob/4.x/Slim/Routing/RouteParser.php

eskies commented 4 years ago

I've tried and got an error like this:

Type: RuntimeException
Code: 0
Message: Cannot create RouteContext before routing has been completed
File: C:\Users\eskie\buildup\vendor\slim\slim\Slim\Routing\RouteContext.php
Line: 31

I put code on my controller:

namespace App\Controller;

use Psr\Container\ContainerInterface;

use Psr\Log\LoggerInterface as Logger;
use Illuminate\Database\Capsule\Manager as Database;
use League\Plates\Engine as PlatesEngine;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

use Slim\Routing\RouteContext;

class Controller
{
  ...
  public function getURLRoute(Request $request, string $name, array $datakey = array(), array $dataget = array()) {
    $routeContext = RouteContext::fromRequest($request);
    $routeParser = $routeContext->getRouteParser();
    $url = $routeParser->urlFor($name, $datakey, $dataget);
    return $url;
  }
}

And call it with extends controller class.

namespace App\Controller\Homepage;
use App\Controller\Controller;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class Homepage extends Controller {

  public function loginpage(Request $request, Response $response, Array $args)
  {
    $data['loginaction'] = $this->getURLRoute($request, "login-post");
    $this->renderPage($response, "user/login", $data);
    return $response;
  }

}

OOT: is it possible to put $app into a container? so I could do more flexible. I've tried using $GLOBALS PHP variable, it works, like this In index.php

...
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();

// Register routes
$routes = require __DIR__ . '/../src/Loader/routes.php';
$routes($app);

//Enable error report
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

//put globals
$GLOBALS['app'] = $app;

$app->run();

In controller class:

public function getURLRoute(string $name, array $datakey = array(), array $dataget = array()) {
    return $GLOBALS['app']->getRouteCollector()->getRouteParser()->urlFor($name, $datakey, $dataget);
  }

But it seems not right using a global variables like that.

eskies commented 4 years ago

After several times, I can inject the $app into container by adding set right before $app->run()

$container->set('app', function () use ($app) {
    return $app;
});
$app->run();

and just get it like other dependency. I still don't know the downside using this method. I think I'll stick to this for a while

l0gicgate commented 4 years ago

Message: Cannot create RouteContext before routing has been completed

That's because the order of the middleware in which you are adding the RoutingMiddleware is wrong. It should be added right before ErrorMiddleware

This will effectively perform routing before getting to

// Add your other middleware here before

// These two should be added last so they get executed first
$app->addRoutingMiddleware();
$app->addErrorMiddleware(true, true, true);