Bonjour,
Ce que je veux est assez simple à comprendre, c'est a dire que j'ai suivi ce tutoriel: https://www.grafikart.fr/tutoriels/php/router-628
Et maintenant je souhaite que chaque controller se trouve à la racine du repertoire (en au même niveau que le dossier App et non dedans)
C'est à dire que mon arborescence devrai juste être:
-App (Gestion des routes et Controller principal)
-Articles (avec a l'interieur les dossiers suivant: Controller, Vue, Model)
-Posts (avec a l'interieur les dossiers suivant: Controller, Vue, Model)
A la racine j'ai:
index.php
<?php
// Inclusion de l'autoloader
require "App/Autoloader.php";
//
\App\Autoloader::register();
$router = new App\Router\Router($_GET['url']);
$router->get('/', function(){ echo 'Homepage';});
$router->get('/posts', function(){ echo 'Tous les articles';});
$router->get('/posts/:slug-:id/:page', "Posts#show")->with('id', '[0-9]+')->with('page','[0-9]+')->with('slug', '[a-z\0-9]+');
$router->get('/posts/:slug-:id/', "Posts#show")->with('id', '[0-9]+')->with('slug', '[a-z\0-9]+');
$router->get('/articles/:id-:slug', "Articles#show")->with('id', '[0-9]+')->with('slug', '[a-z\0-9]+');
$router->get('/article/:id-:slug', function($id, $slug) use ($router) {echo $router->url("Posts#show", ['id' => 1, 'slug' => 'salut-les-gens']);}, "posts.show")->with('id', '[0-9]+')->with('slug', '[a-z\0-9]+');
//$router->get('/posts/:slug-:id', function($slug, $id) use ($router) {echo $router->url("posts.show", ['id' => 1, 'slug' => 'salut-les-gens']);}, "posts.show")->with('id', '[0-9]+')->with('slug', '[a-z\0-9]+');
$router->post('/posts/:id', function($id){ echo 'Poster pour l\'article '.$id. "<pre>" . print_r($_POST) . '</pre>';});
$router->run();
Puis un dossier Posts, avec à l'interieur un dossier Controller qui contiens:
PostsController.php
<?php
namespace Posts\Controller;
class PostsController
{
public function show($slug,$id,$page = 1){
echo "Je suis l'article N:$id ayant pour titre $slug et je suis a la page $page";
}
}
Puis dans mon dossier App, j'ai un fichier autoloader:
<?php
namespace App;
class Autoloader
{
static function register(){
spl_autoload_register(array(__CLASS__, 'autoload'));
}
static function autoload($class){
if(strpos($class, __NAMESPACE__ . "\\") === 0){
$class = str_replace(__NAMESPACE__ . "\\", '', $class);
$class = str_replace("\\", "/", $class);
require $class . ".php";
}
}
}
Et toujours dans le dossier App, J'ai un dossier Router qui contient:
Router.php
<?php
namespace App\Router;
class Router{
private $url;
private $routes = [];
private $nameRoutes = [];
public function __construct($url)
{
$this->url = $url;
}
public function get($path, $callable, $name = null)
{
return $this->add($path,$callable,$name, 'GET');
}
public function post($path, $callable, $name = null)
{
return $this->add($path,$callable,$name, 'GET');
}
private function add($path, $callable, $name, $method){
$route = new Route($path, $callable);
$this->routes[$method][] = $route;
if(is_string($callable) && $name === null){
$name = $callable;
}
if($name){
$this->nameRoutes[$name] = $route;
}
return $route;
}
public function run()
{
if(!isset($this->routes[$_SERVER['REQUEST_METHOD']]))
{
throw new RouterException('REQUEST_METHOD does not exist');
}
foreach($this->routes[$_SERVER['REQUEST_METHOD']] as $route){
if($route->match($this->url)){
return $route->call();
}
}
throw new RouterException('No matching routes');
}
public function url($name, $params = []){
if(!isset($this->nameRoutes[$name])){
throw new RouterException('No route matches this name');
}
return $this->nameRoutes[$name]->getUrl($params);
}
}
Et Route.php
<?php
namespace App\Router;
class Route{
private $path;
private $callable;
private $matches = [];
private $params = [];
public function __construct($path, $callable)
{
$this->path = trim($path, '/');
$this->callable = $callable;
}
public function with($param, $regex){
$this->params[$param] = str_replace('(', '(?:', $regex);
return $this;
}
public function match($url){
$url = trim($url, '/');
$path = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], $this->path);
$regex = "#^$path$#i";
if(!preg_match($regex, $url, $matches)){
return false;
}
array_shift($matches);
$this->matches = $matches;
return true;
}
private function paramMatch($match){
if(isset($this->params[$match[1]]))
{
return '('. $this->params[$match[1]] . ')';
}
return '([^/]+)';
}
public function call(){
if(is_string($this->callable)){
$params = explode('#', $this->callable);
//die(print_r($params));
$controller = "App\\Controller\\".$params[0]."Controller";
$controller = new $controller();
return call_user_func_array([$controller, $params[1]], $this->matches);
}else{
return call_user_func_array($this->callable, $this->matches);
}
}
public function getUrl($params){
$path = $this->path;
foreach ($params as $k => $v) {
$path = str_replace(":$k", $v, $path);
}
return $path;
}
}
Auriez-vous le moyens d'obtenir ce dont je souhaite ?
Yop.
Oui, il faut améliorer/modifier ton autoloader pour faire en sorte de require
les autres classes dont le namespace n'est pas __NAMESPACE__
. ^-^
Et modifier la variable $controller
de ta méthode call
(fichier Route.php
) pour y mettre le bon nom de classe (+ namespace) ;)
Autoloader.php
, méthode statique autoload
, enlève la condition if(strpos($class, __NAMESPACE__ . "\\") === 0)
et $class = str_replace(__NAMESPACE__ . "\\", '', $class);
.Route.php
, méthode call
, remplace $controller = "App\\Controller\\" . $params[0] . "Controller";
par $controller = $params[0] . "\\Controller\\" . $params[0] . "Controller";
. Ceci retournera Posts\Controller\PostsController
(au lieu de App\Controller\PostsController
)