Laravel: Обратная сторона популярности или О чем не расскажут на конференциях

Laravel: Обратная сторона популярности или О чем не расскажут на конференциях

Laravel, без сомнения, один из столпов современной PHP-разработки. Его популярность зашкаливает, и на это есть веские причины: низкий порог входа, огромное сообщество, богатая экосистема и возможность быстро создавать как простые, так и средней сложности проекты. Интеграция с Inertia.js – это вообще отдельная песня, позволяющая относительно безболезненно собирать SPA, пользуясь всеми прелестями fullstack-подхода. Однако, как и у любой технологии, у Laravel есть свои «скелеты в шкафу» – моменты, которые могут доставить немало головной боли, особенно в крупных и долгоживущих проектах. Давайте поговорим о них подробнее.

1. Вечная битва за типы

Несмотря на то, что PHP активно движется в сторону строгой типизации, и Laravel старается не отставать, проблемы с типами все еще остаются актуальными. Да, с каждой новой версией фреймворк становится более типизированным, но до идеала пока далеко. В проектах, особенно в тех, где важна высокая надежность и предсказуемость кода, например, в CRM-системах, где ошибки могут стоить реальных денег, недостаточная строгость типов периодически выливается в неожиданные баги на этапе выполнения, которые можно было бы отловить еще на этапе разработки.

2. Inertia.js: Осторожно, все на виду!

Inertia.js – это действительно прорыв для Laravel в мире SPA. Но есть нюанс, о котором часто забывают новички (да и опытные разработчики порой наступают на эти грабли): все данные, которые вы передаете из бэкенда во фронтенд-компоненты, становятся видны на клиенте. Классический пример из разработки интернет-магазина: случайно передали в компонент карточки товара массив со всеми активными промокодами. Вроде бы мелочь, а неприятно. Отсюда золотое правило: всегда используйте Data Transfer Objects (DTO) и тщательно фильтруйте то, что уходит на фронт. Особенно это критично в CRM, где вы можете случайно "засветить" внутренние статусы сделок или персональные данные менеджеров, не предназначенные для глаз клиента.

3. Livewire: Магия PHP вместо JavaScript – не для всех

Технология Livewire позиционируется как способ писать интерактивные интерфейсы, не покидая уютный мир PHP. Для простых вещей, вроде динамической фильтрации списка или автообновления блока, это может быть удобно. Но лично у меня с Livewire дружба так и не сложилась. Сама идея писать на PHP то, что по своей природе должно быть на JavaScript, кажется мне несколько противоестественной. Вместо того чтобы использовать всю мощь и гибкость современного JS (и его экосистемы), приходится осваивать новый синтаксис и мириться с ограничениями. Попытка реализовать большую вложенную форму с множеством динамических зависимостей и каскадными фильтрами на Livewire обернулась для меня фиаско. В итоге, если стоит выбор между Inertia.js и Livewire, мой голос всегда уходит первой.

4. Единоличное управление: Особенности принятия решений в Laravel

Если сравнивать с Symfony, где сильно развито комьюнити и коллективное принятие решений, в Laravel все несколько иначе. Ключевые решения по развитию фреймворка, принятию Pull Request'ов и внедрению новых фич, по сути, находятся в руках одного человека – Тейлора Отвелла. Количество основных контрибьюторов строго ограничено, и это, в основном, сотрудники Laravel. Такой подход порождает довольно специфические процессы. Нередки случаи, когда критически важные вопросы, активно обсуждаемые сообществом, в итоге решаются Тейлором так, как он считает нужным, даже если это противоречит аргументам большинства. Яркий пример – проблема с truncate, который мог каскадно затронуть другие таблицы в PostgreSQL (тикет #35157 на GitHub). Долгое время Тейлор отказывался исправлять этот баг, ссылаясь на обратную совместимость, хотя проблема затрагивала только один драйвер базы данных, и о какой-либо глобальной обратной совместимости речи не шло.

Такая схема управления вызывает недовольство у части пользователей, и, честно говоря, в других крупных open-source проектах подобное встречается редко. Сам Тейлор в интервью упоминал, что может отклонить PR, а затем реализовать ту же функциональность самостоятельно, просто чтобы лучше понять, как она работает. Такой подход, на мой взгляд, несколько подрывает дух Open Source и снижает мотивацию разработчиков контрибьютить в проект.

5. Миграции: Боль длиной в годы

Представьте себе проект, который активно развивается пять, семь, а то и десять лет. В него постоянно добавляются новые таблицы, поля, изменяются существующие. Сколько миграций накопится за это время? Сотни, если не тысячи. А теперь представьте, сколько времени займет первоначальная настройка такого проекта с нуля, когда каждая из этих миграций будет последовательно накатываться. Инструмента для "сквошинга" (объединения) миграций в ядре Laravel долгое время не было, хотя в большинстве других фреймворков эта проблема решена лет пятнадцать назад, например, через единый файл схемы, который отражает актуальное состояние БД.

Тейлор долго сопротивлялся внедрению подобного механизма в ядро, считая это излишним. Лишь относительно недавно появилась возможность сгенерировать SQL-файл со схемой базы данных, который используется при миграциях вместо прогона всех старых файлов. Но, во-первых, это не поведение по умолчанию. Во-вторых, работает оно не так гладко, как в других фреймворках: после создания миграции схема не обновляется автоматически, требуются дополнительные действия. А ведь многие библиотеки и инструменты разработки полагаются на актуальный файл схемы для автодополнения и анализа кода. В CRM, где структура данных может быть весьма разветвленной и часто меняться, управление таким количеством миграций превращается в настоящую пытку.

6. Middleware: Путаница в слоях

Как в Laravel описать все middleware? По умолчанию это делается в файле bootstrap/app.php (в версиях до Laravel 11 это был app/Http/Kernel.php). Для маленького приложения – никаких проблем. Но как только проект разрастается, этот файл превращается в трудночитаемый список. Как применить middleware к конкретному маршруту или группе маршрутов? Это можно сделать в файлах routes/web.php или routes/api.php. Однако разобраться в общей структуре применения middleware, понять, где какой слой используется, становится очень сложно. Файл bootstrap/app.php (или Kernel.php) дает более наглядное представление о группах, но не позволяет назначать middleware на отдельные маршруты с той же гибкостью.

Новый синтаксис в Laravel 11+ для bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('group-name', [
        First::class,
        Second::class,
    ]);
    $middleware->prependToGroup('group-name', [
        Third::class,
        Fourth::class,
    ]);
})

Это немного улучшает ситуацию для групп, но общая проблема читаемости и управления слоями в больших приложениях остается. Справедливости ради, в Symfony, где middleware как таковых нет, а есть система событий и слушателей, логика обработки запроса может быть еще более неявной и запутанной.

7. Кэширование: "Просто очисти кэш!"

Это, пожалуй, одна из самых наболевших тем в Laravel. Еще на заре своей работы с фреймворком меня невероятно раздражал универсальный ответ на многие проблемы: "Очисти кэш". Делаешь что-то, не понимаешь, почему не работает, лезешь в интернет, а там – "php artisan cache:clear", "php artisan config:clear", "php artisan route:clear", "php artisan view:clear"... У Laravel несколько видов кэша, и не всегда очевидно, какой из них за что отвечает и какой именно нужно чистить в данный момент.

Да, на это есть свои технические причины, и при желании можно разобраться в нюансах. Но почему я должен тратить время на изучение особенностей кэширования самого фреймворка, особенно в локальном окружении? Помню дни, когда я часами бился над проблемой, которая решалась одной командой очистки кэша. И речь не о кэше приложения (данных, которые мы сами кэшируем), а о внутреннем кэше фреймворка – конфигурации, маршрутов, представлений. Ключевая проблема в том, что Laravel кэширует значения переменных окружения (.env файла) при выполнении config:cache. Такой подход не только доставляет неудобства при разработке, но и противоречит принципам Twelve-Factor App, которые предписывают строгую изоляцию конфигурации от кода.

8. Отсутствие полноценной Message Bus "из коробки"

В Laravel есть система "задач" (Jobs), которая позволяет выполнять отложенные операции. Это удобно для отправки email, обработки изображений и т.д. Но если вам нужно выстроить взаимодействие между несколькими микросервисами, особенно если не все они написаны на Laravel, то Jobs оказываются не лучшим решением. Систему Jobs в Laravel сложно назвать полноценной шиной сообщений в классическом понимании. Да, фреймворк позволяет отправить событие, например, в RabbitMQ, но вы не можете гибко контролировать формат сообщения или определять свою логику работы с очередями без значительных "допиливаний". Для сложных сценариев межсервисного взаимодействия приходится либо интегрировать сторонние решения, либо писать много кастомного кода.

9. JSON-переводы: Без права на подпапки

Laravel предоставляет два способа хранения файлов переводов: в виде PHP-массивов или в JSON-файлах. Если вы выбираете PHP-файлы, вы можете удобно структурировать их по подпапкам, например, lang/en/user.php, lang/en/product.php. Это очень удобно для больших многоязычных проектов. Однако, если вы по какой-то причине решили использовать JSON-файлы для переводов (например, для более простого парсинга на фронтенде), то будьте готовы к тому, что все ключи для одного языка должны лежать в одном файле (lang/en.json). Возможности разбить их на логические подпапки, как в случае с PHP-файлами, нет. Это может привести к огромным, трудно управляемым JSON-файлам.

10. Автодополнение в IDE: Магия ценой удобства

Споры о том, хорошо или плохо использовать фасады и "магию" в фреймворках, не утихают. Мое мнение: магия – это хорошо, когда она предсказуема и не мешает разработке. В Laravel же "магия" часто приводит к тому, что без дополнительных инструментов вы не можете полноценно пользоваться автодополнением в вашей IDE. Чтобы IDE поняла, что у User::create() есть такой метод, вам нужно установить специальный плагин (например, Laravel Idea для PhpStorm), потому что статический метод create() на самом деле отсутствует в классе User, он "магически" проксируется через фасад. Если же вы хотите обойтись без плагинов, приходится писать более многословные конструкции вроде User::query()->create(). Аналогичная ситуация и с автодополнением полей модели – без плагинов IDE их не увидит.

11. hasManyThrough: Загадка аргументов

Я специально выделил этот метод определения отношений Eloquent в отдельный пункт. Сколько бы я ни писал на Laravel, я так и не смог довести до автоматизма запоминание порядка аргументов в hasManyThrough. Каждый раз, когда мне нужно его использовать, я лезу в документацию. Почему нельзя было сделать более интуитивно понятный синтаксис, например, что-то вроде hasMany(Deployment::class)->through(IntermediateTable::class)->hasMany(Environment::class) (утрированный пример для иллюстрации идеи), для меня остается загадкой.

12. Трейты, трейты повсюду

Laravel очень активно использует трейты. Они буквально везде. С одной стороны, это позволяет переиспользовать код и добавлять функциональность классам. С другой – это значительно усложняет чтение и понимание исходного кода. Нередки случаи, когда одни трейты используют другие трейты, те, в свою очередь, еще какие-то, и так далее. В итоге мы сталкиваемся с так называемой "Yo-yo problem", когда для понимания логики работы одного метода приходится прыгать по цепочке из нескольких трейтов, пытаясь удержать в голове всю эту сложную структуру наследования и примесей.

13. "Мягкость" моделей по умолчанию

В моделях Eloquent по умолчанию отключены такие полезные опции, как strictMode и preventAccessingMissingAttributes (или preventSilentlyDiscardingAttributes в более новых версиях). Их нужно явно включать в сервис-провайдере или базовой модели. К сожалению, на многих проектах, которые попадают на поддержку, эти фичи не активированы. К чему это приводит? К очень странному поведению, когда вы по ошибке обращаетесь к несуществующему свойству модели или пытаетесь присвоить значение несуществующему атрибуту, и Laravel это "проглатывает" без каких-либо ошибок или предупреждений (в зависимости от конкретной опции и версии). Это может маскировать баги и приводить к трудноотлавливаемым проблемам, особенно когда вы случайно опечатались в имени поля при массовом присваивании атрибутов в CRM, где точность данных критична.

Заключение

Несмотря на перечисленные недостатки, Laravel был и остается мощным инструментом для веб-разработки. Его сильные стороны часто перевешивают слабые, особенно для определенных типов проектов. Однако важно знать и о "темной стороне", чтобы быть готовым к возможным трудностям и принимать взвешенные решения при выборе стека технологий или при долгосрочной поддержке Laravel-проекта. Понимание этих нюансов поможет вам писать более качественный код и избегать многих подводных камней.

Популярное

Самые популярные посты

Как быть максимально продуктивным на удалённой работе?
Business

Как быть максимально продуктивным на удалённой работе?

Я запустил собственный бизнес и намеренно сделал всё возможное, чтобы работать из любой точки мира. Иногда я сижу с своём кабинете с большим 27-дюймовым монитором в своей квартире в г. Чебоксары. Иногда я нахожусь в офисе или в каком-нибудь кафе в другом городе.

Привет! Меня зовут Сергей Емельянов и я трудоголик
Business PHP

Привет! Меня зовут Сергей Емельянов и я трудоголик

Я программист. В душе я предприниматель. Я начал зарабатывать деньги с 11 лет, в суровые 90-е годы, сдавая стеклотару в местный магазин и обменивая её на сладости. Я зарабатывал столько, что хватало на разные вкусняшки.

Акция! Профессиональный разработчик CRM за 2000 руб. в час

Выделю время под ваш проект. Знания технологий Vtiger CRM, SuiteCRM, Laravel, Vue.js, Golang, React.js, Wordpress. Предлагаю варианты сотрудничества, которые помогут вам воспользоваться преимуществами внешнего опыта, оптимизировать затраты и снизить риски. Полная прозрачность всех этапов работы и учёт временных затрат. Оплачивайте только рабочие часы разработки после приемки задачи. Экономьте на платежах по его содержанию разработчика в штате. Возможно заключение договора по ИП. С чего начать, чтобы нанять профессионального разработчика на full-time? Просто заполните форму!

Telegram
@sergeyem
Telephone
+4915211100235
Email