упс — PHP MVC, кто владеет URL?

Я реализую приложение PHP с использованием техники MVC. Краткое введение для загрузки http://site/photos/25?options который переписывает http://site/index.php?page=photos&id=25&options:

  • index.php -> инстанциирует и звонит PhotoController объект
  • PhotoController конкретизирует PhotoView и загружает правильно Photo (модель) в нее
  • PhotoController звонки PhotoView->render()и представления верхнего / нижнего колонтитула
  • PhotoView имеет доступ к Photo модель и другие данные модели, которые ей нужны

PhotoView выведет HTML и хочет гиперссылку на разные страницы. Когда это представление генерирует гиперссылки, я не понимаю, какой слой должен иметь URL-адреса. Варианты, которые я вижу, обрисованы в общих чертах ниже. Пожалуйста, помогите мне выбрать правильный подход.


Слой модели владеет URL

Модели имеют getURL() метод

Преимущества:

  • База View класс может включать свойства Open Graph
  • PhotoView мог бы сделать это: $folderHref = $photo->getFolder()->url

Недостатки:

  • Некоторые контроллеры не имеют ни одной соответствующей модели, например LoginController, AdminControllerКак я могу получить URL для них?

Контроллер владеет URL

Контроллеры имеют url а также title свойства. Чтобы создать ссылку, текущий контроллер создает целевые контроллеры и передает их в представление. Представление обращается к url а также title от пропущенного объекта.

Преимущества:

  • Каждый URL напрямую отображается на контроллер (в index.php), так что обратное кажется чистым

Недостатки:

  • Для представления, для которого нужны URL-адреса для других представлений, потребуется один из следующих уродливых хаков:

    • Запрос неродственного контроллера $folderHref = Controllers\FolderController::urlForFolder($photo->getFolder())

    • Представления, зная слишком много об их контролерах $folderHref = $photo->getFolder()->getControllerAndSetURL()->getURL()

    • Чрезмерная связь между представлением и его контроллером $folderHref = $this->delegate->getURLForFolder($photo->getFolder()) а также $adminHref = $this->delegate->getURLForAdminHref() с delegate имея много методов


У каждого есть URL

Базовый класс OpenGraphObject является родительским классом для Controllers А ТАКЖЕ Models и имеет метод getURL()

Controllers а также Models реализовать это, только если он может быть запущен без аргументов. (Например. PhotoController возвращает NULL, потому что URL зависит от того, какой Photo покажет).

Преимущества:

  • Каждое преимущество выше

Недостатки

  • неразбериха
  • Отзыв моей лицензии на программирование

3

Решение

Чтобы разделить проблемы, я бы разбил это следующим образом:

/**
* Responsible for processing and rendering user output
*/
interface View {

public function render();
}

/**
* Used just to further formalize the messages between Photo and View
*/
interface PhotoView extends View {

public function setPhotoData($id, $title, $link);
}

/**
* Among other things knows how to generate URLs to routes
*/
interface Router {

/**
* Returns the URL to route based on route name
* @param string $routeName
* @param array $params
*/
public function urlTo($routeName, $params = null);
}

Я не собираюсь реализовывать каждый класс, есть множество готовых решений, которые вы можете использовать, например, для шаблонизатора и маршрутизатора.

Примером контроллера для отображения изображения на основе идентификатора может быть:

class PhotoController {

public function displayAction($photoId) {
$photo = Photo::findById($photoId);

$templateFile = $this->pathTo('photo-template.php');

//can be a singleton for example, it holds all the routes for the application
//and knows how to render them based on the name and parameters
$router = AppRouter::instance();

return $photo->render(new ConcretePhotoView($templateFile, $router));
}

}

В этом примере Photo должна иметь возможность извлекать себя из БД и затем отображать себя в PhotoView:

class Photo {

private $id;
private $title;
private $sourceLink;

//other methods...

public function render(PhotoView $view) {
$view->setPhotoData($this->id, $this->title, $this->sourceLink);

return $view->render();
}

}

Допустим, шаблон HTML photo-template.php выглядит так:

<a href="<?= $userLogin['href'] ?>"><?= $userLogin['label'] ?></a>
<a href="<?= $userRegister['href'] ?>"><?= $userRegister['label'] ?></a>
<div>
<img src="<?= $photo['src'] ?>" title="<?= $photo['title'] ?>" />
<a href="<?= $photoLink['href'] ?>"><?= $photoLink['label'] ?></a>
</div>

Должно быть достаточно ясно, какие переменные нужны этому шаблону. Никакой логики в этом, только чистое отображение, хотя некоторые основные элементы управления будут в порядке.

Осталось только кодировать ConcretePhotoView, который будет использовать предоставленный файл шаблона и маршрутизатор для адаптации данных между объектом объекта Photo и конкретным html-шаблоном:

class ConcretePhotoView implements PhotoView {

private $templateEngine;
private $router;

public function __construct($templateFile, Router $router) {
$this->templateEngine = new SomeTemplateEngine($templateFile);
$this->router = $router;
}

public function render() {
//router has a route userLogin with no params
$this->templateEngine->assign('userLogin', array(
'href' => $this->router->urlTo('userLogin'),
'label' => 'Login'
));

$this->templateEngine->assign('userRegister', array(
//router has a route named userRegister that requires no params
'href' => $this->router->urlTo('userRegister'),
'label' => 'Register'
));

return $this->templateEngine->render();
}

public function setPhotoData($id, $title, $link) {
$this->templateEngine->assign('photo', array(
'src' => $link,
'title' => $title
));

$this->templateEngine->assign('photoLink', array(
//router has a route named photo that requires an id
'href' => $this->router->urlTo('photo', array('id' => $id)),
'label' => $title
));
}

}

Напоследок ответить на вопрос «Кому принадлежат URL-адреса?» — Маршрутизатор. Это знание заключено в капсулу и зависит от конфигурации приложения. Другие объекты используют его для генерации ссылок.

0

Другие решения

Других решений пока нет …