Я реализую приложение 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 GraphPhotoView
мог бы сделать это: $folderHref = $photo->getFolder()->url
Недостатки:
LoginController
, AdminController
Как я могу получить URL для них?Контроллер владеет URL
Контроллеры имеют
url
а такжеtitle
свойства. Чтобы создать ссылку, текущий контроллер создает целевые контроллеры и передает их в представление. Представление обращается кurl
а такжеtitle
от пропущенного объекта.
Преимущества:
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
покажет).
Преимущества:
Недостатки
Чтобы разделить проблемы, я бы разбил это следующим образом:
/**
* 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-адреса?» — Маршрутизатор. Это знание заключено в капсулу и зависит от конфигурации приложения. Другие объекты используют его для генерации ссылок.
Других решений пока нет …