Как правильно сделать/доработать router для MVC?

Добрый день! Сейчас изучаю MVC и пытаюсь приспособить один из примеров (habrahabr.ru/post/150267 ) под свой сайт новостей.

На главной странице есть список новостей, клик на "Читать дальше" (<a href=/fullnews/{$v['id']}>) пепрекидывает на dpwork.loc/fullnews/15 (хочу типу как здесь https://toster.ru/q/265105) 15 это номер статьи.
Но, тогда роутер записывает 15 в название action(метода) и ничего не работает, число также должно передаться в метод, что б с помощью него получить нужную статью.
Используя костыли(рядом комметарий КАПСОМ) я немного подшаманил в роутере.
<?php
class Route
{

	static function start()
	{
		// контроллер и действие по умолчанию
		$controller_name = 'Main';
		$action_name = 'index';
		$id = "";
		$routes = explode('/', $_SERVER['REQUEST_URI']);

		// получаем имя контроллера
		if ( !empty($routes[1]) )
		{	
			$controller_name = $routes[1];
		}
		
		// получаем имя экшена
		if ( !empty($routes[2]) )
		{	
			$action_name = $routes[2];
			if(is_numeric($action_name))                   // ЕСЛИ action_name БЫЛ ЧИСЛОМ ПРИСВАИВАЛ ЕМУ имя по умолчания, после прописывал переменую с числом
			{
				$action_name = 'index';
				$id = $routes[2];
			}
		}



		// добавляем префиксы
		$model_name = 'Model_'.$controller_name;
		$controller_name = 'Controller_'.$controller_name;
		$action_name = 'action_'.$action_name;

		
		/*echo "Model: $model_name <br>";
		echo "Controller: $controller_name <br>";
		echo "Action: $action_name <br>";
		echo $id;*/

		// подцепляем файл с классом модели (файла модели может и не быть)

		$model_file = strtolower($model_name).'.php';
		$model_path = "application/models/".$model_file;
		if(file_exists($model_path))
		{
			include "application/models/".$model_file;
		}

		// подцепляем файл с классом контроллера
		$controller_file = strtolower($controller_name).'.php';
		$controller_path = "application/controllers/".$controller_file;
		if(file_exists($controller_path))
		{
			include "application/controllers/".$controller_file;
		}
		else
		{
			
			Route::ErrorPage404();
		}
		
		// создаем контроллер
		$controller = new $controller_name;
		$action = $action_name;		
		if(method_exists($controller, $action))
		{
			if (!is_null($id)) 
			{
				$controller->id = $id;                                      // ЕСЛИ после первой проверки данные записывались в переменную id, Перед основным действием значения передавалось в свойство класса контролера для получения по нему статьи 
			}
			// вызываем действие контроллера
			$controller->$action();
		}
		else
		{
			Route::ErrorPage404();
		}
	}
	function ErrorPage404()
	{
        $host = 'http://'.$_SERVER['HTTP_HOST'].'/';
        header('HTTP/1.1 404 Not Found');
		header("Status: 404 Not Found");
		header('Location:'.$host.'404');
    }
}

контроллер
<?php

class Controller_fullnews extends Controller
{
	public $id;

	function __construct()
	{
		$this->model = new Model_fullnews;
		$this->view = new View;
	}

	function action_index()
	{	
		$data = $this->model->get_data($this->id);	
		$this->view->generate('fullnews_view.php','template_view.php',$data);
	}
}


модель
<?
class Model_fullnews extends Model {
	public  function get_data($id) {
		$cat_news = "SELECT * FROM `news` WHERE `id` = $id";
		$getnews = DB::obj()->connect()->query($cat_news);
			if(is_null($getnews)) {
				return [];
			}
		return $getnews->fetchALL(PDO::FETCH_ASSOC);
}
}

После костылей переходя по такой ссылке dpwork.loc/fullnews/15, я получал нужную статью и общую разметку. Но, файл css перестал подгружаться, потом разобравшись понял, что загружается он не с index.php, а с template_view.php который находиться в другой папке.

Как правильно реализовать данный вопрос?
Или как довести до костыль(но это на самый крайний случай)?
P.S. Если вы хотите помочь ответом, но вам, что то не понятно, из за плохо описаной проблемы. Задайте доп. вопрос в комментариях, я сразу отвечу. Извините, если плохо описано.
  • Вопрос задан
  • 2784 просмотра
Пригласить эксперта
Ответы на вопрос 4
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Может все же написать статью "что такое MVC"... а то народ путается....

Вот там в статье схемка допустим... занятная... две стрелочки между моделью и представлением - это одна стрелочка, вью просто забирает текущее состояние модели. И она строго в сторону view, описывает поток данных. (причем в канонической трактовке, для GUI а не для бэкэнда, это обзервабл связь, то есть view навешивает обработчики событий на модель и подстраивается под текущее состояние модели. Модель поменялось - view обновилось. Но на сервере такого быть обычно не может потому этот момент можно опустить).

view в контексте сервера не должен дергать контроллер (да и вообще он о контроллере знать ничего не знает). Ну то есть это не тот ж view что у нас модельку дергает. Это какой-то другой view, или представление запроса, инпуты и кнопочки. http запрос.

Только одна стрелочка правильная - что контроллер просит модель изменить состояние.

Так, отвлекся я чойто...

Возьмите любой готовый роутер, не пишите свой, это пустое.
Ответ написан
thewind
@thewind
php программист, front / backend developer
В контроллере можно сделать magic метод __call, вызов которого будет происходить для всех ваших fullnews/XXX, а из него вызывать какой-нибудь protected метод того же контроллера action_item()

protected function action_item($news_id){}
public function __call($method, $args){
    return $this->action_item($method);
}
Ответ написан
Stalker_RED
@Stalker_RED
Насколько я понимаю, вы сперва написали простейший парсер URL, который просто разбивает его по слэшам и вызывает module/action. Теперь вы хотите добавить каких-то исключений, особенный правил для разных страничек или разделов.

Тут такая штука, эти "особенные" правила нужны настолько часто, что как правило эти как раз "особые правила" и составляют большую часть конфига :)

Посмотрите как устроен роутинг в современных фреймворках, может подхватите свежих идей.

Вот фрагмент из symfony
video:
    pattern:  /watch/{id}/{slug}
    defaults: { _controller: SiteBundle:Video:watch }  # в URL не указан модуль, зато он прописан в конфиге роутинга
    requirements: { id: "\d+", slug: "[\w-]+" # обязательные параметры: id в виде числа и slug состоящий из букв и дефисов


Документация: symfony-gu.ru/documentation/ru/html/book/routing.html
Код роутинга: https://github.com/symfony/routing

И да, если хотите, можете этот модуль целиком утащить себе в проект, вместо того чтобы писать свой.

Еще вроде в Yii неплохо всё устроено www.yiiframework.com/doc-2.0/guide-runtime-routing.html
Ответ написан
Комментировать
R0s0maxa
@R0s0maxa
junior web-developer
Писать самому что-то - это адовый бред, вы только потратите время зря. Изучайте и пользуйте уже готовые решения - для этого они и были созданы.
Если охота поковыряться - лучшим выбором собрать своего франкенштейна из уже готовых модулей.
Вот вам интересный тутор по сборке, как раз с комментами автора о том - какие паттерны/антипаттерны он юзает и для чего это вообще надо.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы