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). Мне нравится как там реализованы многие контроллеры на простых классах со статическими методами. И ребята не залипают в иллюзиях.

оцените контент и участвуйте в выборе трендов