Введение
То как сейчас мы создаем веб-приложения сильно отличается от кода, пришедшего к нам на поддержку из стародавних времен, когда по телевизору показывали Фабрику Звезд, модно было ходить с расклодушкой, а PHP только начинал приобретать признаки Объектно-ориентированного языка. Программирование в нулевые мне представляется великолепной иллюстрацией к идеям Дарвина: тогда почти любой разработчик-одиночка создавал собственные решения, некоторые из них захватывали умы более чем одного человека, и пройдя ускоренный естественный отбор, фреймворки и CMS стали титульными, образцами лучших и наиболее популярных методов разработки. В некоторых случаях, все же, только популярными, без слова «хорошее» или тем паче «лучшее».
??Но в отличии от динозавров и мамонтов далеко не все альтернативные решения ушли в небытие, особенно на бекэнд, и я знаю об этом не понаслышке. Мне довелось поработать, не скажу с большим, но довольно солидным количеством legacy на php. Кое-что было просто ужасно, а некоторые сайты и даже CMS были довольно интересные. Мне нравится иногда покопаться в том, что рождалось в умах первопроходцев. Тогда не было такой стандартизации, и хотя чаще всего это превосходный материал для изучения анти-паттернов, но этот код меня забавляет, делает работу похожей на ИТ-археологию.
Возлюбленное наследие
Лично мне нравится в работе с работающими сайтами на legacy — внести правку, убрать какой-то дикий костыль, уменьшить уровень хаоса и при этом не сломать работу всей системы. Чувствую в такие моменты себя человеком, сделавшим этот мир немного лучше, и это круто.
print " <button onClick='domultimove();' class=controlbutton><img src=syspix/ico32_move.gif border=0><br>перенос</button>";
print "<b>" . $ITEM->Description . "</b>:<br>";
$browsebgcolor = "#D9D9D9";
$sql = "select i.ID, m.Name, i.perm, i.descr from item4 i inner join main m on i.ID=m.ElementID";
echo "<script language='JavaScript'> document.location='" . $_SERVER['PHP_SELF']";
Частой отличительной чертой legacy является каша из кода, написанного на разных языках, хотя и в современной разработке, конечно же, мы в PHP классы можем добавлять, например, код на SQL. Но я говорю о другом, в legacy нередко идет поток вселенской мысли разработчика — о чем и на какой языке он думал, то и писал потоком. Я хочу рассказать о подобной ситуации с одним мега-олдскульным CMS-решением, с которым работал некоторое время. Там все было настолько «чудесно», что я просто очень рад, что смог все-таки кое-что исправить и сделать так, чтобы сайт стал работать существенно корректней и быстрее.
Итак, мне в работу попал сайт крупной компании, написанный в первой половине двухтысячных. В этом проекте все базировалось на классах с одним методом, в котором происходило все: и работа с логикой, и вывод интерфейса (view) и обращения к базе, при чем, sql был прям там же между командами
print "<table>";
. Вносить правки в работающий на таком коде сайт, кстати, с неплохой посещаемостью и коммерческой составляющей, представляло из себя не самое приятное, но очень веселое мероприятие.??Нетрудно догадаться, что я сильно облегчил жизнь бекэнд-разработчиков, для начала вынеся вывод верстки в шаблоны. Для этого я использовал уже готовый и довольно популярный шаблонизатор twig. Это было несложно и быстро, так что я даже не особо и выделял время на рефакторинг. Следующим шагом по-моему мнению нужно было что-то сделать с запросами к базе данных, в конце концов, MVC не зря так популярна у нас в веб.
??Насмотревшись на Симфони и Ларавел, я решил, что и здесь подход с использованием ORM отлично впишется. Поможет вынести работу с Базой Данных и оставить пока еще не-до-контроллерам только работу уже с полученными данными. Логично и совершенно правильно использовать уже существующие решения. Поэтому перво-наперво я ринулся в packagist посмотреть, какие альтернативы у меня есть кроме Doctrine, но хорошенько подумав, пришел к неутешительному выводу, что это не столь важно. Дело в том, что в этом проекте была довольно необычная структура данных. Такого я не встречал нигде более, хотя я работал с MODx :) Передо мной встала проблема: использовать популярные open-source ORM так, как я хочу не получится, ну, по крайней мере это будет то еще приключение. Так я решился на создание велосипеда.
Немного про то, что же я сделал
??Да, я решил, что напишу ORM на PHP с нуля (нет, ну идеи и концепции перенятые у того же Doctrine у меня-то были) специально для этого проекта, да так, чтобы это работало со структурой данных. Ведь это рабочий сайт, и никто не был готов выделять ресурсы программистов на задачи «переписать все с нуля с нормальной структурой БД». Праотцы основавшие эту CMS создали 2 типа объектов, при чем один из них еще и делился на внутренние «типы данных»: ресурсы, у которых есть даты, ссылки, разные типы текста, изображения и еще ряд хранились в 2 таблицах, но были и объекты, хранящиеся в одной таблице, их думаю можно назвать системными данными.
??Я не хотел, чтобы применяя обращения к моделям, приходилось бы думать о джоинах, или хотя бы постараться сократить такие моменты до минимума. Поэтому я решил, что модели должны быть двух типов: для одно-табличных объектов и для основных двух-табличных. Так как у этих Моделей-классов много общих методов, тот же ORDER BY или LIMIT, поэтому каждый базовый класс, на основании которого и будут создаваться конкретные Модели, я наследовал от общего Абстрактного класса.
Как можно видеть из дерева, я так же добавил поддержку разных типов Баз Данных, что в данном случае избыточно. Но я в тот момент вошел в «поток» и творил :). Также очень правильным в данном случае шагом явилось, то что я делал это на базе PDO, ибо использовавшийся в коде php-mysql не позволял переводить сайт на седьмую версию языка, а это хотелось исправить на корню, как говориться.
$element = (new Model())->getOne($id);
Благодаря тому, что я хорошенько разобрался в логике структуры Базы данных, у меня получилось сделать поиск и получение ресурса только по его ID, даже не зная каким внутренним типом данных обладает ресурс, что было обязательно при старом подходе. Настоящая объектная работа с данными, и мы забываем об sql в коде.??
Заключительные абзацы...
Эта работа принесла 2 неожиданных для меня поворота. Во-первых, я попытался все написать сразу в виде кода, и ничего не получилось. Мне пришлось взять ручку, блокнот, выйти на природу и под щебет воробьев и жужжание пчел сначала нарисовать, что я хочу получить, как это будет связано, какие мне нужны классы, и как это будет вызываться в коде «контроллеров». Кстати, потом реализация отличалась от изначальных зарисовок совсем незначительно. Так что, если программист ничего не кодит, это еще не значит, что он ничего полезного не делает.
Во-вторых, я справился очень быстро, многие бизнесы не любят, когда программисты тратят время на всякие не понятные рефакторинги. Я реализовал весь проект в течении примерно недели, параллельно занимаясь поставленными задачами по внедрению новых features.
Теми плюсами, о которых я заявлял в начале статьи я отмечу: последовательность и постепенность замены уже существующего кода на работающем проекте, а значит и отсутствие необходимости выделять больше времени на переход, рефакторинг кода в рамках поступающих задач. На этом откланяюсь, благодарю за чтение.
m03r
Совершенно неясно, в чём состояла проблема с упомянутым одно- и двух-табличным хранением объектов, и что это за структура, которой свет не видывал. Это же самое интересное, расскажите нам!
А без такого пояснения ORM-на-коленке действительно выглядит вредным велосипедом.