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...
Related
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.
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.
http://www.slimframework.com/docs/tutorial/first-app.html shows using "plain old PHP" (Composer package is php-view and class is PhpRenderer) for templates as follows:
<?php
// Create app
$app = new \Slim\App();
// Get container
$container = $app->getContainer();
$container['view'] = new \Slim\Views\PhpRenderer("../templates/");
$app->get('/tickets', function (Request $request, Response $response) {
$this->logger->addInfo("Ticket list");
$mapper = new TicketMapper($this->db);
$tickets = $mapper->getTickets();
$response = $this->view->render($response, "tickets.phtml", ["tickets" => $tickets]);
return $response;
});
// Run app
$app->run();
The above page states using Twig for templates is basically the same, but http://www.slimframework.com/docs/features/templates.html shows a very different approach. Which is the correct way to use templates, and specifically how should I use Twig for templates?
<?php
// Create app
$app = new \Slim\App();
// Get container
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig('path/to/templates', [
'cache' => 'path/to/cache'
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container['router'],
$container['request']->getUri()
));
return $view;
};
// Render Twig template in route
$app->get('/hello/{name}', function ($request, $response, $args) {
return $this->view->render($response, 'profile.html', [
'name' => $args['name']
]);
})->setName('profile');
// Run app
$app->run();
I have installed slim framework 3 and twig template following the composer.
When i call function http://localhost/elec/helloo/sandesh it displays Hello, Sandesh as followed on slim 3 documentation.
But when i try to call view page(inside templates folder).
It displays an error page Slim Application Error The application could not run because of the following error Error Description
Code Worked ( displays hello , {name} from function)
$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
return $response;
});
Code error ( displays error when called view page from function)
$settings = [
'settings' => [
'displayErrorDetails' => true,
],
];
$app = new Slim\App($settings);
// Get container
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
return new \Slim\Views\PhpRenderer("templates/");
};
// Render Twig template in route
$app->get('/helloo/{name}', function ($request, $response, $args) {
return $this->view->render($response, 'view1.html', [
'name' => $args['name']
]);
})->setName('profile');
Path Detail
elec>
>>cache
>>templates
>>>view1.html
>>vender
>>.htaccess
>>composer.json
>>composer.lock
>>index.php
When passing the templates location, you have to provide a full path, (starting from the location of the running index.php file:
<?php
$container['view'] = function ($container) {
return new \Slim\Views\PhpRenderer(__DIR__ . "/../path/to/templates/");
};
try it out, and good luck.
Note: I'm using the same line but with Twig render:
<?php
$container['view'] = function ($container) {
return new \Slim\Views\Twig(__DIR__ . "/../path/to/templates/");
};
$app = new \Slim\App([
'settings' => [
'displayErrorDetails' => true,
]
]);
// Calling twigview from controller
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig('templates/views',[
'cache' => false,
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container->router,
$container->request->getUri()
));
return $view;
};
$app->get('/home', function ($request, $response) {
return $this->view->render($response, 'home.twig');
});
lets say I have a model called John with those params:
{
Language : {
code : 'gr',
title : 'Greek'
},
Name : 'john'
}
So now when I trigger John.save() it POST those to server:
post params http://o7.no/ypvWNp
with those headers:
headers http://o7.no/x5DVw0
The code in Silex is really simple:
<?php
require_once __DIR__.'/silex.phar';
$app = new Silex\Application();
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// definitions
$app['debug'] = true;
$app->post('/api/user', function (Request $request) {
var_dump($request->get('Name'));
$params = json_decode(file_get_contents('php://input'));
var_dump($params->Name);
});
$app->run();
but first var_dump return null second var_dump of course works since I'm getting the request directly from php://input resource. I'm wondering how I could get the params using Request object from Silex
Thanks
It's pretty easy actually.
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;
$app->before(function (Request $request) {
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data = json_decode($request->getContent(), true);
$request->request = new ParameterBag(is_array($data) ? $data : array());
}
});
And then an example route:
$app->match('/', function (Request $request) {
return $request->get('foo');
});
And testing with curl:
$ curl http://localhost/foobarbazapp/app.php -d '{"foo": "bar"}' -H 'Content-Type: application/json'
bar
$
Alternatively look at the (slightly outdated) RestServiceProvider.
EDIT: I have turned this answer into a cookbook recipe in the silex documentation.
The way I have done it before is the following:
$app->post('/api/todos', function (Request $request) use ($app) {
$data = json_decode($request->getContent());
$todo = $app['paris']->getModel('Todo')->create();
$todo->title = $data->title;
$todo->save();
return new Response(json_encode($todo->as_array()), 200, array('Content-Type' => 'application/json'));
});
In your backbone collection, add the following:
window.TodoList = Backbone.Collection.extend({
model: Todo,
url: "api/todos",
...
});
I have written up a full step-by-step tutorial here http://cambridgesoftware.co.uk/blog/item/59-backbonejs-%20-php-with-silex-microframework-%20-mysql
I've solved it myself by setting an extra $payload property on the Request object
$app->before(function(Request $request) {
if (stristr($request->getContentType(), 'json')) {
$data = json_decode($request->getContent());
$request->payload = $data;
} else {
$request->payload = null;
}
});