Pull to refresh

Comments 175

Вместо require может лучшим будет require_once использовать?
Лучше использовать автоматическую загрузку классов, иначе все require будет отслеживать уже нереально.
А еще лучше начать использовать namespace'ы с автомотической загрузкой классов и тогда в этом плане наступит полная эйфория :). Хотя я сам и пользуюсь этим способом, код от этого лучше выглядеть не начинает :( вместо конструкции require*/include* появляется обратный слеш :(
Ну, код

use Vendor\App\Models;

$blog = new Blog();
$blog->posts[] = new Post();

выглядит лучше чем

require_once 'lib/vendor/app/models/blog.php';
require_once 'lib/vendor/app/models/post.php';

$blog = new Vendor_App_Models_Blog();
$blog->posts[] = new Vendor_App_Models_Post();

Или нет? :)
100% у меня он точно такой же

только вот эту мелочь стоит гда нить ранее объявить

spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();

но вот посмотришь на код глубже и порой от такого количества обратных слешей иногда берет дрожжжж :)
но это больше проблема namespace'ов нежли способа загрузки файлов :)
еще кое что забыл :)

define(«APP_PATH», __DIR__. DS. '..'. DS. 'app'. DS);
set_include_path(APP_PATH); // adding new path to include_path
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();

вот теперь все выглядит как надо.
От require можно избавиться, и останется только new Vendor_App_Models_Blog().
Это лучше, чем use Vendor\App\Models; new Blog(); по двум причинам:
1. В любом месте листинга видно, что за класс создается
2. Создание объекта класса выглядит всегда одинаково, независимо от use в начале файла.

думаю это на 100% зависит от выбранного вами подхода при «строительстве» вашего проекта.

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

Если же вы делаете все с нуля то нет причин не использовать разумный подход, и лучше планировать свою архитектуру.
Даже в таких случаях я предпочту new \Vendor\App\Models\Blog();, а написав new \Vendor\App\Models\Post();, сделаю небольшой рефакторинг и введу use ради DRY.
Вводя этот use, вы теряете в понимании кода. Я не могу сказать, что хуже — нагромождение из \ и нэймспейсов, не понятно, к какому нэймспэйсу принадлежащие классы или же PSR-именованные классы. Мне все кажется плохим ((

ЗЫ: В Google C++ Style Guide конструкция use запрещена.
К нэймспэйсам так или иначе приходят в сложных приложениях, вопрос в том использовать их нативные или эмуляцию через _. Что «всё плохо» почти согласен, но вариант с use мне кажется оптимальным из трёх. Кроме всего прочего он явно показывает (если в в соглашениях принять что он обязателен даже при единичном использование, а не только ради DRY) зависимости текущего файла.
Неожидал увидеть Ололошу на хабре.
>> Подход с множеством точек взаимодействия
это называется точка входа в приложение.
>>В общем, сегодня речь пойдет о самом популярном (разве что после Singleton) шаблоне проектирования MVC
думаю такое сравнение некорректно, т.к. MVC это шаблон другой, и он не входит в список тех 27 из книги GoF.
Картинка порадовала, правда на ней я бы руль обозвал контроллером) Кстати еще, поспешите добавить в статью дисклеймер про то что эта статья для совсем новичков, а то можете рассчитывать что вас раскритикуют) я тоже сначала хотел, но потом подумал, что это будет выглядеть как-то глупо) кому надо из новичков тот посмотрит, а те кто используют fw конечно так пролестают просто на предмет грубых или глупых каких-то моментов. Я смотрю вы тут уже 3 части написали, оперативно однако.
да вроде из контекста очевидно, что для новичков
Библиотека профессионала: «новичкам — от новичков»
думаю такое сравнение некорректно, т.к. MVC это шаблон другой, и он не входит в список тех 27 из книги GoF.
Потому что в статье написано неправильно, MVC — это не шаблон, а парадигма. И построена может быть на разных шаблонах или без них вообще. А шаблоном может назвать тот, кто не очень понимает что такое шаблон. Просто помешались уже на паттернах кругом все.
Да вы абсолютно правы :)
Формально говоря, MVC является архитектурным шаблоном приложения, а тот же Singleton шаблон более низкоуровневый. Или Фаулер, по-вашему, не понимает что такое шаблоны?
Полагаю, что понимает. Но это уже просто вопрос терминов. Суть MVC в идее «разделение V и C (и M)», и на самом деле это всё. Сложно это «шаблоном» назвать, имхо. То, что MVC — это может быть несколько каких-либо «классических» шаблонов Вы согласны?
Отнюдь, это именно шаблон. Он, кстати, описан в книге «Банды четырех», только не в каталоге, а в первой части, где идет рассказ о Smalltalk.
В новой редакции он описан как составной шаблон.
Не читал еще новую ;)
Ну, я рад за автора сайта «Справочник паттерны проектирования», но к чему это? И MVC там стоит в одном разделе с «шаблонизатором» и «контроллером страницы», обозванным «паттерны веб-представления», что, если даже посчитать это более-менее корректным, подтверждает как раз мои слова выше.
Эх, такие бы статьи да во времена, когда я только начинал изучать PHP.
Новичкам, думаю, очень поможет.

Для меня, к счастью это уже не актуально. Сменил профу на JS.
Для новичков расжовано хорошо, пару замечений:
— Почему без namespace?
— Зачем закрывающий тег ?>, потенциальны проблемы с сессиями
Я не знаю php. Подскажите по вашему 2-му замечанию:
— почему не должен стоять закрывающий тег ?>
— почему потенциальные проблемы с сессиями в приведеном примере.

Спасибо!
Закрывающий тег в PHP вообще можно не ставить, если в файле только PHP код.
1. Всё что будет после ?> окажется контентом.
2. Некоторые редакторы могут добавлять последнюю пустую строку. Ну или случайно > может оказаться не последним байтом в файле.
3. Когда этот файл будет за'require'ен, этот хвост будет отправлен на клиент.
4. После начала отправки контента нельзя установить заголовки, в т.ч. куки, в т.ч. сессионные куки.

Вывод — особенно в подключаемых файлах, а лучше вообще всегда, нужно убирать ?>, если после него нет осмысленного контента.
Спасибо большое за развернутый ответ! Почерпнул для себя полезную информацию.
И почему в сотне таких одинаковых статей про php и mvc — нет ни слова как убрать код php из вида и прикрутить какой либо шаблонизатор. Помоему если вы это сделаете — Ваша статья будет качественно отличаться от других.
Я планировал написать про шаблонизацию, но понял, что текст статьи в таком случае будет сильно раздут и к тому же может породить очередной спор на тему, а стоит ли использовать сторонний шаблонизатор, ведь PHP сам по себе является шаблонизатором. О реализации простого шаблонизатора вы можете почитать в следующих статьях: "Мой родной PHP шаблонизатор " и "Делим код пополам или представление по шаблону в PHP ". Кстати, эти ссылки приведены в конце статьи!
Может быть потому, что шаблонизаторы к ПХП прикручивать не надо?
Или потому, что в одной статье лучше разбирать одну тему, а не две?
В данном случае у автора получилась уже не одна статья, так что это не ограничение :-)

А шаблоны к PHP прикручивать надо — как раз по причине того, что из хорошего шаблонизатора сделали посредственный язык программирования. За двумя зайцами погонишься…
Достаточно отделить вид от всего остального, инкапсулировать в него шаблонизатор (или использовать шаблонизатор в качестве вида) и обеспечить более-менее слабую связанность, чтобы заменить один шаблонизатор на другой.
PHP сам по себе чем плох в роли шаблонизатора?

Для каких задач нужно что-то еще?
Нет автоэкранирования вывода для HTML, приходит писать минимум <?= e($var) ?> вместо {{var}}.

Нетривиальна реализация наследования шаблонов.

Не предоставляет изоляции шаблона от остальной программы и глобального нэймспэйса. То есть в шаблоне вы можете сделать ровно то же самое, что и в самой программе, например выполнить eval($_GET['expr']);

Не очень понял.

1. Что за автоэкранирование и зачем? Если данные не готовы для вывода, разве «модель» не должна их подготовить? Или «модель представления»?

2. Видимо я не сталкивался еще с такой проблемой… С другой стороны реализация разве не зависит от вас?

3. А зачем специально изолировать шаблон? Это же ваша программа, просто не используйте там eval($_GET['expr']);.

Правильно ли я понял, что надо сначала изобрести себе кучу проблем, и тогда сторонний шаблонизатор обязательно поможет их решить?
1. Чтобы не приходилось практически в каждом месте html-шаблона писать писать htmlspecialchars($var, ENT_QUOTES, 'UTF-8');. Нет, не модель, модель не знает будут данные в html выданы или, например в JSON. И даже не модель представления, имхо, хотя тут могут быть варианты.

2. Зависит-то то она зависит, но решать её средствами PHP «в лоб» нетривиально, а код шаблона не очень читабельный, например
<?php $view->extend('::base.html.php') ?>

<?php $view['slots']->set('title', 'My cool blog posts') ?>

<?php $view['slots']->start('body') ?>
    <?php foreach ($blog_entries as $entry): ?>
        <h2><?php echo $entry->getTitle() ?></h2>
        <p><?php echo $entry->getBody() ?></p>
    <?php endforeach; ?>
<?php $view['slots']->stop() ?>

по сравнению с
{% extends '::base.html.twig' %}

{% block title %}My cool blog posts{% endblock %}

{% block body %}
    {% for entry in blog_entries %}
        <h2>{{ entry.title }}</h2>
        <p>{{ entry.body }}</p>
    {% endfor %}
{% endblock %}


3. Не всегда шаблон пишет разработчик движка, это может быть верстальщик, веб-мастер, а то и конечный пользователь (индивидуальное оформление своей странички, например). В общем лучше иметь возможность включить режим «песочницы», чем не иметь.
1. Спорно. Во-первых htmlspecialchars($var, ENT_QUOTES, 'UTF-8'); можно сделать и так:
foreach ($data as $k=>$v)  $data[$k] = htmlspecialchars($v, ENT_QUOTES, 'UTF-8');

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

Да и в модели она может быть. Модель не знает, как будут использованы данные. Но вы-то знаете :) Вызывайте метод типа getDataForHTML() или getDataForJSON().

2. Для меня удивительно нечитабельны оба ваших примера :).

У меня например, в шаблоне страницы может быть конструкция <?=show("some_block_template", $page);?>, которая значит, что надо в этом месте надо вставить шаблон некоего блока страницы. У каждого раздела сайта этот блок («заголовок, меню, строка поиска, whatever) может иметь разный шаблон. Если у данного раздела такой шаблон не определен используется базовый, если базового нет, то используется вшитый шаблон по-умолчанию (где просто написано, что шаблон такого-то блока для страницы не определен).

Я не уверен, что это то самое наследование шаблонов про которое вы говорите. Если что вы меня поправите.

3. Тут согласен. В описанной вами ситуации разумнее использовать сторонний шаблонизатор, а не php.

1. Это сработает в случае если все данные нужно экранировать, а если часть надо, а часть не надо? Велик риск пропустить что-то в белом или чёрном списке.

2. Интеллектуальный include, который в зависимости от контекста возвращает разные результаты? То есть у вас каждая страница состоит, утрируя, из <html><?=show(«head_template», $page);?><?=show(«body_template», $page);?></html>? Я правильно понял? Если так, то вы наследуете блоки, но не наследуете основной layout.

1. Ваш изначальный посыл был в том, что все надо экранировать и запись {{var}} лучше, чем php-fаналог. Разве нет?
2. Я не делю основной layout и блоки, layout это просто блок «page», который включает в себя другие блоки (или не включает, если это не надо).
1. Не всё, но по умолчанию всё экранировано, если нужны raw данные, так и пишем {{var|raw}}

2. А как вы из шаблона ссылаетесь на layout?
1. ок. Поэтому вопросу делаю для себя вывод: без шаблонизатора не обойтись, когда надо предоставить возможность создания/изменения шаблонов человеку со стороны (не разработчику) и особенно пользователю. В других случаях выбор использовать или не использовать шаблонизатор (а не сам PHP) это вопрос технических требований и личного вкуса разработчика.

2. Если я правильно понял, что такое layout (<html><head></head><body><header><div class="content"></div><footer></footer></body></html>), то я никак не ссылаюсь на него из шаблонов других блоков. Я придерживаюсь концепции независимых блоков.
1. Я бы еще добавил, если велика вероятность, что код придётся поддерживать через некоторое время, когда детали забудутся. Или на все проекты должны быть единые соглашения.

2. Вот есть, допустим, блок user, есть post, есть admin, есть layout базовый (для user и post), куда блоки должны вставиться в .content и есть расширенный для админки. Как вы задаёте, что для uri /user/ нужно использовать базовый layout, для /post/id тоже, а для /admin — расширенный?
1. Не могу согласиться. Сам долго работал на поддержке чужих проектов (к счастью простых). И тут чем меньше технологий используется, тем проще поддерживать.

2. Какой layout использовать я определяю по uri, соответствующие файлы будут лежать, например, в templates/user/page.block.user.htm, templates/post/page.block.post.htm, templates/admin/page.block.admin.htm. Свой файл для каждого раздела/объекта. если файла по такому пути нет, используется глобальный.

Т.к. layout'ы страниц весьма похожи, то общие фрагменты (header, footer) хранятся в отдельных файлах и «инклюдятся» в page.block.htm.
1. ну как долго… всего пару лет. Так что мой опыт ничтожно мал. Я могу быть не прав.
{% extends '::base.html.twig' %}

Вот за это я и не люблю наследование. Не дело знать шаблону контента, где этот контент будут использовать. Один и тот же контент может быть как показан на странице, так и отправлен клиенту по почте. Как это решать при наследовании?
Ну, если допустить, что у нас есть один layout для html представления контента на странице, а другой для почты, то можно поступить так:

{# /content_http.twig.html #}

{% extends '::base.html.twig' %}

{% block body %}
  {% include '::content.html.twig' %}
{% endblock %}

{# http specific #}


{# /content_mail.twig.html #}

{% extends '::mail.html.twig' %}

{% block body %}
  {% include '::content.html.twig' %}
{% endblock %}

{# mail specific #}



То есть content.html.twig отрисовывает себя одинаково для http и почты, не зная в каком конткесте он вызывается, а контроллер вызывает разные шаблоны для разных контекстов, в которых установлена связь между шаблоном контента и соотвествующего контенту лэйаута.
Т.е. те же самые инклуды контента, что и в случае без наследования, только более хитрые, да еще и с дублированием кода.
Мне это не нравится.
Может я слишком широко понял задачу и в конкретном случае можно выкрутиться покрасивее.
1. Не думаю, что задача экранирования должна лежать на шаблоне. В конце-концов, это защита сайта от XSS, её нужно решать выше.
2. Наследование шаблонов — далеко не всегда нужно, но ладно, запишем в недостатки
3. Как обучение MVC и его принципам — изоляция полезна. Но в повседневной разработке — нет. Как только вы поняли, что такое MVC, и почему не стоит обращаться из шаблона куда попало — это для вас не проблема. А если у вас в команде студенты, то доступ к репозиторию давать только через Code Review.
1. Только шаблон в идеале знает, в каком виде будут представлены данные, скажем правила экранирования немного да отличаются для html4 и html5, не говоря об xml.

2. Практически всегда нужно, если следовать принципу DRY. Многие сайты/приложения состоят из многих страниц с однотипной вёрсткой, различающейся только блоком контента. Есть вариант обойтись без дублирования с помощью конструкций типа include "header.phtml"/include "footer.phtml", но он не очень удобен для разработки и, особенно, поддержки.

3. Не всегда разработчик контролирует код приложения.
1. Шаблон не должен знать, каким данным можно доверять, а каким нет! Это не его прерогатива.
2. Вариант с layout отдельно, content отдельно, на мой взгляд лучше для таких сайтов. Наследование — для более сложных ситуаций.
3. И что? Защита от дурака, это, конечно, хорошо, но нельзя же везде поставить такие препоны — быдлокодеры всегда найдут где в код нагадить ;)
1. Вопрос экранирования данных это не вопрос защиты от XSS или ещё чего, это лишь вопрос корректного вывода данных в конкретном представлении. И это область ответственности шаблона (вернее вида). Он должен знать какие данные ему выводить как есть, а какие экранировать согласно своему типу вывода, чтобы получить корректный вывод. Защита тут лишь побочный эффект. Как у экранирование с помощью mysql_real_escape_string в параметрах или использование подготовленных варажений побочный эффект защита от некоторых видов SQL-инъекций, но защитой они не являются.

2. Это частный случай наследования, одноуровневое. Не всегда его хватает. Да и всё равно обычно не так читаемо получается.

3. Если это нам ничего не стоит, максимум строчка в конфиге, почему бы не ставить? :)
1. Правильно, это область ответственности вида. Шаблону об этом знать не нужно.
2. По-моему это читаемее, и, что важно — более гибко. Наследование — слишком сильно связывает шаблоны друг с другом.
3. Это не строчка в конфиге, это возросшая сложность приложения. KISS не соблюдается.
1. Сложно разделить ответственность между видом и шаблоном, шаблон — составная часть вида и вполне, по-моему, может знать о данных столько же сколько вид. В конце-концов только разработчик шаблона знает в каком месте данные нужно экранировать, а в каком нет, а вид, не говоря о контроллере и, тем более, модели должен быть способен работать с любым шаблоном. А в модели для каждого поля делать не только нативный геттер, но геттеры для каждого формата по-моему очень неразумно. Выбор, имхо, стоит только в том экранировать по умолчанию или нет, основной набор данных передаваемых в шаблон. Исходя из того, что проще забыть заэкранировать, чем разэкранировать, я выбираю экранирование по дефолту, которое PHP без костылей реализовать не может.

2. О вкусах не спорят :)

3. Сложность инкапсулирована в лучших традициях ООП :)
1. Как это шаблон и вид могут знать о данных одинаково?
«Вид» это код, который пишите вы, шаблон — разметка, которую пишет верстальщик или пользователь. Вы сами говорили об этом выше. Я думаю, в таком случае, разработчик знает о данных не очень много.
Вид предназначен для представления данных, шаблонизация лишь один из способов его реализации. Вид, вернее его разработчики, должны знать какие данные ему идут на вход и как их корректно вывести. В одних реализациях разработчику кода достаточно знать какие данные, в других — только верстальщику, в третьих — обоим необходимо знать и синхронизировать свои знания.
Я не знаю PHP. Вопрос. Из модели вернули массив. В представлении использовали обращение не по ключам массива, а «напрямую». Как это возможно?
С массивом из модели должен работать контроллер, а не представление
Пусть бы и так. Только это ничего не изменило. Контроллер получил данные из модели в виде массива. Котроллер передал данные в представление в виде массива. В представлении используется не массив.
Правильно, потому что в классе load.php, в методе view есть строчка extract($data)
Каждый ключ массива превращается в переменную.
php.net/manual/ru/function.extract.php
Наоборот. Вы внимательный :)

Ведь где-то могут существовать переменные с такими же именами, как и ключи массива.
Пусть в примере их нет. Но в жизни бывает всякое. :)

С чего вы так решили?
Я нашел ответ на вопрос. Функция extract.
extract не безопасная функция, попробуйте почитать про ActiveRecord или DataMapper
Простите, причём здесь ActiveRecord или DataMapper?
Речь идёт о массиве в представлении, не о базах данных.
При том, что можно отдать представлению модель и обращаться к ее свойствам из представления.
Всё равно считаю, что в представлении должно быть представление, а работать с моделью должен контроллер.
Спорить не буду, лишь отмечу, что концепции MVC это не противоречит. Можно в контроллере вбить передать значения свойств в представление, можно получать эти значения сразу в представлении.
Просто в зависимости для кого это делать.
Если для себя, то никаких проблем, а если для кого-то, то в один прекрасный день кто-то, скорее всего, это сломает, пытаясь заменить что то в дизайне.
Даже для приведенного примера. В чем разница между

<?php foreach ($someArr as $item) : ?> ... здесь какой-то вывод ... <?php endforeach; ?>

и

<?php foreach ($model->getSomeData() as $item) : ?> ... здесь какой-то вывод ... <?php endforeach; ?>

Представление также инкапсулировано от модели геттерами как если бы через эти же геттеры нужно было получить данные в контроллере
Если так, то никаких проблем.
Я Вас, скорее всего, не правильно понял. Простите.
Причем использование геттеров, на мой взгляд, более оправдано, так как позволяет гораздо быстрее делать (кодить представления). Написал геттер, который через LazyLoad получает нужные объекты и сразу можешь к нему обратиться в представлении без необходимости вручную передавать контроллером — модель подгружает себе данные по мере обращения к ним.
На каком этапе реализуется ACL?
ACL — задача контроллера.
Использую шаблоны и все выглядит примерно {data}, мне так кажется удобнее.
Само собой, тот же Smary вполне положительно относится к тому, что я отдаю ему объект и в представлении обращаюсь к его свойствам.
В том, что можно сделать foreach ($model->setSomeData())
т.е. в представление нельзя передавать обьекты?[сарказм] Я же не говорил использовать эти паттерны, а взять идею. К примеру передав в метод объекта setFromArray() или populate() этот массив, понятно что произойдет, А представьте мы получаем массив в котором есть индекс _SESSION и extract делаем, круто правда?
Не знаю как остальные, а у меня представление только выводит данные, всей их обработкой занимается контроллер.
Контроллер не должен заниматься из обработкой, это задача либо модели, либо сервисного слоя. Контроллер передает вызовы от одного к другому, максимум — содержит простую логику, максимум, собрать все необходимые объекты чтобы отдать их модели.

Толстый контроллер не айс.
Непонятно я выразился, я имею ввиду, что он получает данные от модели\пользователя, в первом случае смотрит на данные и вывод их, например в случае с входом пользователя в систему, только саму форму, а если от модели пришло указание, что данные неверные\содержат недопустимые символы, выводит ошибку. В этом плане.
ну вообще-то на такие моменты используют префиксы, хватит уже попроть чушь. если посмотреть как делаю во фреймворках, то тма как раз через экстрактинг и делают с префиксом если надо. а вы тут чушь и демагогию развели.
вот уже и минус успели нажать, вместо того, что бы немного подумать, что я написал
Я Вас не минусовал, Вы правы, спорить не буду.
Но, как по мне, проще не пихать в extract то, что нельзя, чем делать хардкор или работать с моделью через вид. Или же просто передавать массив в вид.
Опыта у меня явно меньше Вашего, так что я просто прислушаюсь. :)
вот примерный класс view который избавит вас от extract: simpleView
а использовать просто:
в скрипте
$anyArrayWithData['title']='Mega Title';
$view->assign($anyArrayWithData);
$view->render('my/mega/template.phtml');

в представлении:
<h1><?php echo $this->title ?></h1>

вот и все
Когда работал с Opencart очень понравилась её архитектура, считаю одной из самых простых и гибких реализаций MVC.
OpenCart не смотрел, но считаю архитектуру Yii одной из самых приятных.
Сюда бы еще автозагрузчик классов, сами имена классов согласно паттерну MVC и шаблонизатор, было бы самое то.
И давно MVC как-то определяет имена классов? ))
А то я и не в курсе :)
шаблонизатор тут как бы есть, его роль играет сам php.
Коллеги, раз уж собрались специалисты по архитектуре PHP приложений, у меня вопрос.

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

Поскольку задачи из проекта в проект остаются похожими, желание сделать быстро — преодолевает желание переделать все с нуля.
Для начала стоит написать задачи, которые выполняет ваш фреймворк. Глядишь, посоветуют подходящую для вас замену.
Если не поможет — выложить на Github, рассказать про проблемы (и необходимые костыли), задачи, планы и т.д. Либо опять же — посоветуют замену, либо могут решить проблемы с костылями.
Задачи нехитрые, тот же Yii их выполняет прекрасно, не считая некоторых специфичных вещей. В других проектах которые поддерживаю есть и Zend и Kohana.

Проблема не в том, что я не знаю на что перейти, а в том, что это поддерживать пока проще, чем писать с нуля. Я видимо некорректно объяснил. На базе этого «фреймворка», уже написано довольно большое приложение, которое собственно и тянется видоизменяясь.
Я сталкивался с похожей ситуацией, и перешел на Zend. Время, потраченное, на переход, вполне себе окупилось.
Собрать волю в кулак, обуздать лень и перейти на Yii/Symfony/Zend/etc, что ваша религия примет больше.
Старые проекты поддерживать на старом, а новые писать уже на любом общепринятом fw.
Мне кажется, что если вы отчетливо понимаете, что в вашем фреймоврке есть архитектурные проблемы, то не зачем делать на нем новые проекты, незачем эти проблемы плодить.
А касаемо скорости разработки — если возьмете Yii/Code Igniter/Kohana — то это фреймворки с простым уровнем входа, в них легко разобраться и начать писать. Вы не много потеряете времени — попробуйте, вам понравится)
Вот если честно как не пытался себя заставить перейти на какой-либо популярный фреймворк — не смог. Не вижу смысла. Стрелять из пушки по воробьям. Применяю свой микрокаркасс для MVC.

Прошедший год занимался разработкой сайтов на Joomla, там свой фреймворк, конечно изучить его был смысл, но точно не Yii или Zend.
А сейчас работаю над проектом, который представляет собой вавилонскую башню из говнокода, накопленного наверное лет за 5, не меньше. И в команде к тому же нет единого мнения о целесообразности ООП, не то, что бы даже про фреймворк.

Так что не пойму я этой повальной моды на фреймворки.
Схожая ситуация, но постепенно перевожу код на ООП и MVC с использованием тестов, ORM, шаблонизаторов, библиотек имея в виду переход полноценный фреймворк.
Я тоже считаю это направление развития правильным.
Но не всегда это оказывается возможным или приемлимым с точки зрения временных затрат.
Проблема еще в том, что мода на фреймворки это прежде всего мода.
Которая проходит, а проекты остаются.
Другими словами, создаете ад? :)
Фреймворки — как стандарт, экономят время сотрудников в крупных проектах на изучение фреймворка.
В случае PHP это работает не очень :( В вакансиях чаще видишь «опыт работы с MVC-фреймворками: ..., ..., ..., и т. п.», чем «опыт работы с ...». Работодатели как бы не надеются, что достаточно соискателей знают конкретный фреймворк, их устраивает, чтобы хоть какой-то знали, ведь переход с одного на другой займёт, в общем случае, куда меньше времени чем принятие концепций фреймворков, MVC и т. п. с нуля.
Чтобы разрабатывать с использованием фреймворка нужно гораздо больше знаний и опыта, чем написание простых функций, раскиданных по сотне файлов. Все просто — чтобы использовать фреймворк нужно хотя бы иметь представление о принципах, которые лежат в его основе, поэтому требование опыта работы с фреймворком вполне обосновано.
Я к тому что единого стандарта де-факто, как Django в Python, RoR в Ruby или ASP .NET MVC в C#, нет (я в курсе что есть и другие фреймворки в них, но эти явно доминируют). И работодатели хотят чтоб хоть с каким-нибудь был опыт, чтобы сократить время на изучение, но на то, что оно вообще не понадобится и новому сотруднику нужно будет изучать только проект, но не сам фреймворк, особо не рассчитывают. RoR-разработчик вакансию можно увидеть, а Yii- или ZF-разработчик как-то не встречались.

Насчёт количества знаний необходимых для разработки вопрос спорный. Фреймворки многие детали скрывают.
Все верно. Это нужно для того, чтобы разрабатывать с использованием фреймворка.

Но не обязательно для того, чтобы решать прикладные задачи.
У меня сложилось впечатление, что это как стандарт только читая Хабр.

В вакансиях сейчас часто конечно встречается пожелание знать популярный фреймворк, но также полно еще предложений без этого требования.
Я в этом убедился сам, недавно выбирая работу, ходя по собеседованиям. А мой знакомый, который даже ООП толком не понимает, но имеет стаж лет 5, устроился на ведущего программиста.
Если уж есть какой-то готовый проект, который до тебя писала орда говнокодеров, то обладая какими-то навыками построения приложений есть смысл как-то сбоку прикрутить свой фреймворк, аккуратно его подцепить, чтобы и старое работало и новое писать было удобнее. В случае, когда есть уже существующая система к ней гораздо легче прицепить свой фреймворк, чем прицепить Yii/Cohana/CI/etc. Причина очень проста — каждый разработчик знает свой фреймворк гораздо лучше, чем любой из готовых, знает, как с минимальными изменениями адаптировать его под нужны уже имеющегося приложения.

У меня у самого была такая же ситуация. Есть проект, который просто написан стихийно на коленке, достаточно неплохо работает, но к нему нужно написать много сложных дополнений. Комментарий в коде практически никаких, зато есть собственный фреймворк, который можно урезать и как основу новых модулей использовать. Вроде бы и старый код продолжает работать и рефакторить его не надо, с другой стороны и зависимость нового кода от старого минимальна.

А переход на распространенный фреймворк обоснован, как минимум, тем, что он распространенный. Узких мест в нем найдено гораздо больше, багов и вариантов использования протестировано также гораздо больше. Да и база готовых модулей сильно сокращает время разработки.
Я выбрал способ вялотекущего рефакторинга с использованием Symfony Components, Doctrine 2 и Twig, имея в виду полный переход на Symfony 2, Doctrine 2 и Twig. Вялотекущего потому что код не мой и для написания тестов фактически его реверс-инженерю, толком даже не понимая где баг, а где фича.
У меня вот полного перехода не получается — к проекту пришлось написать собственный велосипед с почти полным копированием возможностей Yii.
Как только увидел стрелку от View к Контроллеру с пояснением «Вызов метода контроллера», то решил, что дальше читать не стОит :)
Правильно, от View не должно быть стрелок!
Серьёзно? Ничего что именно View отвечает за взаимодействие с пользователем? Двухстороннее взаимодействие, а не одностороннее. В случае веба стрелки от View — это ссылки, формы, ajax-запросы и т. п.
Серьезно. Двухстороннее взаимодействие — а какого хрена мы тогда говорим о разделении визуализации и бизнес-логики? Она тогда не разделена по определению, и все это тогда наука не о чем.

Вместо этой классической хрени MVC — контроллер может элементарно подписать на события View, нужные методы Модели — и устранить это излишние двухстороннее взаимодействие.
Бизнес-логика — логика модели предметной области, в которой существуют термины, например, «перевести сумму S со счёта A на счёт B», но не существуют «нажать на ссылку „перевод“».

Подписка на события — один из вариантов реализации MVC, но двухсторонние взаимодействия никуда не деются.
«Подписка на события — один из вариантов реализации MVC, но двухсторонние взаимодействия никуда не деются.»

Деются. В этом случае у View нет ссылки ни на контроллер, ни на модель.
Они просто идут через посредников, диспетчера событий и модель представления.
Но эта связь скрыта, и не влияет на повторное использование, в отличии от других где необходима непосредственная ссылка на объект. Такую скрытую связь на UML диаграмма не отображают, и она не может считаться двухсторонней.
Это ещё хуже, имхо. Связь есть, но она неявна, скажем модель Blog изменила своё состояние и оповещает слушателей, вызывая метод raiseEvent(new BlogState($this)). Какие классы зависят от BlogState узнать не так просто.
Часто это зашито собственно язык программирования.
Часто это зашито собственно в язык программирования.
>Для сайта domain1.com
Поскольку статья больше ориентируется на новичков, то давай будем последовательны и не прививать плохие привычки в духе той, что я процитировал. Забудьте вы про всякие domain.com, site.ru и прочих доменных именах которые приводятся в качестве примеров. Следуйте RFC 2606 (IANA — Example domains, Домены для примеров) и прочим полезным спецификациям. Уверяю вас, это не сложно ;)
Лучше бы автор itdumka.com.ua/ на хабре написал ;) чем Вы. Вы взяли у него всю инфу и выложили сюда, в чем смысл?
Те кто знает про MVC это не нужно, тем кто не знает — материала полно. Collection топики это круто, но они должны ИМХО быть крутого качества!
Классическое заблуждение о пригодности MVC продолжает жить, поразительно. А может ли мне кто-то сказать для чего нужно MVC? Разве не для разделения бизнес логики от визуализации? Нет? А Вы думаете представленная модель MVC с этим справляется? Вот оно заблуждение…

Как правильно организовать разделение визуализации и бизнес-логики я писал ранее тут.
Не только, трехкомпонентная сущность MVC предполагает, как минимум, разделение логики на три части: вида (то, что вы называете визуализацией), модели (бизнес-логика) и контроллера (всей остальной, логики приложения, как-то: логика хранения, логика контроля прав доступа).
Логика приложения — это другой слой, как правило оформляемый как ядро системы и используемый напрямую в бизнес-логике, делать ответственным за это контроллер — несерьезно и незачем. Это лишь запутывает систему взаимодействий — у вас тогда никакая логика без обращения к контроллеру не будет работать.
Я привык к подходу где бизнес-логика не зависит от других частей приложения. Контроллер должен знать о модели, вид может знать о модели, но модель, имхо, должна быть самодостаточной, максимум содержать логику хранения (а-ля ActiveRecord), но и то, имъо, это будет лишняя связанность, запутвыающая систему.
В том то и дело, что в MVC модель не самодостаточна. Легко проверить — часто бывает, что модель нужно использовать без визуализации — теневое выполнение. Тогда ни представление, ни контроллер не нужны — проверьте сможет ли заработать без этих классов модель — в общем случае нет, т.к. будет обращаться к представлению, чтобы отправлять обновленные данные.
Имхо, либо представление должно получать данные от модели непосредственно (свойства, геттеры), либо ему данные должен предоставлять контроллер (возможно подписывая представление на события модели). А модель ведёт себя либо пассивно, либо посылая сообщения об изменении своих данных абстрактному подписчику на них.
Я тут открыл Фаулера, там черному по белому написано «В основе MVC лежит разделение кода пользовательского интерфейса (представления, называвшегося раньше view, а сейчас чаще presentation) и логики предметной области (модели)». Поэтому не путайте меня «логикой приложения в контроллере» — это частные додумки, и далеко не эффективные.

Что касается первого варианта «представление должно получать данные от модели непосредственно (свойства, геттеры)» — это вообще не вариант — это того ради чего вообще затевается MVC с его тяжеловесной конструкцией. У Фаулера опять же описан более классический вариант под названием «Дублирование видимых данных» — это тот рефакторинг который нужно осуществить если в программе есть реализация вашего первого варианта.

А вот один из вариантов реализации «Дублирования видимых данных» — это через события, но в C# + WPF проще — там есть технология binding
Всё правильно. Логика UI находится во вью, логика предметной области — в модели. Всему остальному остаётся место только в контроллере (декомпозцию его функций никто не отменял).

Всего остального — НЕТ.
Как нет? Логика хранения, например, контроль прав и т. п., это и не UI, и не логика предметной области.
Как мы говорили ниже — это логика предметной области, только другой.
И да если модель не а-ля ActiveRecord, то как же её можно считать самодостаточной, если она не сохраняется?
Сохранение данных между запусками приложения (для PHP аналогично «между запросами») — задача логики приложения в общем случае, задача модели — моделировать предметную область, как ни странно. Скажем в бухучёте нет терминов «сохранить документ» или «загрузить документ», есть «создать документ» и «уничтожить документ» по каким-то событиям предметной области (приход товара, истечение срока хранения), в которую не входит «авария в электросети, компьютеры выключились» или «PHP сбрасывает среду выполнения для каждого запроса». А так модель самодостаточна, она как бы содержится в памяти с первого запуска приложения до последнего и лишь в силу неидеальности этого мира приложению приходится заботиться о сохранении и загрузки.
Так то оно так, но тогда это надо решать чище. Работа с базой данных — это по сути тоже бизнес-логика, только не бухгалтерии например, а именно так и называемое «хранение данных». Тогда в модели бухучета должны быть события «ОкончаниеСозданияДокумента» и «ОкончаниеУничтоженияДокумента», тогда контроллер должен подписать методы бизнес-логики базы на эти события. Но это слишком чисто для не совершенного мира программирования, и часто вместо этого у модели стоит прямо вызов метода ядра предназначенный для сохранения данных (а-ля ActiveRecord).
Работа с базой данных — это по сути тоже бизнес-логика

Нет, это не бизнес-логика, это техническое решение.
Блин, (чего тупим ;) ) — пусть Вы работаете в Microsofte и разрабатываете SQL Server Enterprice Manager, какая у Вас предметная область? В чем состоит бизнес-логика?
Я говорил, что в общем случае. Но опять же если взять, например, phpmyadmin то он взаимодействует с БД двумя способами — и как с предметной областью, моделируя в памяти структуру и данные БД, и как со средством хранения части своего состояния между запросами.
ИМХО, вот это и является проверкой решения на прочность, если от изменения предметной области (а работа в базой как мы выяснили в частном случае может быть бизнес-логикой) — меняется вся архитектура приложения — значит в такой архитектуре проблемы. Поэтому лучше всегда одинаково относится к бизнес-логике, и лишь разные предметные области разделять по слоям. Тогда бухгалтерия — это высокоуровневая бизнес-логика, а работа с базой — это несколько вырожденная, но все равно бизнес-логика, но уже низкоуровневая.
Не меняется от изменения предметной области вся архитектура. Если я буду писать SQL Manager, то в модели предметной области у меня будут классы типа Database, Table, Column, Relations и т. п. для моделирования предметной области, при этом Database, Table и Column будут получаться из SQL-запроса SHOW ... аналогично другим классам, зависящим от внешних сервисов. А Relation будет храниться архитектурно отдельно, на физическом уровне это может быть та же БД, может быть другая, а может вообще быть не БД.
И да если модель не может решить, визуализировать себя или нет — как она может быть самодостаточной? А такое решение и есть бизнес-логика — вида взаимодействовать с пользователем, или работать в автономном режиме. И вот только в случае необходимости GUI возникает отделение деталей визуализации от бизнес-логики.
В предметной области нет, как правило, и термина «визуализация», есть бизнес-процессы в различном состоянии и правила изменения этих состояний. Сохранность этих состояний между запусками приложения или их визуализация — не здача модели. Возлагая на неё эти задачи мы, как минимум, нарушаем принцип единственной ответственности искусственно усложняя приложение.
Вы не поняли. Я же не говорю, что в предметной области есть термин «визуализация». Но именно бизнес-логика решает взаимодействовать с пользователем или нет, например, один платеж можно провести автоматически, а второй нужно показать для утверждения пользователю. И вот в терминах предметной области и возникает необходимость в GUI. Таким образом, решение на верхнем уровне о визуализации диктуется именно бизнес-логикой — и это первейшая задача модели, решить нужен ей пользователь или она может работать автономно и все это в зависимости от определенных бизнес-правил.
Имхо, в терминах предметной области «нужно показать для утверждения пользователю» означает переход в состояние типа «ожидание подтверждения» или «черновик». «Показать» — логика приложения, «как показать» — логика представления.
Можно и так. Но это будет именно то чистое решение, о котором я говорил выше.
По сути наличие «ожидание подтверждения» означает, что есть некий диспетчер, который выбирает такие документы из очереди, но на практике многие ленятся так полноценно реализовывать.
Полноценно обычно избыточно, хотя и доставляет неудобства, если правила меняются.
А Вам не приходила идея, что «логик», так же как и представлений может быть много? Логика хранилища данных, логика бизнеспроцессов, логика пользовательского интерфейса (да-да, интерфейс это не просто статичная страничка «отчёта» со ссылками и формочками). Всё это разные уровни единой системы и MVC прекрасно применяется на каждом.

Соотношение примерно такое же как между транзакциями базы данных и транзакциями бизнес-модели. Вроде бы и похожие вещи, но не одно и то же и одно через другое не реализуется.
«логик», так же как и представлений может быть много — естественно, только если Вы от View обращаетесь и к модели и к контроллеру — вы тем самым зацементировали эту связку 3 классов. Попробуйте теперь полностью выкинуть ваш контроллер и модель и подсунуть представлению совершенно другую бизнес-логику и контролер — у них не будет того, что вызывает View и все у вас захлебнется…
View не подразумевает независимости от моделей и контроллеров в общем случае. Можно построить архитектуру, чтобы не зависело, если задаться такой целью, но в общем случае это не подразумевается. Отделение не значит независимость.
> Отделение не значит независимость.

Тогда грош цена такому отделению. И именно поэтому я начал с того, что «Классическое заблуждение о пригодности MVC продолжает жить, поразительно. „
Представление должно знать, что оно представляет. Иначе возможны трудноуловимые ошибки, которые статически не выявишь.
Сомнительное утверждение.
Вот эту функцию можно повредить передачей $data['file_name']

function view($file_name, $data = null)
{
if(is_array($data)) {
// преобразуем элементы массива в переменные
extract($data);
}
О… прочел теорию. Дайте-ка ругнусь, пока не приступил к практике.

Сам я не особо силен в MVC и прочих поименованных паттернах. Иногда думаю, почему?
Да потому что в подобных статьях всегда непойми как объясняется теория.

Вот, концептуальная диаграмма с кучей связей между тремя элементами. Хотя ниже в сопроводительном тексте сказано, что контроллер нужен для для связи модели и представления. Минуточку… Но на концептуальной схеме мы видим, что модель и представление могут взаимодействовать между собой.

Чему верить? Уже в этот момент у новичка начинаются трудности с пониманием.

Дальше идет описание жизненного цикла приложения с использованием кучи умных и не нужных слов. И это запутывает.

Я например, знаю, что жизненный цикл веб-приложения примерно такой:

1. — клиент посылает HTTP-запрос серверу (метод, uri).
2. — приложение запускается, определяет метод и uri
3. — выполняет код в соответствии с uri и методом.
4. — возвращает сгенерированный HTML (XML, JSON, GIF, whatever)
5. — приложение завершается.

Просто, понятно, знакомо. (IMHO. Допускаю, что для кого-то не просто, не понятно и не знакомо).

И где тут что. Например, п.2. это контроллер, п.3 — модель, п.4 — представление.

Прав я или нет? Верно я понял концепнию или не совсем?

Я пока не знаю, что мы будем делать в Практике (не дочитал топик и не знакомился еще с другими комментами), надеюсь будет интересно.

Но вот как я вижу мини-фреймворк после прочтени теории.

<?php
// контролер
$method = $_SERVER["REQUEST_METHOD"];
$uri = $_SERVER["REQUEST_URI"];

// обращаемся за данными к модели
$model = getModel($uri);
$data = getModelData($model, $method, $uri);

// обращаемся за HTML-кодом к представлению.
$view = getView($method, $uri, $data["result"]); // вид зависит от того, как отработала модель
$output = getViewOutput($view, $data);

echo $output;
?>


Как-то так, да?

Ну все, пошел читать дальше.

Спасибо за статью, вызвала неподдельный интерес.

1. — клиент посылает HTTP-запрос серверу (метод, uri).
2. — приложение запускается, определяет метод и uri
3. — выполняет код в соответствии с uri и методом.
4. — возвращает сгенерированный HTML (XML, JSON, GIF, whatever)
5. — приложение завершается.


С использованием MVC будет скорее так:
1. — клиент посылает HTTP-запрос серверу (метод, uri).
2. — приложение запускается, определяет метод и uri, вызывает контроллер
3.1 — получает данные (как правило один или несколько объектов/массивов) в соответствии с запросом из модели
3.2 — передаёт данные в вид для генерации HTML,…
4. — возвращает сгенерированный HTML (XML, JSON, GIF, whatever)
5. — приложение завершается.

Важно понимать, что пункт 2 это ещё не контроллер, обычно это называют маршрутизацией, осуществляется или веб-сервером, или фронт-контроллером. То есть в вашем примере контроллер это, как минимум,
$model = getModel($uri);
$data = getModelData($model, $method, $uri);

// обращаемся за HTML-кодом к представлению.
$view = getView($method, $uri, $data["result"]); // вид зависит от того, как отработала модель
, а то и остальные две строчки. Ну и, как правило, uri и method не передаются в модель и представление напрямую, а из них извлекаются необходимые для данного запроса параметры и определяется какие модели вызывать и какие вью использовать. Модель и вью как бы изолируются от HTTP, не User::getByUri($uri), а User::getById($_GET['user_id'])
Тут вроде понятно… весь мой пример скрипта — контроллер.

Модель и представление, типа, определены в других файлах.

Насчет uri не совсем понятно. С учетом того, что Uri — universal resourse identifier, чем он хуже любого другого идентификатора?

Впрочем, я тоже не всегда использую uri, а извлекаю из него параметры. Но иногда (в удачный день :) получается использовать uri без дополнительной обработки.
В принципе да, весь код контроллер, но когда таких контроллеров будет два, три и т. п. вы наверняка выделите часть кода в один, скажем, index.php, в котором будете инклудить зависящий от запроса конкретный контроллер.

Слишком универсальный в контексте контроллера, Допустим, для запроса GET /users/volch к моменту получения объекта с id volch уже ясны тип объекта, что нужно его именно получить (а не удалить и т. п.) и достаточно вызова $users->getById('volch'), а не $users->getByIUri('/users/volch'), который придётся дополнительно разбирать. Хотя, конечно, это вопрос удобства и архитектуры могут быть разными, но как-то наиболее популярна при которой при разборе метода и части uri определяется собственно обработчик (контроллер), а при разборе оставшейся части uri и post/put данных определяются параметры, которые ему передаются, часть которых он передаёт в модель, а часть использует сам например для выбора формата представления.
Разобрать uri не так и сложно, если заранее о его (или ее? мне нравится «она», правильнее, особенно по-английски — «он») структуре подумать.

list($empty, $db_name, $id) = explode("/",$uri);


Да и вообще сам uri может выступать идентификатором ресурсе в БД, XMLфайле или key-value хранилище.

Конечно, не всегда это может быт правильно или удобно.
Разобрать, конечно, не сложно, вопрос в том, где это делать и сколько раз. Особенно если есть вероятность, что правила разбора могут меняться.
Ругаться тут не надо. Представьте свою схему и пояснения к ней и почему она более актуальна чем приведенная в статье.
Вы — учитель. Я лишь заметил странное несоответствие схемы и ее описания.
Пешил попробовать.
Fatal error: Call to undefined method Load::veiw() in /Applications/MAMP/htdocs/backoffice/application/controller.php on line 19

И дальше никак :(
Модель, ВИD, контроллер… Оригинально!
Занялся изучением Yii. Решил начать с повторения основ ООП и изучения паттерна MVC. Ваша статья ОЧЕНЬ помогла! Большое спасибо!
Sign up to leave a comment.

Articles

Change theme settings