I am looking for advice in passing parameters in my router's routes.
This is my router...
<?php namespace Framework;
class Router
{
private array $routes = [];
private Request $request;
public function __construct(Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
}
public function get(string $path, string|callable|array $callback): void
{
$this->routes['GET'][$path] = $callback;
}
public function post(string $path, string|callable|array $callback): void
{
$this->routes['POST'][$path] = $callback;
}
public function dispatch(): mixed
{
$method = $this->request->method();
$path = $this->request->path();
$callback = $this->routes[$method][$path];
if(is_null($callback))
{
throw new Exception\NotFoundException;
}
if(is_string($callback))
{
return new View($callback);
}
if(is_array($callback))
{
$callback[0] = new $callback[0];
}
return call_user_func($callback);
}
}
And my routes look like this...
return function (App $app)
{
$app->get('/', [Controllers\HomeController::class, 'index']);
$app->get('/about', [Controllers\HomeController::class, 'about']);
$app->get('/contact', function() {
var_dump(App::load()->request()->body());
});
$app->get('/blog', [Controllers\BlogController::class, 'index']);
$app->get('/blog/{id}', [Controllers\BlogController::class, 'index']);
};
I want to be able to pass a parameter (for example on the last blog route). And I am sort of at a loss about how to go about it.
Related
I have a method with a lot of code
public function createNewObject(Request $request)
{
// Code...
}
There is another method that I plan to call, but how to pass it to the createNewObject method as a Request argument?
public function deleteAndCreateObject()
{
$this->createNewObject(???);
}
Just type-hint it in your deleteAndCreateObject() method.
class YourController
{
public function createNewObject(Request $request)
{
// Code...
}
public function deleteAndCreateObject(Request $request)
{
$this->createNewObject($request);
}
}
If that—for some reason—doesn't work for you, you can always use request():
class YourController
{
public function createNewObject()
{
$request = request();
// Code...
}
public function deleteAndCreateObject()
{
$this->createNewObject();
}
}
I would like to call a route middleware with a parameter passed from the route when added. How is it possible?
$app->get('/path', function($request, $response, $lvlreq = 1) {
$oViewParams = new \lib\ViewParams("referencia", "", "", "", "");
$params = array('viewp' => $oViewParams->getMassParams());
return $this->get('view')->render($response, 'some.html', $params);
})->add($authenticate)
->add($tmhasaccess);
First middleware doesn't need params, thats going well.
$authenticate = function (Request $request, RequestHandler $handler) {
if (!isset($_SESSION['param'])) {
$routeContext = RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
$redirect = $route->getPattern();
$_SESSION['urlRedirect'] = $redirect;
$this->get('flash')->addMessage('error', 'error');
$response = $handler->handle($request);
return $response->withStatus(302)->withHeader('Location', '/login');
} else {
$response = $handler->handle($request);
return $response;
}
};
$tmhasaccess = function (Request $request, RequestHandler $handler) {
###I need $lvlreq value inside here to work with it. This won't work:
$routeContext = RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
$lvlreq = $route->getArgument('lvlreq');
};
Middleware changed to class:
class TMHasAccessMiddleware
{
protected $lvlreq;
private $container;
function __construct($container, $lvlreq = 0) {
$this->lvlreq = $lvlreq;
$this->container = $container;
}
public function __invoke(Request $request, RequestHandler $handler): Response
{
##$this->lvlreq now accessible.
}
}
Can be called from route:
->add(new TMHasAccessMiddleware($container, 1));
I'm a new user of Slim framework, I've a simple Slim 3 application, with sign in and sign up validation. But I'm not really sure if this is the right/best way to set errors and check if user is logged in -In order to redirect it to his account if session user.id exists.
I used a middleware: AuthMiddleware which includes:
class AuthMiddleware
{
protected $container;
public function __construct($container)
{
$this->container = $container;
}
public function __invoke($request, $response, $next)
{
if (isset($_SESSION['user.id']) && !empty($_SESSION['user.id'])) {
return $response->withRedirect($this->container->router->pathFor('user.index'));
}
$twig = $this->container->view->getEnvironment();
if (isset($_SESSION['validation'])) {
$twig->addGlobal('errors', $_SESSION['validation']['errors']);
$twig->addGlobal('values', $_SESSION['validation']['values']);
unset($_SESSION['validation']);
}
if (isset($_SESSION['auth.signup.success'])) {
$twig->addGlobal('auth_signup_success', $_SESSION['auth.signup.success']);
unset($_SESSION['auth.signup.success']);
}
if (isset($_SESSION['auth.signin.failed'])) {
$twig->addGlobal('auth_signin_failed', $_SESSION['auth.signin.failed']);
unset($_SESSION['auth.signin.failed']);
}
$response = $next($request, $response);
return $response;
}
}
And I used Twig for my views.
Session validation assigned in the validator.php which includes:
class Validator
{
protected $errors = [];
protected $values = [];
public function validate($request, $rules)
{
foreach ($rules as $field => $rule) {
$this->values[$field] = $request->getParam($field);
try {
$rule->setName(ucfirst($field))->assert($request->getParam($field));
} catch (NestedValidationException $e) {
$this->errors[$field] = $e->getMessages()[0];
}
}
if ($this->failed()) {
$_SESSION['validation'] = [
'errors' => $this->errors,
'values' => $this->values,
];
}
return $this;
}
public function failed()
{
return !empty($this->errors);
}
}
Using Respect\Validation. Also, is this the right use of Middlewares?
Thanks in advance.
try creating a separate file for the methods, and calling it from the middleware:
<?php
class AuthMiddleware extends Middleware {
public function __invoke($request, $response, $next) {
if (!$this->container->auth->check()) {
$this->container->flash->addMessage('danger', 'Please sign in to continue.');
return $response->withRedirect($this->container->router->pathFor('auth.signin'));
}
$response = $next($request, $response);
return $response;
}
}
while the Auth class would have those methods to check:
<?php
public function check () {
return isset($_SESSION['user']);
}
public function user() {
if (isset($_SESSION['user'])) {
return User::find($_SESSION['user'])->first();
} else {
return false;
}
}
Don't forget to include the Auth Class within your $app:
<?php
$container['auth'] = function ($container) {
return new \App\Auth\Auth();
};
How can I get the param in this case?
$this->get('/{id}', function($request, $response, $args) {
return $response->withJson($this->get('singleSelect'));
});
$this->appContainer['singleSelect'] = function ($id) {
return $this->singleSelect($id);
};
public function singleSelect($id) {
return $id;
}
Thanks in advance.
UPDATE
Solution in my case:
$app->group('/id', function () {
$this->get('/{id}', function($request, $response, $args) {
$this['container'] = $args; //work with $args inside the container
return $this->singleSelect($id);
});
});
If I right understand services have not access to routes parameters. All you can access is container itself but it could be tricky to get information about arguments from it (something like $container->getRoutes()['<routename>']->getArguments() where <routename> router could have subroutes, etc.)
IMHO your code should look like:
$container = new \Slim\Container;
$app = new \Slim\App($container);
class Example {
public function singleSelect($id) {
return $id;
}
}
$container['example'] = function () {
return new Example();
};
$app->group('/id', function () {
$this->get('/{id}', function($request, $response, $args) {
return $response->withJson($this->example->singleSelect($args['id']));
});
});
$app->run();
I'm trying to profile the requests made to an API server from a PHP client using Guzzle (v 6).
In the Guzzle 5.3 there is this complete and before event handling.
class GuzzleProfiler implements SubscriberInterface
{
public function getEvents()
{
return [
'before' => ['onBefore'],
'complete' => ['onComplete']
];
}
public function onBefore(BeforeEvent $event, $name)
{
start_profiling();
}
public function onComplete(CompleteEvent $event, $name)
{
end_profiling();
}
}
But how do I do this in v6?
Just found it using Middleware. Here's the code.
class Profiler {
/**
* #return callable
*/
public static function profile() {
return function(callable $handler) {
return function(\Psr\Http\Message\RequestInterface $request, array $options) use ($handler) {
start_profiling();
return $handler($request, $options)->then(function(\Psr\Http\Message\ResponseInterface $response) use ($token) {
end_profiling();
return $response;
});
};
};
}
}
And then attach the profiler like this.
$stack = \GuzzleHttp\HandlerStack::create();
$stack->push(Profiler::profile());
$client = new \GuzzleHttp\Client([
'handler' => $stack
]);