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

Сам сервис написан на PHP с использованием фреймворка Laravel и служит для общения с внешней системой.

Поскольку есть внешняя система, то в первую очередь проверил её. Но с ней всё было в порядке. Данные уходили и приходили. И в БД сервиса всё заносилось как надо.

Но при обращении к ресурсам определённых объектов по API не возвращалась часть полей, которые хранятся в связанной таблице, связь типа полиморфное отношение «один-к-одному» («MorphOne»).

class FirstModel extends Model
{
    ...

    public function childModel(): MorphOne
    {
        return $this->morphOne(ChildModel::class, 'entity');
    }
    ...
}
class SecondModel extends Model
{
    ...

    public function childModel(): MorphOne
    {
        return $this->morphOne(ChildModel::class, 'entity');
    }
    ...
}
class ChildModel extends Model
{
    ...

    public function entity(): MorphTo
    {
        return $this->morphTo();
    }
    ...
}

Анализ проблемных объектов показал, что неполные данные возвращались по тем из них, которые были созданы до определенного момента времени вчера. Значит, дело возможно в изменениях в коде. Посмотрел логи git’а.

И в них увидел, что как раз вчера, добавляя новые модели, коллега изменил пространство имён у ряда уже существующих. Именно у тех, в которых и появились проблемы. Само изменение было правомерным и давно напрашивалось. И сделано правильно: пространство имён было изменено во всех нужных местах кода. PHPStorm за этим проследил.

Было:

namespace App\Models\Name1;
...
class FirstModel extends Model
{
    ...
}
namespace App\Models\Name1;
...
class SecondModel extends Model
{
    ...
}

Стало:

namespace App\Models\Name2;
...
class FirstModel extends Model
{
    ...
}
namespace App\Models\Name2;
...
class SecondModel extends Model
{
    ...
}

Значит, предположил я, где-то закэшировалось старое namespace. Были очищены все кэши Laravel. Но проблема осталась.

Снова внимательно изучил записи в БД. В них всё было записано правильно.

Обратился за помощью к коллегам. Созвонились, описал проблему, показал сделанные шаги. Бурное обсуждение и дискуссия не дали результата. В конце концов, один из нас спросил у DeepSeek. И в ответе ИИ, глаз выхватил строку про то, что в таблице, связанной связью типа “MorphOne”, хранится полное квалифицированное имя объекта.

Снова глянул в таблицу БД. Ну, конечно. Для объектов, созданных до переноса классов в новое пространство, в таблице было записано старое. Я же смотрел на эти записи и не заметил очевидного. Вот что значит «глаз замылился».

Написал и залил простенькую миграцию.

    public function up()
    {
        ChildModel::where('entity_type', 'App\Models\Name1\FirstModel')
            ->update(['entity_type' => 'App\Models\Name2\FirstModel']);
        ChildModel::where('entity_type', 'App\Models\Name1\SecondModel')
            ->update(['entity_type' => 'App\Models\Name2\SecondModel']);
    }

Дождался окончания тестирования и развёртывания. И вуаля, всё заработало правильно.

Вывод простой: в Laravel при любых изменения в полном квалифицированном имени класса модели, имеющей полиморфные отношения, не забывать изменять эти имена в соответствующих связанных таблицах в БД.

И остаётся проблема: как не забыть про этот нюанс в следующий раз.

Комментарии (4)