I am trying to get a minimum Symfony routing system together to run on the side of my Laravel install for the incoming APIs to be outside the full Laravel install. I noticed that our normal Laravel system includes 575 files to render / load and it just is too slow to handle the incoming workload. More details here: https://serverfault.com/questions/959018/apache-tuning-for-512gb-ram
To put it together I made up a new composer file named minvendor.json with the following:
{
"config": {
"vendor-dir": "minvendor"
},
"require": {
"symfony/routing": "^4.2",
"symfony/http-foundation": "^4.2",
"symfony/yaml": "^4.2",
"symfony/config": "^4.2"
}
}
Since there is already a composer.json file, you have to use a different command to load the minvendor.json file.
env COMPOSER=minvendor.json composer install
I then put the following into the .htaccess file:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^api api.php [L]
After that I made the api.php file with the following
<?PHP
require __DIR__.'/../minvendor/autoload.php';
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
$fileLocator = new FileLocator([__DIR__]);
$loader = new YamlFileLoader($fileLocator);
$routes = $loader->load('routes.yaml');
class LuckyController
{
public function number()
{
$number = random_int(0, 100);
return new Response(
'<html><body>Lucky number: '.$number.'</body></html>'
);
}
}
and in the routes.yaml file:
route1:
path: api/foo
controller: LuckyController::number
methods: GET|POST
Using the php function print_r(get_included_files()) I can see I am only using 20 include files for this new way, so much better, but I don't get anything returned to the browser when I go to the site: http://www.myserver.org/api/foo
I tried a couple other ways such as:
$foo_route = new Route('/api/foo', array('App\Http\Controllers\Api\V2\LuckyController' => 'number') );
Nothing I do seems to get Symfony to access the functions and return the results to the browser. What am I missing in the process?
Dbrumann had a much better link than I did. I had been going through the regular documentation section here: https://symfony.com/doc/current/routing.html instead of the Build Your Own Framework section: https://symfony.com/doc/current/create_framework/routing.html
Before I got his response I tried a different routing library Macaw (https://github.com/noahbuscher/macaw) which ended up being pretty simple and used much fewer include files.
In addition to the routing, I needed an autoloader since the controller files were outside the public directory, I chose robot-loader, so now my minvendor.json file looks like this:
{
"config": {
"vendor-dir": "minvendor"
},
"require": {
"noahbuscher/macaw": "dev-master",
"nette/robot-loader": "dev-master"
}
}
and a full working example page would look like this:
<?PHP
require __DIR__.'/../minvendor/autoload.php';
use \NoahBuscher\Macaw\Macaw;
$loader = new Nette\Loaders\RobotLoader;
// Add directories for RobotLoader to index
$loader->addDirectory(__DIR__ . '/../app/Http/Controllers/Api/V2');
// And set caching to the 'temp' directory
$loader->setTempDirectory(__DIR__ . '/temp');
$loader->register(); // Run the RobotLoader
//just a couple ways you can define routes
Macaw::get('/api/foo', function() {
echo 'Hello world!';
});
Macaw::post('/api/bar', 'App\Http\Controllers\Api\V2\Controller#accounting');
Macaw::dispatch();
Related
I use: slim-skeleton (Mr. Rob Allen's scaffold provided on github)
Under /projects grouped routes, only "get" method routes work without any problem but with rest of all return page not found error. Also these routes have auth middleware + cors middleware (Cors Middleware taken from Slim Framework's v3 Documentation).
Here is my ../src/routes.php file:
use Slim\Http\Request;
use Slim\Http\Response;
// Routes
// Administration
$app->group('/admin', function() use ($app) {
// Dashboard
$app->get('/dashboard', 'App\Controllers\Admin\Dashboard:index');
// Projects and images management
$app->group('/projects', function() use ($app){
// Projects Actions
$app->get('/list', 'App\Controllers\Admin\ProjectManagement:index');
$app->post('/add', 'App\Controllers\Admin\ProjectManagement:add'); # NOT WORKING
$app->get('/id/{id}', 'App\Controllers\Admin\ProjectManagement:find');
$app->put('/edit/{id}', 'App\Controllers\Admin\ProjectManagement:edit'); # NOT WORKING
$app->delete('/remove/{id}', 'App\Controllers\Admin\ProjectManagement:remove'); # NOT WORKING
// Project Images Actions
$app->get('/{pid}/images', 'App\Controllers\Admin\ProjectImageManagement:attachments');
$app->post('/{pid}/images/attach', 'App\Controllers\Admin\ProjectImageManagement:attach');
// Project's Image management
$app->get('/{pid}/images/id/{id}', 'App\Controllers\Admin\ProjectImageManagement:find');
$app->put('/{pid}/images/edit/{id}', 'App\Controllers\Admin\ProjectImageManagement:edit');
$app->delete('/{pid}/images/remove/{id}', 'App\Controllers\Admin\ProjectImageManagement:removeImage');
/**
* Project's Image Sort Order
*
* Additional Info:
*
* GET /{pid}/images Retrieves current sort order beforehand
*
*/
$app->put('/{pid}/images/order/{id}/resort', 'App\Controllers\Admin\ProjectImageManagement:sortOrder');
});
// Page management
$app->group('/pages', function() use ($app) {
// About Page
$app->get('/about/content', 'App\Controllers\Admin\PageManagement:aboutPage');
$app->put('/about/content/update', 'App\Controllers\Admin\PageManagement:updateAbout');
// Contact Page
$app->get('/contact/content', 'App\Controllers\Admin\PageManagement:contactPage');
$app->put('/contact/content/update', 'App\Controllers\Admin\PageManagement:updateContact');
});
// Settings
$app->group('/settings', function() use ($app) {
// Account
$app->get('/account/details', 'App\Controllers\Admin\Settings:accountDetails');
$app->post('/account/details/apply', 'App\Controllers\Admin\Settings::applyAccountSettingChanges');
});
})->add($auth);
// Auth
$app->get('/auth/point', 'App\Controllers\AuthController:checkPoint');
$app->post('/auth/login','App\Controllers\AuthController:login');
$app->get('/auth/logout', 'App\Controllers\AuthController:logout');
// Guest
$app->get('/about', 'App\Controllers\Guest\PageContents:about');
$app->get('/contact', 'App\Controllers\Guest\PageContents:contact');
$app->get('/works', 'App\Controllers\Guest\ProjectExplorer:projects');
And here is my .htaccess under /public directory:
<IfModule mod_rewrite.c>
RewriteEngine On
# Some hosts may require you to use the `RewriteBase` directive.
# Determine the RewriteBase automatically and set it as environment v
ariable.
# If you are using Apache aliases to do mass virtual hosting or
installed the
# project in a subdirectory, the base path will be prepended to allow proper
# resolution of the index.php file and to redirect to the correct URI. It will
# work in environments without path prefix as well, providing a safe, one-size
# fits all solution. But as you do not need it in this case, you can comment
# the following 2 lines to eliminate the overhead.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
# If the above doesn't work you might need to set the `RewriteBase` directive manually, it should be the
# absolute physical path to the directory that contains this htaccess file.
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</IfModule>
I also added Allow from all, but it results in same way. But if i have doing something wrong why all other routes created outside of "/projects" work well. For instance, Auth related routes work well.
I know that, I have to see "Method Not Allowed" warning instead of seeing not found. Where am i doing something wrong? Any help would be very appreciated.
EDIT:
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function($req, $res) {
$handler = $this->notFoundHandler; // handle using the default Slim page not found handler
return $handler($req, $res);
});
I forgot this one. This mapping hides/exchanges method not allowed page => not found handler. It seems to me as an application based error. Sorry for my carelessness...
My suggestion is to add a root for the group you added
$app->group('/admin', function(){};
Under this you should add
$this->get('', function(){/*-----activities----*/});
And try to use $this while using the groups
Explanation:
when you are using $app you are referring to slim object.
when you are using $this inside your $app->group(){} then you are referring to grouped object which will be responsible for grouping the statements.
So, basically when you use $app inside the group then you make it useless because your slim-app is looking for it on above layer of the code(ie. outside the group). and you are not using group object($this in this case) to declare routes so eventually they are getting declared but not addressed properly.
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function($req, $res) {
$handler = $this->notFoundHandler; // handle using the default Slim page not found handler
return $handler($req, $res);
});
I forgot this one. This mapping hides/exchanges method not allowed page => not found handler. It seems to me as an application based error. Sorry for my carelessness...
But in my opinion notFoundHandler is not the right choice for this. A custom error message has to be raised for this situation like 405 Method Not Allowed - (notFoundHandler returns 404)
Not sure if this is the case, but if you need to change $app->get or $app->post to $this->get or $this->post if you group the routes.
I haven't found many questions and answers relating to this so far, so thought I would ask the question as it will greatly help me out as a beginner learning PHP and the Slim framework. It's pretty straightforward (i think).
So, I want to route my home page to another page called about.php. I'm using the Slim/Slim framework which is installed in my vendor folder. And I have an index.php file with the following code:
<?php
require '/vendor/autoload.php';
$app = new \Slim\Slim();
$app->get('/', function() use($app){
$app->render('about.php');
});
$app->run();
?>
I also have an about.php file, which does exist.
This is what's currently in my composer.json file:
"require": {
"monolog/monolog": "^1.22",
"slim/slim": "^3.7",
"twig/twig": "^1.32",
"slim/views": "^0.1.3"
}
When I run MAMP (set-up to access the project I am working on) to see the page, it's blank. Can anyone help me understand what I'm doing wrong?
I've then run this (removing the leading '/' from the require statement, and adding a line to display errors), and it displayed a 500 error:
<?php
ini_set('display_errors', 1);
require 'vendor/autoload.php';
$app = new \Slim\Slim();
$view = $app->view();
$view->parserOptions = array(
'debug' => true
$app->get('/', function() use($app){
$app->render('about.php');
});
$app->run();
?>
I'm unclear if you want about.php to be separate from your Slim application or not.
i.e. when someone goes to http://example.com/ do you want the browser's URL to change to http://example.com/about.php where about.php is a completely independent PHP file in the same directory as your index.php?
If you do then you need to redirect:
$app->get('/', function ($request, $response) {
return $response->withRedirect('/about.php');
});
More usually, Slim is used to route to and display all pages in your application and in this situation, you wouldn't see the .php in the URL. This is because our app always runs index.php regardless of the actual URL in the the browser's address bar.
In this situation, you would probably still redirect, but without the .php:
$app->get('/', function ($request, $response) {
return $response->withRedirect('/about');
});
You would also need a handler for /about otherwise Slim won't know what to do:
$app->get('/about', function ($request, $response) {
return $response->write("This is my about page");
});
We don't actually tend to write the HTML directly in our handler though. We use a renderer. There's two in the Slim project: PhpView and TwigView. The Skeleton application shows how the PhpView version works. You may also find the tutorial in the documentation useful.
Finally, if you see a 500, then you have a PHP error somewhere. The easiest way to find this is to ensure that the php.ini setting error_reporting is set to E_ALL and that display_errors is set to On.
As you've already discovered, Slim 3's main class is called App. You can also enable detailed error displays within Slim using:
$app = new Slim\App(['settings' => ['displayErrorDetails' => true]]);
Again, usually we have a separate settings.php file containing this configuration information as shown in the skeleton application.
On the documentation for Slim Framework, it says
In this example application, all the routes are in index.php but in
practice this can make for a rather long and unwieldy file! It’s fine
to refactor your application to put routes into a different file or
files, or just register a set of routes with callbacks that are
actually declared elsewhere.
It doesn't say how to actually do this though. My only thought is that you could split code into multiple PHP files and then use include or require in index.php to reference these.
I'm also not sure what it means by "register a set of routes with callbacks that are actually declared elsewhere"
Does anyone have any thoughts on this, since the application I'm wanting to build might have quite a few routes?
Being a micro-framework, Slim does not enforce any specific method. You can either find a ready-to-use structure (Slim Skeleton Application comes to my mind) or write your own; unlike other frameworks, Slim does not try to protect you from PHP.
Route definitions can be something as simple as an array of strings:
<?php // routes.php
return [
'/' => ['Foo\\Home', 'index'],
'/about' => ['Foo\\Home', 'about'],
'/contact' => ['Foo\\Contact', 'form' ],
];
... which you then load and process in your index.php entry point:
$routes = require('/path/to/routes.php');
foreach ($routes as list($path, $handler)) {
$app->get($route, $handler);
}
And you can leverage the existing Composer set up to auto-load your classes by adding the appropriate directories to composer.json:
{
"require": {
"slim/slim": "^3.3",
"monolog/monolog": "^1.19"
},
"autoload": {
"psr-4": {"Foo\\": "./Foo/"}
}
}
From here, it can get as complex as required: define routes in a YAML file, auto-load from defined classes, etc.
(Code samples are shown for illustration purposes and might not even be valid.)
There're some thoughts on it in Slim documentation
Instead of require's you can use composer autoloading
"register a set of routes with callbacks that are actually declared elsewhere"
From docs:
Each routing method described above accepts a callback routine as its final argument. This argument can be any PHP callable...
So you can do:
$routeHandler = function ($request, $response) { echo 'My very cool handler'; };
$app->get('/my-very-cool-path', $routeHandler);
But usually people use classes instead of functions:
http://www.slimframework.com/docs/objects/router.html#container-resolution
I think you almost get the basic idea right. I recommend reading chapter on routing a couple of times. It covers everything pretty good.
Happy coding and let me know if you need any other help!
I am trying to get the Bronto api PHP lib to work with composers autoload. But no go. What is missing?
Composer.json:
{
"require": {
"slim/slim": "2.4.*",
"bronto/bronto-api-php-client": "dev-master"
},
"minimum-stability": "dev"
}
index.php
<?php
require '../vendor/autoload.php';
$app = new \Slim\Slim();
$app->get('/', function () {
$bronto = new \Bronto_Api();
$bronto->setToken($token); // Or pass $token to the constructor of Bronto_Api
$bronto->login(); // Only needs to be called once
});
$app->run();
Slim's framework loads fine. I just keep getting a 'Fatal error: Class 'Bronto_Api' not found in /app/location/'.
Any ideas on what could be going on?
This is 3 years after the original question was asked but I had the same problem trying to add the package to a Laravel project I was working on. I resolved it by adding the following to my composer.json (the one belonging to my project).
"autoload": {
"psr-0": {
"Bronto_": "./vendor/bronto/bronto-api-php-client/Symfony/Component/Console/src/"
}
}
It feels a little dirty but works OK.
According to the Silex documentation:
Symfony provides a Twig bridge that provides additional integration between some Symfony2 components and Twig. Add it as a dependency to your composer.json file.
I include the following in my composer.json file:
{
"require": {
"silex/silex": "1.*",
"twig/twig": ">=1.8,<2.0-dev",
"symfony/twig-bridge": "2.3.*"
}
}
I register the TwigServiceProvider() like so:
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__ . '/views'
));
I'm attempting to use the twig path() method like so:
Log out
The error I get is as follows:
Twig_Error_Syntax: The function "path" does not exist
Why am I getting this error?
I have tried switching around versions to check if it is a version issue
One google groups comment suggested 'registering' the twig bridge provider, but this doesn't exist
I don't want to have to use: app.url_generator.generate in all my templates instead
A temporary solution I have found:
Ensure The UrlGeneratorServiceProvider() is registered:
$app->register(new UrlGeneratorServiceProvider());
Create a new function for twig for path():
$app['twig']->addFunction(new \Twig_SimpleFunction('path', function($url) use ($app) {
return $app['url_generator']->generate($url);
}));
I shouldn't have to do this!! How can I get this working properly?
Hopefully this will help future viewers as many have posted this question without a solid answer, so here is one.
It is literally that you need UrlGeneratorServiceProvider() registered
$app->register(new UrlGeneratorServiceProvider());
Also, as umpirsky mentions in the comments, you need symfony/twig-bridge installed via composer.
You do not need to add your own function. You need both the TwigServiceProvider() and the UrlGeneratorServiceProvider() registered before loading your twig template. This isn't easily apparent from the documentation.
I too had to create a new function for twig for path(), but I improved it a bit to handle a variable number of arguments to allow passing arrays in the twig template:
$app['twig']->addFunction(new \Twig_SimpleFunction('path', function(...$url) use ($app) {
return call_user_func_array(array($app['url_generator'], 'generate'), $url);
}));
Four easy steps.
Create the loader
Create the twig object.
Create you custom function
Add to the Twig object.
use Twig\Environment;
use Twig\TwigFunction;
use Twig\Loader\FilesystemLoader;
$loader = new FilesystemLoader('/twig/templates');
$twig = new Environment($loader, []);
$function = new TwigFunction('url', function () { return 'MyURL'; });
$twig -> addFunction($function);