Вот подход, который я использую при создании веб-сайтов на PHP с использованием шаблона MVC / разделения проблем:
Фреймворк, который я использую, состоит из трех основных частей:
- Модели - классы PHP. Я добавляю к ним методы для получения и сохранения данных. Каждая модель представляет отдельный тип сущности в системе: пользователи, страницы, сообщения в блогах.
- Просмотры - шаблоны Smarty. Здесь живет HTML.
- Контроллеры - классы PHP. Это мозги приложения. Обычно URL-адреса на сайте вызывают методы класса. example.com/user/show/1 вызовет метод $ user_controller-> show (1). Контроллер извлекает данные из модели и передает их представлению.
Каждая из этих частей имеет определенную работу или «заботу». Задача модели - обеспечить понятный интерфейс для данных. Обычно данные сайта хранятся в базе данных SQL. Я добавляю в модель методы для извлечения и сохранения данных.
Задача представления - отображать данные. Вся разметка HTML отображается в представлении. Логика для обработки "зебры" для таблицы данных идет в представление. Код для обработки формата, в котором должна отображаться дата, входит в представление. Мне нравится использовать шаблоны Smarty для представлений, потому что они предлагают несколько хороших функций для обработки подобных вещей.
Задача контроллера - действовать как посредник между пользователем, моделью и представлением.
Давайте посмотрим на примере того, как они сочетаются и в чем заключаются преимущества:
Представьте себе простой блог-сайт. Основная часть данных - это пост. Также представьте, что сайт отслеживает количество просмотров публикации. Для этого создадим таблицу SQL:
posts
id date_created title body hits
Теперь предположим, что вы хотите показать 5 самых популярных постов. Вот что вы можете увидеть в приложении, отличном от MVC:
$sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT 5";
$result = mysql_query($sql);
while ($row = mysql_fetch_assoc($result)) {
echo "<a href="post.php?id=$row['id']">$row['title']</a><br />";
}
Этот фрагмент довольно прост и хорошо работает, если:
- Это единственное место, где вы хотите показывать самые популярные сообщения
- Вы никогда не хотите менять внешний вид
- Вы никогда не решите изменить то, что такое "популярный пост"
Представьте, что вы хотите показать 10 самых популярных постов на главной странице и 5 самых популярных на боковой панели на подстраницах. Теперь вам нужно либо продублировать приведенный выше код, либо поместить его во включаемый файл с логикой, чтобы проверить, где он отображается.
Что, если вы хотите обновить разметку для домашней страницы, чтобы добавить класс «новое сообщение» к сообщениям, которые были созданы сегодня?
Предположим, вы решили, что сообщение популярно, потому что в нем много комментариев, а не хитов. База данных изменится, чтобы отразить это. Теперь каждое место в вашем приложении, где отображаются популярные сообщения, должно быть обновлено, чтобы отразить новую логику.
Вы начинаете видеть снежный ком сложной формы. Легко увидеть, как в ходе проекта становится все труднее поддерживать в рабочем состоянии. Также учитывайте сложность, когда над проектом работают несколько разработчиков. Должен ли разработчик консультироваться с разработчиком базы данных при добавлении класса к выходным данным?
Использование подхода MVC и принудительное разделение задач в вашем приложении может смягчить эти проблемы. В идеале мы хотим разделить его на три области:
- логика данных
- логика приложения
- и логика отображения
Посмотрим, как это сделать:
Начнем с модели. У нас будет класс $post_model
, и мы дадим ему метод с именем get_popular()
. Этот метод вернет массив сообщений. Кроме того, мы дадим ему параметр, чтобы указать количество возвращаемых сообщений:
post_model.php
class post_model {
public function get_popular($number) {
$sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT $number";
$result = mysql_query($sql);
while($row = mysql_fetch_assoc($result)) {
$array[] = $row;
}
return $array;
}
}
Теперь для домашней страницы у нас есть контроллер, мы назовем его «домашним». Представим, что у нас есть схема маршрутизации URL-адресов, которая вызывает наш контроллер при запросе домашней страницы. Его задача - получить популярные посты и привести их в правильное русло:
home_controller.php
class home_controller {
$post_model = new post_model();
$popular_posts = $post_model->get_popular(10);
// This is the smarty syntax for assigning data and displaying
// a template. The important concept is that we are handing over the
// array of popular posts to a template file which will use them
// to generate an html page
$smarty->assign('posts', $popular_posts);
$smarty->view('homepage.tpl');
}
Теперь посмотрим, как будет выглядеть представление:
homepage.tpl
{include file="header.tpl"}
// This loops through the posts we assigned in the controller
{foreach from='posts' item='post'}
<a href="post.php?id={$post.id}">{$post.title}</a>
{/foreach}
{include file="footer.tpl"}
Теперь у нас есть основные части нашего приложения, и мы можем видеть разделение проблем.
Модель занимается получением данных. Он знает о базе данных, он знает о SQL-запросах и операторах LIMIT. Он знает, что должен вернуть хороший массив.
Контроллер знает о запросе пользователя, что он просматривает домашнюю страницу. Он знает, что на главной странице должно отображаться 10 популярных сообщений. Он получает данные от модели и передает их представлению.
Представление знает, что массив сообщений должен отображаться как последовательность тегов ахора с тегами разрыва после них. Он знает, что у сообщения есть заголовок и идентификатор. Он знает, что заголовок сообщения должен использоваться для текста привязки и что идентификатор сообщения должен использоваться в href. Представление также знает, что на странице должны быть верхний и нижний колонтитулы.
Также важно упомянуть то, что не знает каждая часть.
Модель не знает, что популярные сообщения отображаются на главной странице.
Контроллер и представление не знают, что сообщения хранятся в базе данных SQL.
Контроллер и модель не знают, что после каждой ссылки на сообщение на главной странице должен стоять тег разрыва.
Итак, в этом состоянии мы установили четкое разделение задач между логикой данных (модель), логикой приложения (контроллер) и логикой отображения (представление). Ну что теперь? Мы взяли небольшой простой фрагмент PHP и разбили его на три запутанных файла. Что это нам дает?
Давайте посмотрим, как разделение проблем может помочь нам в решении упомянутых выше проблем. Повторяю, мы хотим:
- Показывать популярные сообщения на боковой панели на подстраницах
- Выделяйте новые сообщения с помощью дополнительного класса css
- Измените базовое определение "популярного сообщения"
Чтобы отображать популярные сообщения на боковой панели, мы добавим два файла на нашу подстраницу:
Контроллер подстраницы ...
subpage_controller.php
class subpage_controller {
$post_model = new post_model();
$popular_posts = $post_model->get_popular(5);
$smarty->assign('posts', $popular_posts);
$smarty->view('subpage.tpl');
}
... и шаблон подстраницы:
subpage.tpl
{include file="header.tpl"}
<div id="sidebar">
{foreach from='posts' item='post'}
<a href="post.php?id={$post.id}">{$post.title}</a>
{/foreach}
</div>
{include file="footer.tpl"}
Новая подстраница контроллер знает, что на подстранице должно отображаться только 5 популярных сообщений. Подстраница представление знает, что подстраницы должны помещать список сообщений в div боковой панели.
Теперь на главной странице мы хотим выделить новые сообщения. Этого можно добиться, изменив файл homepage.tpl.
{include file="header.tpl"}
{foreach from='posts' item='post'}
{if $post.date_created == $smarty.now}
<a class="new-post" href="post.php?id={$post.id}">{$post.title}</a>
{else}
<a href="post.php?id={$post.id}">{$post.title}</a>
{/if}
{/foreach}
{include file="footer.tpl"}
Здесь представление обрабатывает всю новую логику отображения популярных сообщений. Контроллеру и модели не нужно было ничего знать об этом изменении. Это чисто дисплейная логика. Список подстраниц продолжает отображаться, как и раньше.
Наконец, мы хотели бы изменить популярный пост. Вместо того, чтобы основываться на количестве обращений к странице, мы хотели бы, чтобы он основывался на количестве комментариев к посту. Мы можем применить это изменение к модели:
post_model.php
class post_model {
public function get_popular($number) {
$sql = "SELECT * , COUNT(comments.id) as comment_count
FROM posts
INNER JOIN comments ON comments.post_id = posts.id
ORDER BY comment_count DESC
LIMIT $number";
$result = mysql_query($sql);
while($row = mysql_fetch_assoc($result)) {
$array[] = $row;
}
return $array;
}
}
Мы увеличили сложность логики "популярного поста". Однако, как только мы внесем это изменение в модель в одном месте, новая логика будет применяться повсюду. Домашняя страница и дополнительная страница, без каких-либо других изменений, теперь будут отображать популярные сообщения на основе комментариев. Нашему дизайнеру не нужно было в этом участвовать. На разметку это не повлияет.
Надеюсь, это наглядный пример того, как разделение логики данных, логики приложения и логики отображения может упростить разработку вашего приложения. Изменения в одной области, как правило, меньше влияют на другие области.
Следование этому соглашению - не волшебная палочка, которая автоматически сделает ваш код идеальным. И вы, несомненно, столкнетесь с проблемами, где гораздо менее ясно, где должно быть разделение. В конце концов, все дело в управлении сложностью приложения.
Вы должны хорошо продумать, как вы строите свои модели. Какие интерфейсы они будут предоставлять (см. Ответ Грегори о контрактах)? С каким форматом данных предполагается работать с контроллером и представлением? Если подумать об этом заранее, в будущем все станет проще.
Кроме того, при запуске проекта могут возникнуть некоторые накладные расходы, чтобы все эти части хорошо работали вместе. Существует множество фреймворков, которые предоставляют строительные блоки для моделей, контроллеров, механизмов шаблонов, маршрутизации URL-адресов и многого другого. См. Множество других сообщений на SO для предложений по фреймворкам PHP MVC. Эти фреймворки помогут вам начать работу, но вы, как разработчик, отвечаете за управление сложностью и обеспечение разделения задач.
Также отмечу, что приведенные выше фрагменты кода - это просто упрощенные примеры. У них могут (скорее всего) быть ошибки. Однако по структуре они очень похожи на код, который я использую в своих проектах.
person
GloryFish
schedule
12.03.2009