Bonjour,

Ce que je veux

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)

Ce que j'ai comme code

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 ?

3 réponses


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) ;)

je débute vraiment en POO alors je ne comprend pas du tout comment faire cela :-/

  • Dans ton fichier Autoloader.php, méthode statique autoload, enlève la condition if(strpos($class, __NAMESPACE__ . "\\") === 0) et $class = str_replace(__NAMESPACE__ . "\\", '', $class);.
  • Dans ton fichier 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)