Silex - Best Practice for Client Choice Response Format (JSON / XML) - php

I would like to know the best coding practice that allows the client to define the response format for the request he made, which can also include filters, conditions, sorts, etc ...
I made a small template for an answer, I do not know if it is a best practice, but it works (advice is welcome). I coded no Middleware after();
PS: The choice of format will be dynamic after building the request code.
Now about a requisition. I figured I'd code any middleware before(). What is the best way to do it?
Follow the code:
index.php
<?php
define('ROOT', dirname(__DIR__));
chdir(ROOT);
require 'vendor/autoload.php';
require 'src/Config/bootstrap.php';
require 'src/Config/routes.php';
$app->run();
bootstrap.php
<?php
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$app = new Application();
$app['serializer'] = function(){
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
return new Serializer($normalizers, $encoders);
};
$app['debug'] = true;
/*$app->before(function (Request $request) use ($app){
$request->query->
});*/
$app->after(function (Request $request, Response $response) use ($app){
//var_dump($response);
$response->headers->set('Content-Type', 'application/xml');
return $response;
});
return $app;
routes.php
<?php
$app->mount('/classificados', require 'src/App/Controllers/ClassificadosController.php');
ClassificadosController.php
<?php
use Symfony\Component\HttpFoundation\Response;
$classificados = $app['controllers_factory'];
$classificados->get('/', function() use ($app) {
$post = array(
'title' => 'Titulo',
'body' => 'corpo',
);
$serializeContent = $app['serializer']->serialize($post, 'xml');
return new Response($serializeContent, 200);
});
return $classificados;
What is the best way to build a logic to dynamize the response format (json or xml) for the client?

Related

Understanding Slim 4 routing function

I am trying to learn slim framework and I am following the tutorial. What I would like is a detailed explanation of what the be low snippet of code is doing within the slim environment.
$app->get('/client/{name}'
The reason that I am asking is because in keep getting route not found. But I have yet to figure out why. The base route works. But when I added the twig and tried to route to that. It fails.
Now comes the code:
This part is in my webroot/public/index.php
<?php
use DI\Container;
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
use Twig\Error\LoaderError;
require __DIR__ . '/../vendor/autoload.php';
$container = new Container;
$settings = require __DIR__ . '/../app/settings.php';
$settings($container);
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->addRoutingMiddleware();
$app->addErrorMiddleware(true, true, true);
// Create Twig
$twigPath = __DIR__ . "/../templates";
$twig = '';
try {
$twig = Twig::create($twigPath, ['cache' => false]);
} catch (LoaderError $e) {
echo "Error " . $e->getMessage();
}
// Add Twig-View Middleware
$app->add(TwigMiddleware::create($app, $twig));
$routes = require __DIR__ . '/../app/routes.php';
$routes($app);
$app->run();
This part is in the routes.php:
<?php
use Slim\App;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Views\Twig;
return function (App $app) {
$app->get('/', function (Request $request, Response $response, array $args) {
$response->getBody()->write("Hello world! Really?");
return $response;
});
$app->get('/client/{name}', function (Request $request, Response $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'client_profiles.html', [
'name' => $args['name']
]);
})->setName('profile');
};
The first route works fine the second does not. According to what I am reading. It should work. https://www.slimframework.com/docs/v4/features/templates.html
I feel that if I knew what get is looking to do. I may be able to fix it and build a proper route.
When I dig into the $app->get which connects with the RouterCollecorProxy.php. There is the $pattern variable and $callable. The callable is the anonymous function that comes after the common in the
$app->get('/client/{name}', function <- this is the callable, right?
I see the map class which takes me to the createRoute which returns the $methods, $pattern, callable and a few other things.
I think the pattern is where my problem is.

Slim 4 assign twig parameters from middleware

I'm trying to upgrade my website's code from Slim v2 to v4. I'm not a hardcore programmer so I'm facing issues.
In Slim v2 I had some middleware where I was able to assign parameters to the Twig view before the route code executed.
Now I'm trying to manage the same with Slim v4 but without success.
I have a container:
$container = new \DI\Container();
I have the view:
$container->set('view', function(\Psr\Container\ContainerInterface $container) {
return Twig::create(__DIR__ . '/views');
});
I try to use this from middleware:
$this->get('view')->offsetSet('fbloginurl', $loginUrl);
But nothing append when the view rendered.
If I try to use the same from the route inside, its working fine.
Example route:
$app->get('/', function ($request, $response, $args) {
$params = array(...);
return $this->get('view')->render($response, 'index.html', $params);
});
There are two possible failures. First, the DI container may always return a new instance, thus it doesn't store the variables in the correct instance and they are not rendered in the twig template. Second, you use a different approach in your route sample. You pass the variables via your $params variable and they are given into the template by this way.
So you may store $this->get('view') in a variable or pass the variables as the third parameter of $params.
EDIT: You could also check, if your variable in your DI\Container already exists and then just return the instance.
So this is a test code:
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Routing\RouteContext;
require 'vendor/autoload.php';
require 'config.php';
lib\Cookie::init();
$container = new \DI\Container();
$container->set('view', function($container) {
return Twig::create(__DIR__ . '/views');
});
$container->set('flash', function ($container) {
return new \Slim\Flash\Messages();
});
$container->get('view')->getEnvironment()->addGlobal('flash', $container->get('flash'));
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->addErrorMiddleware(true, false, false);
$fb = new Facebook\Facebook([
'app_id' => '...',
'app_secret' => '...',
'default_graph_version' => '...',
]);
$beforeMiddleware = function (Request $request, RequestHandler $handler) use ($fb) {
$response = $handler->handle($request);
if (!isset($_SESSION['fbuser'])) {
$helper = $fb->getRedirectLoginHelper();
$permissions = ['email'];
$loginUrl = $helper->getLoginUrl('...', $permissions);
$this->get('view')->offsetSet('fbloginurl', $loginUrl);
}
else {
$this->get('view')->offsetSet('fbuser', $_SESSION['fbuser']);
}
$uri = $request->getUri();
$this->get('view')->offsetSet('currenturl', $uri);
return $response;
};
$app->add($beforeMiddleware);
$app->get('/test', function (Request $request, Response $response, $args) {
$oViewParams = new \lib\ViewParams("home", "", "", "", "");
$oProfession = new \models\Profession();
$oBlogPost = new models\BlogPost();
$oBlogTopic = new models\BlogTopic();
$professions = $oProfession->getProfessionsWithLimit(14);
$posts = $oBlogPost->getMainPagePosts();
echo $this->get('view')->offsetGet('fbloginurl');
$params = array('professions' => $professions,
'posts' => $posts,
'viewp' => $oViewParams->getMassParams());
return $this->get('view')->render($response, 'index.html', $params);
});
$app->run();
When I use echo $this->get('view')->offsetGet('fbloginurl'); within the middleware it shows up. When I use the same within the route there is nothing show up...

Organize Slim 3 API routes logic into functions

I would like structure the API in order to separate both the routing organization from actions in separate files.
The current code does not return any errors, but the parameters are not collected correctly.
Is there a simple way to organize into functions without the need for classes, or __invoke?, the application does not require it.
public/index.php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../vendor/autoload.php';
$app = new \Slim\App;
foreach (glob("../src/middleware/*.php") as $middleware) {
require $middleware;
}
require '../src/routes/routes.php';
$app->run();
src/routes/routes.php
$app->group('/v1', function () use ($app) {
$app->post('/register', 'registerParticipant');
});
src/middleware/registerParticipant.php
require '../lib/qrlib/vendor/qrlib.php';
function registerParticipant($request, $response, $args) {
// demo for testing
$foo= $request->post('foo');
echo "foo= ".$foo;
// more app logic
}
Replacing $foo= $request->post('foo'); with $foo= $request->getParam('foo'); did the trick.
src/middleware/registerParticipant.php
require '../lib/qrlib/vendor/qrlib.php';
function registerParticipant($request, $response, $args) {
// demo for testing
$foo= $request->getParam('foo');
echo "foo= ".$foo;
// more app logic
}

Add log files to slim framework

I am completelly new to Slim. I have used php for the last 3-4 years but I have always done everything from scratch. I want to learn this frameworks for some rest services I have to do.
I have followed a tutorial on the slim webpage to get a simple rest service working but I want to add a log system to see what is happening when something goes wrong or wathever.
This is what I have right know:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../Slim/Slim.php';
\Slim\Slim::requisterAutoloader();
$application = new \Slim\App();
$logger = $application->log;
$logger->setEnabled(true);
$logger->setLevel(\Slim\Log::DEBUG);
$application->get(
'/hello/user',
function ()
{
GLOBAL $logger;
$logger->info("starting the handling function");
echo "<data>response</data>";
$logger->info("ending handling function");
});
$application->run();
?>
I also tried with monolog but I didn't get it working.
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
require '../vendor/autoload.php';
$logger = new \Flynsarmy\SlimMonolog\Log\MonologWriter(array(
'handlers' => array(
new \Monolog\Handler\StreamHandler('./logs/'.date('Y-m-d').'.log'),
),
));
$app = new Slim\App(array(
'log.writer' => $logger,
));
$app->get('/hello/{name}', function (Request $request, Response $response){
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
//$app->log->writer("hola");
$this->logger->info("Slim-Skeleton '/' route");
return $response;
});
$app->run();
What I would really want is to have a daily log with warnings, debug, info... in the same file. Changing the file every day.
The slim-skeleton application shows how to integrate monolog.
Register the logger with the DI container:
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new \Monolog\Logger('test-app');
$logger->pushHandler(new Monolog\Handler\StreamHandler('php://stdout', \Monolog\Logger::DEBUG));
return $logger;
};
Use it in your closure:
$app->get('/hello/{name}', function (Request $request, Response $response){
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
$this->get('logger')->info("Slim-Skeleton '/' route");
return $response;
});
The logs will out output to stdout, so you'll see the in your apache error log or if you're using the built-in PHP server, in the terminal.

Silex app->redirect does not match routes

Let my application run on localhost, the path is: localhost/silex/web/index.php, defined routes as in the code below, I'd expect visiting localhost/silex/web/index.php/redirect redirects me to localhost/silex/web/index.php/foo and displays 'foo'. Instead it redirects me to localhost/foo.
I am new to Silex and maybe I got it all wrong. Could someone explain where is the problem? Is it correct behavior and it should redirect for absolute paths? Thanks.
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Response;
$app = new Silex\Application();
$app['debug'] = true;
$app->get('/foo', function() {
return new Response('foo');
});
$app->get('/redirect', function() use ($app) {
return $app->redirect('/foo');
});
$app->run();
The redirect url expects an url to redirect to, not an in-app route. Try it this way:
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->get('/foo', function() {
return new Response('foo');
})->bind("foo"); // this is the route name
$app->get('/redirect', function() use ($app) {
return $app->redirect($app["url_generator"]->generate("foo"));
});
For internal redirects, which does not change the requested URL, you can also use a sub-request:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
$app->get('/redirect', function() use ($app) {
$subRequest = Request::create('/foo');
return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);
});
See also Making sub-Requests.
Up to "silex/silex": ">= 2.0", a native trait allow you to generate an URL based on the route name.
You can replace :
$app['url_generator']->generate('my-route-name');
By :
$app->path('my-route-name');
Then use it to redirect :
$app->redirect($app->path('my-route-name'));
Another possibility is to create a custom trait to directly redirect with a route name :
namespace Acme;
trait RedirectToRouteTrait
{
public function redirectToRoute($routeName, $parameters = [], $status = 302, $headers = [])
{
return $this->redirect($this->path($routeName, $parameters), $status, $headers);
}
}
Add the trait to your application definition :
use Silex\Application as BaseApplication;
class Application extends BaseApplication
{
use Acme\RedirectToRouteTrait;
}
Then use it wherever you need it :
$app->redirectToRoute('my-route-name');

Categories