MVC в WordPress

MVC в WordPress

У программистов часто возникает спор о том что такое MVC (Model, View, Controller)?

Заметил что многие думают что если класс назвать Model то он станет Model, а если его назвать View, то он станет View. А если папочку назвать MVC то вот у нас уже свой MVC 🙂 Мозг слишком залипает в слова и за словами не видит реальности.

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

Думаю для многих будет сюрпризом узнать что в WordPress написан с учетом MVC 🙂 Просто классы там не называются как Model, Controller и View имеет иное название. А раз программисты не очень хорошо понимают особенности этих паттернов, то им кажется что в WordPress нет MVC.

Что такое MVC?

MVC — это набор паттернов, который разбивают логику работы на 3 части:

  • models — объекты отвечающие за логику работы с сущностями, как правило их свойства ассоциируются с полями таблицы БД, а методы состоят преимущественно из геттеров и сеттеров
  • views — это шаблоны представления данных 🙂 по сути у них есть второе название — templates
  • controllers — объекты которые отвечают за получение данных, их обработку и иногда передачу в views (templates)

Подробнее про Views

Есть программисты которые путают View и контроллер который работает с View. Особенно в WordPress.

Если посмотреть View в Laravel или RoR, то можно увидеть что они повторяют логику работы с templates в WP (метод get_template_part).

Symfony мимикрирует в сторону WP 🙂

В Symfony 3 эта механика тоже называлась View, но в версии Symfony 4 они переименовались в templates и стали походить на то как это сделано в WP https://symfony.com/doc/current/templating.html

Возможно это было сделано как раз по причин того чтобы снизить путаницу в голове у программистов.

Примеры работы с представлением в WP (views/templates)

В мире MVC-фреймворков мы готовим данные в контроллере, и потом эти данные передаем массивом в view чтобы представить их.

В мире WP все не так чтобы сильно иначе 🙂 Но есть особенности.

Самый прикольный с моей точки зрения подход у WooCommerce, они написали функцию wc_get_template(‘name’, $data). Подготовили данные, передали в шаблон, и представили их согласно шаблону. Очень похоже на то как это работает у RoR или Laravel/Symfony.

У чистого WP все чуть хитрее. Там есть метод get_template_part. Но он не умеет пробрасывать данные через функцию. Потому все данные что вы подготовили в контроллере — привычным способом в шаблон не попадут 🙂 Но если вы знакомы с REST интерфейсом, то тут можно следовать его природе. У WP есть функция set_query_var и get_query_var. В контроллере сохраняем туда данные. А в шаблоне получаем их от туда. Бонус — по ключу переменной мы легко можем определить связь между шаблоном и контроллером.

Этот подход также может быть не понятен молодому поколению, учитывая что многие также не понимают природу REST интерфейсов, а также как образуются ЧПУ через Rewrite Rules 🙂 Которые затем превращаются в роутеры, контроллеры и знакомые им массивы передающиеся в шаблоны. Все это части одной системы. Но фреймворки скрывают все это от программиста за стенами своей магии. WordPress в этом плане является представителем old school. Тут меньше магии. Все ближе к базовым настоящим протоколам. Но тк это не привычно для молодого поколения — то им это кажется страшным 🙂

А где же Twig?

Есть еще отдельная порода программистов, которые очень любят Twig и прочие шаблонизаторы аля синтаксический сахар. Они плохо понимают тот простой факт что php и есть шаблонизатор в своей основе 🙂 Он переводится как Hypertext Preprocessor. По сути он был придуман как способ генерации HTML из данных.

Понятно когда шаблонизаторы Twig используется в Ruby, JS или Python. Это все языки не заточенные под HTML. Но php и есть шаблонизатор 🙂 Зачем использовать шаблонизатор внутри шаблонизатора? Шутка с долей правды 🙂

php конечно уже давно вырос из шаблонизатора. Но при этом он не потерял способность удобно работать с HTML.

Вероятно именно в этом причина того что система шаблонов WP спокойно работает на php. И это круто.

Опытные программисты которые много работали в сложных системах — уже видели какой кошмар образуется если внедрить туда Twig. А молодежь этого не видела и вероятно поэтому все еще верят в магию Twig. Для примера можно почитать шаблоны в платных темах WHMCS. Там php, сложная логика, но шаблоны на Twig. Читаешь их и хочется блевануть, а потом застрелиться. Вещи которые можно уместить в 20-40 строк, тут разрастаются до 200-300. Вот вам и шаблонизатор внутри php 🙂

Как мне удалось заметить, у ребят которые разрабатывают ядро WP есть мозги. И они не пытаются усложнять систему без причин. Если php может спокойно работать с HTML то нафига Twig? Это простая мысль. Не доступная большинству программистов.

Это конечно тоже не точно. В случае с MVC фреймворками эта проблема скорее всего не существенна.

Возможно она есть только в больших системах где есть темы и шаблоны…

В WP удалось решить проблему тем но с php. Темы имеют простой лаконичный код и хорошо развиваются, их достаточно легко писать самому. В WHMCS используют Twig и проблему решить не удалось. Код темы выглядит страшно, не читаемо и крайне избыточно. Написать свою тему — крайне сложно. Совпадение? 🙂

Подробнее про Models

Модель — это такой способ написать класс объектов, когда его свойства ассоциируются с полями таблицы сущности. Например если у нас есть данные о Постах, Продуктах, или Недвижимости, то мы создаем под каждую сущность класс модель, которая позволяет работать с данными этой сущности.

Например класс WC_Product в WooCommerce это классическая модель. У нее есть ID и другие свойства согласно таблице.

Если мы говорим о базовом WP, то там моделями являются классы WP_Post, WP_User & WP_Comment …

Методы классов-моделей в основном состоят из геттеров и сеттеров.

Разница WordPress и MVC-фреймворков типа RoR или Laravel заключается в том что у MVC фреймворков для модели есть базовый класс. Который содержит в себе готовый набор удобных методов. Вам не надо пилить класс с нуля, вы наследуетесь от базового класса и получаете из коробки почти готовую модель. Просто дописываете нужные вам геттеры и сеттеры — и поехали.

Минус этого подхода — программисты перестают думать головой и часто просто полагаются на магию фреймворка.

Например class Property extend Model … так мы создали модель для Недвижимости и можем работать с объектами недвижимости. Если мы в Ларавел.

В WP нет базового класса Model, и мы просто создаем class Property, дописывая туда то что нам нужно — обычно надо писать геттеры и сеттеры. Чуть более трудоемко и нужно чтобы программист понимал что такое Model 🙂 Трудоемкость условно выше на пару часов, гораздо сложнее с пониманием. Мало кто из программистов хорошо понимает что такое Model и вот тут обычно возникают все проблемы 🙂

Многие плагины и задачи в WP могут быть решены вообще без образования моделей. Потому что там многие задачи решаются через единую модель WP_Post. И для большинства задач этого хватает. У этой модели есть редкая особенность, о которой мы поговорим далее…

Пример хорошей реализации правильной модели это класс WC_Product в WooCommerce. Это классическая модель. Которая как то работает без наследования от базового класса Model. По всем признакам это модель. Но тк нет наследования от одноименного базового класса — у программистов может возникнуть разрыв шаблона и подгорание пукана.

Подробнее про Controllers

Легче понять что такое View и Model, и все что не влазит в View и Model — может быть размещено в Controller. По сути это все другие классы, которые обеспечивают логику работы приложения.

Хотя конечно типы и паттерны классов не ограничены лишь Моделями и Контроллерами. Существует множество других классов по иным паттернам. Например попробуйте угадать какому паттерну соответствует класс DateTime? 🙂 Это не модель, не контроллер и тем более не view/template. Классы бывают разные и не всегда они должны называться контроллерами или быть моделями 🙂

В мире Laravel & Symfony мы можем сказать что контроллер совместно с роутерами обрабатывает HTTP запросы, обеспечивает взаимодействие с моделями, и обычно возвращает данные в представления (view/template).

В мире WP все слегка иначе…

Во первых вместо роутеров у нас Rewrites Rules (те самые которые появились во времена Apache & htaccess). Смею полагать что это сложнее чем роутер, но гибче. Молодому поколению привыкшему к простым роутерам — бывает сложно освоить Rewrite Rules 🙂 По этой же причине многие не понимают природу образования ЧПУ. Также как и с моделями — программисты полагаются на магию фреймворка, перестают думать головой и мозг постепенно атрофируется.

Во вторых HTTP запрос в мире WP это лишь часть системы обмена сообщениями. Потому контроллеры в мире WP могут не работать напрямую с HTTP запросом. А получать данные и обрабатывать их далее посредством системы обмена сообщениями (тут ее зовут системой хуков, сегодня этот паттерн чаще известен как EDA или Event-driven architecture, 30 лет назад когда паттерн только зарождался его называли просто обменом сообщений между объектами или компонентами системы).

Потому в более широком смысле тут контроллером называется класс, который принимает и обрабатывает данные в потоке запроса. Не всегда напрямую от HTTP. Обычно он возвращает данные в представление (view/template) или в JSON API. А может быть и ничего не возвращает. Принял, сохранил и уснул 🙂 Так тоже бывает.

Почему WP_Post без возможности наследования?

Встречал критику WP_Post которая касалось того факта что это класс с архитектурой без наследования. Мол это плохо. Критики не понимают что это было сделано осознанно. И наследование легко решается при желании.

Мне кажется это сделано по причине экономии запросов к БД на сайтах без объектного кеша (Redis/Memcached…). При создании объекта модели (например Product), у нас срабатывает конструктор, который забирает данные из базы и сохраняет их в состояние класса (свойства класса получают значения из БД). Но в коде у нас на 1 запросе может быть множество мест где мы создаем объект. Если у нас нет объектного кеша — каждый такой объект займет память и сделает запрос в БД. А это дорого и нагрузка. Надо чтобы при каждом создании объекта, если он уже был создан ранее — не нужно было запускать конструктор и снова запрашивать данные в базе. Именно эту проблему можно решить если наш класс будет Синглтоном или как в случае с WP_Post используется механика кеширования без наследования. А тк WP рассчитан на то чтобы работать в том числе на простых сайтах где нет объектного кеша — это единственный способ обеспечить снижение нагрузки на БД.

Но как быть с наследованием? Тут все просто. Надо включить мозг и понять что такое модель 🙂 Например тот же WC_Product это унаследованная модель продукта, которая базируется на WP_Post. Никто не мешает создать любую модель, которая может содержать в себе данные WP_Post из таблицы posts и если нужно то какие то данные из post_meta, а при желании подцепить любые кастомные таблицы.

Вариантов решений тут много. Было бы понимание что такое Model, а найти решение уже не составит труда.

Но это не точно 🙂

Итого

За последнее время я лучше стал понимать что такое MVC. Видел разные варианты реализации: RoR, Laravel, Symfony, WP. У всех вариантов есть свои особенности, плюсы и минусы. Но в этой части я не готов говорить какой вариант реализации MVC лучше. Мне кажется это не возможно.

Единственное что мне кажется странным, это то что программисты не разобравшись в том что есть MVC — пытаются утверждать что этого нет в WP 🙂 Городят разные костыли, называя их MVC. Порой эти костыли выглядят очень странно и смешно. А доказать людям что их MVC ничем не лучше той что уже есть в WP — достаточно сложно.

Хороший пример на мой взгляд это WooCommerce. Там все сделано красиво. Люди которые пилят этот продукт — реально понимают что такое MVC. Там правильно сделаны модели Продуктов и Заказов, там правильно используется представления (View/Template). Мне нравится как там реализованы многие контроллеры на простых классах со статическими методами. И ребята не залипают в иллюзиях.

У этой записи 4 комментариев

  1. Надеялся увидеть какую-то конкретику, а получил стакан воды. Контроллеры совместно с системой роутинга обрабатывают HTTP запросы, получают данные от модели, выполняют ещё какую-то логику и передают данные в вид — так мне кажется понятней что делают контроллеры.
    Как по-мне, эта статья более познавательная — https://www.pvsm.ru/razrabotka-sajtov/262428, а также есть ещё интересная книга Rakhitha Nimesh Ratnayake — WordPress Web Application Development на этот счёт.

  2. Спасибо за коммент. Чуть поправил статью 🙂 Мысль верна относительно фреймворков основанных на MVC.
    Но в WP кроме MVC есть еще EDA. А потому природа контроллеров слегка видоизменяется.

    Также спасибо за ссылку. Мысль автора крайне глубока и интересна.

    Однако у нас множество расхождений в мышлении по этой теме 🙂
    1. Автор думает что тема WP = шаблоны и призывает минимизировать там логику. А это не так. MVC мб как в плагине, так и в теме. Тема может нести на себе функционал. Такова природа WP. И на мой взгляд это правильно.
    2. Автор призывает за Twig. Что на мой взгляд очень глупо. Решение WP мне видится более эффективным.
    3. Автор верно заметил про то что миграции не нужны в случае с WP_Post & EAV. Возможно он это взял из моей статьи https://wpcraft.ru/notes/wordpress-eto-prosto/
    4. Но автор не учел что миграции чаще всего нужны для моделей в ORM парадигме. В WP они тоже нужны, не так часто, чаще когда мы работаем с кастомными таблицами.

  3. Уведомление: WordPress — это просто?

  4. Теперь статья преобразилась и стала намного интересней 🙂

Добавить комментарий

Закрыть меню
×

Корзина