I'm using AltoRouter and works pretty well. But for every request, a new instance of AltoRouter is create and so the mapping (I followed the example from their github page).
How to avoid this overhead?
I was thinking in Singleton anti pattern. You guys think is it ok?
I have no experience with PHP.
This is code:
<?php
//begin of singleton
require 'AltoRouter.php';
$router = new AltoRouter();
$router->map('GET', '/', function () {
require '../app/home/controllers/homecontroller.php';
});
//end of singleton
$match = $router->match();
if ($match && is_callable($match['target'])) {
call_user_func_array($match['target'], $match['params']);
} else {
// no route was matched
header($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
}
?>
My idea is to "singletonizing" the AltoRouter instance and mapping all route only once.
Related
I need help. I am currently trying to use PHP route AltoRouter but it does not work for me. Here is my code:
require_once __DIR__ . '/vendor/autoload.php';
$router = new AltoRouter();
$router->map('GET', '/', function(){
echo 'It is working';
});
$match = $router->match();
// Here comes the new part, taken straight from the docs:
// call closure or throw 404 status
if( $match && is_callable( $match['target'] ) ) {
call_user_func_array( $match['target'], $match['params'] );
} else {
// no route was matched
header( $_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
}
My browser output:
The existing list of answers I found are still not working for my code. I need help please. It's totally messing with my head.
THANK YOU VERY MUCH!
The current answers I found that did not solve for me:
PHP AltoRouter - can't get GET request
Routing via Php AltoRouter
For example in a Yii Framework application the url is in this format
www.example.com/index.php?r=foo/bar
Which renders the script inside the actionBar() method of class FooController. Further, this class (or its parent class) implements a render() method which can render a view file.
All the url's are handled through the entry script index.php.
I would like to write my own class which can handle url's through this way.
Can someone give me a very basic 'hello world' example of writing such a script ?
I'll give it a shot:
// index.php
$r = $_REQUEST['r']; // 'foo/bar'
$rParts = explode('/',$r);
$foo = $rParts[0];
$bar = $rParts[1];
$controller = new $foo; // foo
echo $controller->$bar();
Here is what I did for a friend recently, when teaching him how frameworks works. This is a basic example, but it demonstrates how a container works, how to handle the router, giving the controller a request and a response and handling redirects and the like.
<?php
require 'autoload.php';
$container = [];
$container['controller.elephant'] = function() {
return new Controller\Elephant();
};
$routes = [];
$routes['/babar'] = 'controller.elephant:babar';
$routes['/celeste'] = 'controller.elephant:celeste';
$request = new Request();
if (!isset($routes[$request->path()])) {
http_response_code(404);
exit;
}
$route = $routes[$request->path()];
list($class, $method) = explode(':', $route);
$controller = $container[$class]();
$response = $controller->{$method}($request, new Response());
if ($response->isRedirect()) {
http_response_code($response->status());
header('Location: '.$response->destination());
} else {
echo $response->content();
}
exit;
I won't include anything more than that (albeit there is other files) because it would bloat the answer needlessly (I can send them to you by other means if you want to).
I highly advise you to look at the Slim Framework code, as it is a micro framework that basically do just that.
Well in the Symfony documentation you have this page: http://symfony.com/doc/current/components/http_kernel/introduction.html
where it explains how is the life cycle of a request, it's just a flow diagram.
But it will give you a really good idea on how you should build yours
If you are more interested in how based on a url you get the controller you should read the RoutingComponent in symfony
http://symfony.com/doc/current/components/routing/introduction.html
http://symfony.com/doc/current/components/routing/hostname_pattern.html
But if you want to write your own class, you should use something like regex expression groups where you can detect the url parts separated by i.e: '/' then you somehow map the url to the controller i.e associative array 'Hash'
someurl.com/someController/someAction
$mappings = [
...
'someController' => 'The\Controller\Class'
]
$controller = new $mappings[$urlControllerPart]();
$response = $controller->{$urlActionPart}($request);
return $response;
For some reason I am not able to start AltoRouter. I am trying the most basic call, but nothing is happening. How can I make it work?
My index.php file looks like this:
<?php
include('settings/autoload.php');
use app\AltoRouter;
$router = new AltoRouter;
$router->map('GET', '/', function(){
echo 'It is working';
});
$match = $router->match();
autoload.php:
<?php
require_once('app/Router.php');
Your Problem is that AltoRouter, according to the documentation (and in contrast to the Slim Framework, which seems to have the the same syntax), won't process the request for you, it only matches them.
So by calling $router->match() you get all the required information to process the request in any way you like.
If you just want to call the closure-function, simply modify your code:
<?php
// include AltoRouter in one of the many ways (Autoloader, composer, directly, whatever)
$router = new AltoRouter();
$router->map('GET', '/', function(){
echo 'It is working';
});
$match = $router->match();
// Here comes the new part, taken straight from the docs:
// call closure or throw 404 status
if( $match && is_callable( $match['target'] ) ) {
call_user_func_array( $match['target'], $match['params'] );
} else {
// no route was matched
header( $_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
}
And voilĂ - now you'll get your desired output!
I am using the Slim Framework for a simple crud-style application. My index.php file has become quite long and unwieldy with all the different routes. How can I clean up / refactor this code? For example I have code like the following for all the different routes and GET, POST, PUT, DELETE etc.
$app->get("/login", function() use ($app)
{//code here.....});
What I like to do is group routes, and for each group, I create a new file under a subdir called routes. To illustrate with some example code from the Slim docs:
index.php:
$app = new \Slim\Slim();
$routeFiles = (array) glob(__DIR__ . DIRECTORY_SEPARATOR . 'routes' . DIRECTORY_SEPARATOR . '*.php');
foreach($routeFiles as $routeFile) {
require_once $routeFile;
}
$app->run();
routes/api.php:
// API group
$app->group('/api', function () use ($app) {
// Library group
$app->group('/library', function () use ($app) {
// Get book with ID
$app->get('/books/:id', function ($id) {
});
// Update book with ID
$app->put('/books/:id', function ($id) {
});
// Delete book with ID
$app->delete('/books/:id', function ($id) {
});
});
});
You can even do this for multiple levels, just be sure you don't overcomplicate yourself for this.
You can also do this for hooks off course.
You can for example move code the inner code to class:
$app->get("/login", function() use ($app)
{
$user = new User();
$user->login();
});
or even create your own class that will handle routing
class Router {
public function __construct($app) {
$this->app = $app;
}
public function createRoutes() {
$this->app->get("/login", function() use ($this->app)
{
$user = new User();
$user->login();
});
// other routes, you may divide routes to class methods
}
}
and then in your index.php
$router = new Router($app);
$router->createRoutes();
You can move index.php content into diferent files and just include them. For example:
index.php:
$app = new \Slim\Slim();
...
require_once 'path_to_your_dir/routes.php';
...
$app->run();
routes.php:
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
...
Or you can even create different files for different routes types:
index.php:
$app = new \Slim\Slim();
...
require_once 'path_to_your_dir/routes.php';
require_once 'path_to_your_dir/admin_routes.php';
require_once 'path_to_your_dir/some_other_routes.php';
...
$app->run();
Same approach is also ok for DI services initialization etc. (everything from your index.php)
This is how i use Slim. I do have a single file with the routes but i use a three layer approach.
index.php - i don't handle any logic here. i just register the routes and consume the api methods (post, put, delete, etc).
$app->post('/bible/comment/', function() use($ioc) {
$ioc['commentApi']->post();
});
The API layers inherit a base class where it's inject the slim app object. From there i use helper methods to extract the data from requests using arrays with the required fields, optional fields, etc. I don't do validation here. This is also where i clean the requests for xss.
Most important, i handle the exceptions here. Invalid requests throws exceptions, which are caught and transformed in a error response.
class CommentApi extends BaseApi {
public function post() {
$fields = array(array('message', 'bookId', 'chapter', 'verseFrom', 'verseTo')):
$dtoModel = new Models\CreateComment();
$data = $this->extractFormData();
Utils::transformDto($dtoModel, $data, $fields):
try {
$result = $this->commentService->create($this->getUserId(), $dtoModel);
$response->success("You've added a book to the bible."); // helper from BaseApi to set the response 200
$response->setResult($result);
}
catch(\Exceptions\CommentRepeatedException $ex) {
$response->invalid('The foo already exist. Try a new one');
}
catch(\Exceptions\CommentsClosedException $ex) {
UtilsExceptions::invalidRequest($dtoModel, $ex);
$response->invalid('Invalid request. Check the error list for more info.');
}
$this->respond($response); // encode the response in json, set the content type, etc
}
This layer class consume the business layers which will use repositories and others resources. I test the projects against the business layers, the api layers just extract the data, create the dto models and handle the response.
The request/response models implements interfaces to return a status code, erros messages to be consumed in for the clients (respect is cool to automate this).
class CommentBusiness {
public function create($userId, Models\CreateComment $model) {
// Validate the request object
// Assert all logic requirements
$dataRes = $this->repository->create('message' => $model->getMessage(), 'bookId' => $model->getUserId(), 'chapter' => $model->getChapter(), 'verseFrom' => $mode->getVerseFrom(), 'verseTo' => $model->getVerseTo());
if($dataRes->isInvalid()) {
throw new \Exceptions\DataException($dataRes->getExModel());
}
return $dataRes;
}
}
I like to use classes as route callbacks, and combine that with grouping of routes. Doing that, your index.php file (or wherever you choose to define your routes) will probably become very "Slim":
$app->group('/user', function() use ($app) {
$app->map('/find/:id', '\User:search')->via('GET');
$app->map('/insert', '\User:create')->via('POST');
// ...
});
In this example, the /user/find/:id route will call the search method (passing the value of :id as the first parameter) of the User class. So your callback class might look a bit like this:
class User {
public function search($userId) {
// ...
}
}
There are a number of advantages to this approach, especially if you are writing a CRUD-style application (that needs database access). For example, you can contain all your database logic in a base class and use inherited child classes for the route callbacks. You can also encapsulate all route-group-related logic into separate classes, e.g. having a User class that handles all the /user routes, and so on.
I am building an AJAX web app, using PHP for my back end. I am trying to design a routing system that will let me easily drop new pages in, and let me focus on the Javascript. The actual pages that PHP will be serving up are simple, just views that are essentially containers for Javascript charts (built with d3.js). Thus, my controller won't even have to interact with my model until I start making AJAX calls.
I am new to OOP, especially in back end. I've been doing a bit with Javascript, but I am brand new to incorporating OOP with MVC & solving the issue of routing. I know there are modules/plugins out there that have Routing classes written, but as the back end part of this project is very straight-forward - essentially, how best to serve up an 'About' page on a blog - I'd like to take this opportunity to learn it thoroughly myself.
I have one controller:
<?php
//controller.php
include 'views/view.php';
class Controller
{
public function homeAction() {
$view = new View();
$view->setTemplate('views/home.php');
$view->render();
}
public function categoryAction($category) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$category}.php");
$view->setCategory($category);
$view->render();
}
public function monitorAction($category, $monitor) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$monitor}.php");
$view->setCategory($category);
$view->setMonitor($monitor);
$view->render();
}
}
?>
Right now, I instantiate my controller at the beginning of index.php:
<?php
// Load libraries
require_once 'model.php';
require_once 'controller.php';
$controller = new Controller();
$uri = str_replace('?'.$_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']);
// home action
if ($uri == '/') {
$controller->homeAction();
// /{category}/{monitor}
} elseif (preg_match("#/(.+)/(.+)#", $uri, $matches) ) {
$category = $matches[1];
$monitor = $matches[2];
$controller->monitorAction($category, $monitor);
// /{category}
} elseif (preg_match("#/([^/.]+)#", $uri, $matches) ) {
$category = $matches[1];
$controller->categoryAction($category);
// 404
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && (!empty($_GET)) && $_GET['action'] == 'get_data') {
$function = $_GET['chart'] . "_data";
$dataJSON = call_user_func($function);
header('Content-type: application/json');
echo $dataJSON;
}
?>
I have read a bit about PHP's autoloader, but I'd like to get it down manually first, because I want to make sure and understand the fundamentals.
Is this the appropriate place to instantiate my Controller object?
First, your architecture is facing some major problems. You need a router to take care of your requested URIs by the users and next you need an initialization state for your system. The usual way to create Controllers is to extend a parent class, then in your parent class __construct method you can initialize your children controllers, however, your system isn't in a good shape.
This is a gold link that I never delete:
http://johnsquibb.com/tutorials/mvc-framework-in-1-hour-part-one