Yii2 и виджеты: немного полемики

Насколько прекрасен и прост Yii2 в работе с backend (хотя, так с любым фреймворком: сколько бы ни ругался на Symfony, но 90% работы происходит с PHP, а не с ним), настолько отвратителен в виджетах и рендеринге. Не знаю, есть ли где-нибудь подобный треш, как в Yii2. Сравнивая с тем же Symfony: там разработчики решили остановиться на дефолтном шаблонизаторе twig и не внедрять в стартовые шаблоны никаких примеров чудо-виджетов. Спасибо, ребят!

Виджеты — ужасная штука. «Это самодостаточный блок», говорили они. «MVC», говорили они. Неужели попытки скрыть передачу данных под видом DataProvider во вьюху действительно не дают повода задуматься?

Как только приходится что-то поменять в виджете, начинается боль. Если вы хотите поменять расположение кнопок — должны потратить два часа на поиски и применение информации. Вы достаточно постигли стандартные виджеты типа GridView? Не волнуйтесь! Любой другой виджет, который вы установите, например, FileInput от Kartik — и вуаля, гуглите снова. Гуглите много.

Окей, я не буду голословным, вот вам код:

Серьёзно, он такой и есть. Выглядит интуитивно понятно и очень гибко. Можно поменять язык, можно настроить множественную загрузку файлов, главное что смотрится красиво! Гляньте:

46078f35a64995249bae95a2076351e7

Да, мне тоже нравится. Работа над плагином проделана большая. Но оставьте это плагином. Зачем делать из него такое, от чего мои глаза ( речь идёт про мои впечатления после использования виджета, и они нисколько не объективны, да ) кровоточили разочарованием в принятых решениях. Побуду циником: взамен предложить нечего. Возможно, потому что Yii2 не стоит лезть во frontend. Возможно потому что отделение backend от frontend есть самое логичное, что произошло с вебом. С тем же вебом, где ECMAScript5 до сих пор не определился, как регламентировать асинхронные процессы, и весь Ajax реализован как бы интуитивно, а не по стандарту ECMA (ну и здорово!).

Давайте вернёмся к коду. Вы заметили ‘pluginEvents’? Это элемент массива, значением которого является… JavaScript! Даже jQuery. Впрочем, на использование jQuery нас обрёк сам фреймворк, но ладно. Дело в том, что приходится писать текст JavaScript прямо в строках PHP. То есть, когда в html встречался коннект к БД, бывалые программисты пускают скупую слезу отчаяния, но сами пишут JavaScript в PHP. Почему? Потому что «виджет есть независимый самодостаточный блок». Тогда что можно назвать виджетом? Календарь? Зачем нам календарик, если мы с ним не можем взаимодействовать? Только в том случае, когда нужно выбрать конкретную дату (и только), он нам пригодится из коробки, как есть. Но если при перелистывании месяцев должно что-то меняться во фронте — о-о, да, запасайтесь кофе и зефирками, впереди долгие бессонные ночи.

JavaScript должен уметь общаться с другим JavaScript. Если нет — в дело пойдут глобальные переменные и window.variable = ‘1’. Как только мы захотим привинтить к календарику что-то красивое, то 1) видим что он виджет и плачем 2) не можем спихнуть эту работу на фронтенда, кроме как заставить всё переписать.

Все видели HTML-разметку? Её обычно много, и в глазах рябит. Но вызывает ли сложности изменить разметку? Хм, обычно нет. Это работа лёгкая и скучная.

Но подождите, сейчас вы увидите на редкость грустную разметку.

Часть шаблона, по которому генерится виджет (точнее, сам плагин). Да, здесь yii2 уже ни при чём, но давайте представим, что нам нужно что-то поменять из этого, а оно оформлено и зашито в виде виджета? А если верстальщик сверстает прекрасное новое оформление для FileInput, отдаст вам, чтобы вы внедрили это, что будет? Снова будет грустно. HTML, от которого просто не может быть весело.

И вы попытаетесь использовать библиотеки виджета, чтобы поменять что-то в нём на js, но не получится! Это ведь самодостаточный независимый блок.

Как вывод, я хочу сказать, что современный веб всегда подразумевает интерактивность компонентов интерфейса. Сейчас такие увесистые компоненты, что перезагружать каждый раз страницу ради серверного рендеринга есть непозволительная роскошь. И если вы загрузили файл, а в списке файлов он отобразится только после ф5, это уже плохо!

Интерактивность требует доступности всех компонентов. В виджетах эта доступность страдает. Может, я не умею их готовить (скорее всего, так и есть) — но зачем учиться готовить то, в чём другим людям придётся ещё долго после меня разбираться? Это одинаково непонятная магия для любого новичка, и одинаково муторная проблема для мидла — виджеты, которые нужно менять, с которыми нужно взаимодействовать, и уж тем более с которыми нужно уживаться, когда пишешь SPA.

Трио методов для асинхронных процессов

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

Хороший пример: запуск поиска со множеством параметров по множеству API c последующим разбором результатов через адаптеры. Сам процесс нас не волнует, лишь рычаги управления: допустим, метод run или search (что-то вроде ‘http://site.com/api/v1/run’ ) принимает через POST множество параметров для запуска, а возвращает id.

051091cbc58b2d3c549adbd0168cb3db

Окей, что нам делать с этим id? Нам его нужно отдавать тому же серверу, чтобы 1) смотреть, что он сделал по нашему запросу 2) брать информацию, когда он всё сделал.

Хорошая идея (и, возможно, первая, которая приходит в голову) — сделать ещё два метода, например, check (или status, я просто привык именовать методы глаголами, для REST это неприемлемо) — ‘http://site.com/api/v1/check/id‘. И get-results — ‘http://site.com/api/v1/get-results/id‘.

Зачем нам нужен check? Чтобы проверять, в каком состоянии находится запущенный процесс с полученным из метода run id. А get-results чтобы получать результаты.

Можно решить эту задачу двумя методами: и правда, зачем вообще нужен check, если у нас есть метод get-results, который просто не вернёт информацию, если её нет. Его можно нагрузить дополнительной логикой и возвращать status как один из ключей ответа. Преимущество такого метода интуитивно понятно:

bef78d760a87e6aab392cff755a8ff0d

В то время, как запросы должны с некой переодинчностью проверять Check, чтобы убедиться в наличии данных, а лишь потом спрашивать сами данные, вариант с двумя методами позволяет получить данные сразу, подгадав нужный момент. То есть, мы избавимся от одного потенциально лишнего запроса.

Но это не панацея для всех проблем такого вида: если процесс поиска составной, и в фоне запускается несколько потоков для выгрузки разнородной информации, то метод get-results может вернуть лишь часть результатов. К примеру, поиск по транспортным средствам, где самолёты и автомобили доступны с разных API — тогда самолёты могут найтись, а автомобили нет. Получится такая ситуация, что мы лишний раз нагружаем сервер, вытаскиваем полученные результаты, а они оказываются слишком громоздкими или неполными, и нам придётся делать ещё один запрос, чтобы получить более полную информацию. Само собой, можно спроектировать метод get-results так, чтобы он сводил потери такого вида к минимуму, но гораздо проще в сопровождении будет отделение статистики и статуса процесса поиска в отдельный метод: check. Он может возвращать статистику: сколько уже найдено, минимальная и максимальная цена \ вес \ что-либо ещё из нужных параметров, при этом не станет затрагивать сами данные.

Передача массива элементов под одинаковым именем