Работая над очередным проектом на фреймворке Laravel мы обнаружили его уязвимое место. Это модель Eloquent, о которой написано немало, и примеров её использования тоже достаточно. Вот что мы разглядели. Возьмём простую модель (не Eloquent) на рисунке 1.
Видим, что джойнятся несколько таблиц, делается какая-то выборка по определённым свойствам из этого join, а потом апдейтится определённое поле определённой таблицы. Случай ординарный и не заслуживает, чтобы его подробно разбирали.
Но что произойдёт если мы применим модель Eloquent вместо предыдущей модели? Как например на рисунке 2.
Здесь идёт выборка полей из таблиц ‘prospects’, ‘properties’, ‘leads’. Во всех трёх таблицах есть поле ‘id’.
Что мы ожидали? После join таблиц по определённым признакам мы хотели взять у этих leads поле ‘mailer_address_1’ и отправить его на geocoding и получить для этого поля новое значение, т.е. сделать update.
Что по факту получилось благодаря Eloquent? Поскольку поле ‘id’ встречается в нескольких таблицах, то при update leads у нас финальный sql получился — update leads & id where id от prospects, а не от ‘leads’. Если подробнее, то на выходе join получаем множество столбцов с одинаковыми (повторяющимися) полями. Когда модель Eloquent наполняется, то перебираются столбцы по очереди. В случае ‘id’ каждый следующий перезатирает предыдущий ‘id’, который взят из join. Коварство Eloquent в том, что если даже задать alias, то имена полей после join всё равно будут без префиксов, просто ‘id’.
Решение проблемы Eloquent. Надо указывать конкретные поля. Мы добавили строку → selectRaw (“distinct leads.*”)
как на рисунке 3.
Эти ошибки невероятно коварны, так как ваш скрипт работает, а вы получаете ерунду в базе. Узнаёте вы об этом спустя время, когда уже поздно.