Как правильно написать MVC роутер?

111
10 сентября 2021, 04:10

Всем привет. Сделал небольшую MVC. Все контроллеры,модели и виды работают так как надо. Единственное точка входа-очень кривовата. Если кто-то может подсказать как можно сделать еще без require/require_once(Просто какое-то колдовство-с этими require никак не работает). Конструктор ParseUrl не обрабатывает require/require_once/include , хотя файл существует. Через run1 все работает , но , на мой взгляд , это топорный метод. Код

index.php 
    <?php
    namespace ProductReview;
    require_once 'vendor/autoload.php';
    $obj = new ParseUrl();
    $obj->run1();

ParseUrl.php

 class ParseUrl
{
    protected $controller = 'ProductController.php';
    protected $method = 'index';
    protected $params = 'name';

    public function __construct()
    {
        $url = $this->getRoute();
        $controller = ucfirst($url[1] . 'Controller.php');
        print $controller;
        print '<br>';
        print 'Controllers/' . $controller;
        print '<br>';
        if (file_exists(__DIR__ . '/Controllers/' . $controller)) {
            $this->controller = $controller;
            print 'yes';
        } else {
            print 'No';
        }
        print '<br>';
        print __DIR__ . '/Controllers/' . $this->controller;
        require_once 'Controllers/' . $this->controller;
        new $this->controller;
        print_r($this->controller);
    }
    public function getRoute()
    {
//        $uri = explode('/', filter_var(rtrim($_SERVER['REQUEST_URI']), FILTER_SANITIZE_URL));
        $uri = $_SERVER['REQUEST_URI'];
        return $uri;
    }
    public function run1()
    {
        switch ($this->getRoute()) {
            case '/':
                {
                    $obj = new ProductController();
                    $obj->index();
                    break;
                }
            case '/test':
                {
                    $obj = new ProductController();
                    $obj->test1('name');
                    break;
                }
            case '/show':
                {
                    $obj = new ProductController();
                    $obj->show();
                    break;
                }
            case '/form':
                {
                    $obj = new ProductController();
                    $obj->create();
                    break;
                }
            case '/store':
                {
                    $obj = new ProductController();
                    $obj->store($_POST);
                    $obj->index();
                    break;
                }
            case'/reviews':
                {
                    $obj = new ReviewController();
                    $obj->index();
                    break;
                }
            case '/sort/' . $this->params:
                {
                    $obj = new ProductController();
                    $obj->sort($this->params);
                }
            case '/sort/' . $this->params:
                {
                    $obj = new ProductController();
                    $obj->sortDesc($this->params);
                }
            default :
                {
                    $obj = new ProductController();
                    $obj->notFound();
                    break;
                }
        }
    }
Answer 1

я проект ваш не в корне сделал а во вложенной папке, так что учитывайте этот нюанс глядя код ниже.

начну с композера

дальше ваш индекс

<?php
//namespace ProductReview;
use App\ParseUrl;
use Mpakfm\Printu;
require_once 'vendor/autoload.php';
$obj = new ParseUrl();
// Это вместо print_r - не обращаем внимания
Printu::log($obj, '$obj');

Вызвали парсер и все. Теперь посмотрим на парсер

namespace App;
use Mpakfm\Printu;
class ParseUrl {
    protected $controller = 'App\\Controller\\ProductController';
    public function __construct() {
        $url = $this->getRoute();
        //$controller = ucfirst($url[1] . 'Controller.php');
        $controller = 'App\\Controller\\' . ucfirst($url) . 'Controller';
        Printu::log($url, '$url');
        Printu::log($controller, 'controller');
        try {
            $res = new $controller();
        } catch (\Throwable $exception) {
            Printu::log($exception->getMessage(), 'exception');
            $controller = $this->controller;
            Printu::log($controller, 'default controller');
            $res = new $controller();
        }
        Printu::log($res, 'call controller');
        return;
    }
    public function getRoute() {
        //        $uri = explode('/', filter_var(rtrim($_SERVER['REQUEST_URI']), FILTER_SANITIZE_URL));
        $uri = $_SERVER['REQUEST_URI'];
        // для теста беру путь с GET
        if (isset($_GET['page'])) {
            return $_GET['page'];
        }
        return 'product';
    }

Итак что мы делаем, заставляем работать композер вместо нас. У нас есть подключенный к автозагрузчику каталог lib как точка вхождения для namespace App дальше по прав илам psr-4 Контроллер помещаем в соотв папку и делаем там соотв имя и неймспейс:

namespace App\Controller;
use Mpakfm\Printu;
class ProductController {
    public function __construct() {
        Printu::log(true, 'ProductController');
    }
}

Все, теперь парсер получает имя из урла, пристыковывает ему неймспес до контроллеров, делает первую букву большой и вконце дописывает Controller: $controller = 'App\Controller\' . ucfirst($url) . 'Controller';

После остается только запустить и композер сам найдет этот контроллер, если он есть. Если нет - подсунем контроллер по умолчанию и запустим его. Так же запустим через автолоадер композера.

вывод на экран для индекса:

и для какой-либо страницы где контроллер не нашелся:

PS: На всякий случай, после любых изменений файла composer.json нужно в консоли исполнять "composer update", что бы эти изменения применились.

Answer 2

Проблема не с require, а со строкой new $this->controller . В свойство $this->controller установлено значение ProductController.php - файл с таким именем существует, он подключён, класса с таким именем нету. Тут либо сразу пропишите protected $controller = 'ProductController', а при реквайре допишите расширение .php, либо обрезайте расширение при вызове new $this->controller.

READ ALSO
Invalid API key с прямым геокодированием в геокодере

Invalid API key с прямым геокодированием в геокодере

Всем привет! Идея такая: Нужно взять координаты места по его названию и записать их в JSON-файлДальше из этого файла взять координаты и расставить...

160
Объединить две картинки средствами PHP

Объединить две картинки средствами PHP

Загружаемую на сервер картинку необходимо подвергнуть следующей обработке: 1Удалить белый/прозрачный фон; 2

100
Не загружается песня на сайт php

Не загружается песня на сайт php

Не загружаются аудио файлы, с картинками все работает tmp_name пустой ""

73
Как должна выглядеть служба на смартфоне, чтобы принимать push уведомления с сервера без использования Firebase?

Как должна выглядеть служба на смартфоне, чтобы принимать push уведомления с сервера без использования Firebase?

Надо создать приложение (под Android), которое бы принимало push уведомления с моего сервера (на php)Приложения на разных устройствах соответственно...

179