For me is very annoying because I have more than 30 routes and create in same place and one by one calling method is very disorganized.
In other frameworks like Silex I can create routes in Controller Constructors:
$app->group('/v1', function () {
$this->group('/auth', 'App\controllers\AuthController');
});
class AuthController {
public function __construct( $app ) {
$app->map(['GET','POST'], '/login/', 'App\controllers\AuthController:login');
$app->map(['GET','POST'], '/logout/', 'App\controllers\AuthController:logout');
}
}
$container['App\controllers\AuthController'] = function($c) use ($app) {
return new App\controllers\AuthController( $app );
}
Something like this will help to organize routes inside the controllers and the groups in a separate file.
I tried this but not work for me. Is something similar posible?
You could do that by passing the Slim application instance into the controller and declare some routes in there. But I wouldn’t recommend that, since this means that the controller has to be initialized even if its not gonna be used.
Or maybe you can put the route registration in a static method of the controller. This will still include the file, but not create an instance.
$app = new App();
new Controller($app);
// or within a group
$app->group('/v1', function () {
new Controller($this);
});
class Controller {
public function __construct($app) {
// $app->get(...);
}
}
// or static
Controller::registerRoutes($app);
class Controller {
public static function registerRoutes($app) {
// $app->get(...);
}
}
As @JoeBengalen says, putting route definitions in controller constructors isn’t very efficient as you end up instantiating objects that are then not used. However, if that’s what you want to do, this is how you do it:
$app->group('/v1', function () {
$this->group('/auth', function() {
new App\Controllers\AuthController($this);
});
$this->group('/data', function() {
new App\Controllers\DataController($this);
});
});
with a controller looking like this:
class AuthController
{
public function __construct($app)
{
$app->map(['GET','POST'], '/login/', [$this, 'login']);
$app->map(['GET','POST'], '/logout/', [$this, 'logout']);
}
public function login($request, $response, $args) {
// do stuff & return $response
}
public function logout($request, $response, $args) {
// do stuff & return $response
}
}
When you say it doesn’t work, what error are you getting?
In my topic “Middleware dynamic loading - code consultation appreciated” (I wonder why nobody has even a word of comment) I have developed more conventional approach for Slim with routing and controllers calling - works fine even for HMVC pattern that I prefere - meaning separate controllers for main sections of a page or modals (I hate templates )
I was trying more modern approach and i think that not work using containers (that’s what i’m trying):
$app->group('/v1', function () {
$this->group('/auth', 'App\controllers\AuthController');
});
$container['App\controllers\AuthController'] = function ($c) use ($app) {
return new \App\controllers\AuthController($app);
};
This don’t work for me.
Creating routes in controllers ensure to me that i’m not adding more code/routes to memory and this are a little bit more efficient. If you don’t access route, the controller aren’t created in memory and the rest of the routes either.
/*** CONTAINER ****/
$container['App\controllers\CountriesController'] = function ($c) use ($app) {
// logger will be removed soon because $app have container
// but helps to explain my problem
return new \App\controllers\CountriesController($app, $c->get('logger'));
};
/*** ROUTES ****/
$app->group('/v1/{lang}', function () {
$this->group('/countries', 'App\controllers\CountriesController');
});
/*** CONTROLLERS OR ACTION ****/
namespace App\controllers;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class CountriesController extends BaseController {
public function __construct( $app, LoggerInterface $logger )
{
parent::__construct( $app, $logger );
$app->get('', [$this, 'getCountries']);
$app->get('/tryme', [$this, 'getCountries']);
}
public function getCountries(Request $request, Response $response, $args) {
$response->withJson('200', ['success' => true]);
}
}
//basecontroller
namespace App\controllers;
use Psr\Log\LoggerInterface;
abstract class BaseController
{
/**
* @var $app \Slim\App
*/
protected $app;
/**
* @var $logger \Psr\Log\LoggerInterface
*/
protected $logger;
public function __construct( $app, LoggerInterface $logger )
{
$this->app = $app;
$this->logger = $logger;
}
}
And Errors:
2016/04/13 15:39:29 [error] 30307#0: *355 FastCGI sent in stderr: “PHP message: PHP Catchable fatal error: Argument 2 passed to App\controllers\CountriesController::__construct() must be an instance of App\controllers\LoggerInterface, instance of Monolog\Logger given, called in /var/sites/xxxxx/app/dependencies.php on line 82 and defined in /var/sites/xxxxx/app/src/controllers/CountriesController.php on line 15” while reading response header from upstream, client: 192.168.56.1, server: xxxxx, request: “GET /v1/es/countries/tryme HTTP/1.1”, upstream: “fastcgi://unix:/var/run/php5-fpm.sock:”, host: “xxxxx”
2016/04/13 15:39:34 [error] 30307#0: *355 FastCGI sent in stderr: “PHP message: PHP Catchable fatal error: Argument 2 passed to App\controllers\CountriesController::__construct() must be an instance of App\controllers\LoggerInterface, instance of Monolog\Logger given, called in /var/sites/xxxxx/app/dependencies.php on line 82 and defined in /var/sites/xxxxx/app/src/controllers/CountriesController.php on line 15” while reading response header from upstream, client: 192.168.56.1, server: xxxxx, request: “GET /v1/es/countries HTTP/1.1”, upstream: “fastcgi://unix:/var/run/php5-fpm.sock:”, host: “xxxxx”
Add use Psr\Log\LoggerInterface; to the CountriesController.
That will solve the error you have. But it will not make your routing work as you intended, because those wont get registered without actually instantiating the CountriesController.
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
public function __invoke(Request $request, Response $response, $args) { //line 25
$response->withJson('200', ['success' => true]);
}
2016/04/13 16:31:39 [error] 30307#0: *393 FastCGI sent in stderr: “PHP message: PHP Catchable fatal error: Argument 1 passed to App\controllers\CountriesController::__invoke() must be an instance of Psr\Http\Message\ServerRequestInterface, none given, called in /var/sites/xxx/vendor/slim/slim/Slim/RouteGroup.php on line 45 and defined in /var/sites/xxx/app/src/controllers/CountriesController.php on line 25” while reading response header from upstream, client: 192.168.56.1, server: xxx, request: “GET /v1/es/countries HTTP/1.1”, upstream: “fastcgi://unix:/var/run/php5-fpm.sock:”, host: “xxx”
It’s complicated because the $app variable is different when arrive to Controller constructor, and isn’t returned to normal thread execution and new paths are lost.