22. Робимо Laravel очевиднішим. Це треба увімкнути в laravel. Проблема N+1, тести н плюс один.
Відео версія
(YouTube - для зворотнього зв'язку)
N+1
Проблема N+1 актуальна для всіх ORM. Виникає вона дуже просто: кожного разу, коли коду потрібен доступ до пов'язаного запису, виконується новий SQL запит для отримання цього запису. Це є властивістю ORM, що називається Lazy loading
. Протилежністю лінивого завантаження є Eager Loading
, яке слід використовувати завжди.
Заблокувати ліниве завантаження в Laravel глобально дозволяє команда:
Model::preventLazyLoading();
Після активації, при спробі отримати незавантажену Eloquent залежність, ви отримаєте помилку замість мовчазного виконання.
Важливо! Ця команда не вирішує проблему N+1 повністю. У розробника залишається безліч варіантів зробити помилку.
Єдине автоматизоване рішення для боротьби з N+1 - автотести.
$this->expectsDatabaseQueryCount($expectedQueryCount);
Важливо! Метод рахує кількість підключень до БД, а не запитів. Тому обгортання в транзакцію методу завжди дасть 1 запит. Будьте обережні.
Доступ до незавантажених атрибутів
За замовчуванням, Eloquent повертає null при спробі отримати атрибут, який ви не завантажили. Це відбувається у випадках, коли ви не використовуєте Select *
. Це може спричинити купу неявних помилок, від звичайної перевірки булевої змінної до ситуацій з віддачею клієнту null замість даних, які реально існують.
Таку поведінку можна глобально заблокувати командою:
Model::preventAccessingMissingAttributes();
Неочевидне масове призначення в Eloquent
При створенні Eloquent моделі зручно використовувати масове призначення за допомогою функцій fill або create. Для безпеки такого заповнення клас моделі вказує, які поля можна масово заповнювати, а які ні, за допомогою властивостей fillable або guarded.
Всі властивості, що не вказані у fillable або вказані в guarded, просто не збережуться в моделі при спробі їх зберегти. Це неочевидна поведінка, якої треба уникати.
Model::preventSilentlyDiscardingAttributes();
Тепер при спробі призначити недозволену властивість ви отримаєте помилку. Зазвичай такі поля надходять від непровалідованих запитів.
Ніколи не використовуйте $request->all(). Використовйте тільки $request->validated() з описом всіх властивостей.
Eloquent strict mode в продакшені
Всі три команди можна увімкнути однією:
public function boot(): void
{
Model::shouldBeStrict();
}
Булевим параметром можна вмикати та вимикати ці правила. Якщо ви підтримуєте вже існуючий проєкт, не вмикайте цей режим для продакшену! Користувачі не повинні отримувати помилки, які з великою вірогідністю будуть виникати. Локально розробник та QA на стейджі допоможуть відловити такі помилки.
Якщо розробляєте проєкт з нуля - сміливо вмикайте глобально.
HTTP запити в тестах
Скоріш за все, ваше рішення матиме HTTP запити до зовнішніх сервісів або до мікросервісів. Не всі вони вимагають авторизації, але можуть мати обмеження на кількість запитів і банити за DDOS. Ваші ж тести можуть викликати такі запити безліч разів.
Тому раджу глобально заборонити реальні HTTP запити для всіх тестів:
protected function setUp(): void
{
parent::setUp();
Http::preventStrayRequests();
}
Всі ж запити до зовнішніх сервісів треба мокати:
Http::fake([
'http://ip-api.com/json/*' => Http::response([
'status' => 'success',
'continent' => 'Europe',
'continentCode' => 'EU',
'country' => 'Ukraine',
'countryCode' => 'UA',
'regionName' => 'Sumy',
'city' => 'Sumy',
'district' => '',
'zip' => '40009',
]),
]);