Несовместимое поведение метода преобразования/карты коллекции Laravel

В моем интерфейсе HTML у меня есть jQuery DataTable, отображающий все записи, полученные через AJAX из базы данных - довольно прямолинейная вещь. Я использую ->transform(function($o){ . . . }) коллекции Laravel, чтобы перебирать коллекцию и возвращать ее в виде массива. Просто подумайте о следующем фрагменте кода в контроллере:

        $cAllRecords = DatabaseRecord::all();
        if (!empty($aData['sFilterIds']))
        {
            $cAllRecords = $cAllRecords->whereIn('creator', explode(',', $aData['sFilterIds']));
        }
        return response()->json(['data' => $cAllRecords->transform(function ($oDatabaseRecord) {
            /** @var $oDatabaseRecord DatabaseRecord */
            $sActionsHtml = '<a href = "#">edit</a>';
            $sUrl = route('some.route', ['iDatabaseRecordId' => $oDatabaseRecord->getAttribute('od')]);
            return [
                $oDatabaseRecord->getAttribute('id'),
                $oDatabaseRecord->getAttribute('updated_at')->toDateTimeString(),
                $oDatabaseRecord->getAttribute('created_at')->toDateTimeString(),
                $sActionsHtml
            ];
        })]);

На самом деле я просто фильтрую записи, созданные определенными идентификаторами пользователей (вызов whereIn() в строке 4. Однако ответ, отправленный обратно клиенту, выглядит по-разному для разных отфильтрованных пользователей, что приводит к тому, что таблица jQuery показывает «нет доступных записей», поскольку это получил некорректный ответ от сервера. Для одного пользователя ответ выглядит так:

{
   "data":[
      [
         1,
         "2019-05-29 16:44:53",
         "2019-05-29 16:44:53",
         "<a href=\"#\">edit</a>"
      ]
   ]
}

Это правильно сформированный ответ сервера, который будет регулярно отображаться в таблице. Здорово! Теперь что-то, что сводит меня с ума - тот же код для другого пользователя (ID 1, в то время как первый запрос был для пользователя ID 2) возвращает это:

{
   "data":{
      "1":[
         3,
         "2019-05-29 17:08:49",
         "2019-05-29 17:08:49",
         "<a href=\"#\">edit</a>"
      ]
   }
}

который, совершенно очевидно, искажен и неправильно анализируется таблицей данных. ОК, теперь, объединив два фильтра и фильтрацию для идентификаторов пользователей 1 и 2, мы снова вернем ответ в правильном формате:

{
   "data":[
      [
         1,
         "2019-05-29 16:44:53",
         "2019-05-29 16:44:53",
         "<a href=\"#\">edit</a>"
      ],
      [
         3,
         "2019-05-29 17:08:49",
         "2019-05-29 17:08:49",
         "<a href=\"#\">edit</a>"
      ]
   ]
}

Я пробовал несколько вещей, но ни одна из них не сработала, поскольку я просто догадывался, почему это может работать с одним пользователем, а не с другим. (Такие вещи, как изменение порядка идентификаторов для фильтрации и т. д., но я обнаружил, что фильтрация не является проблемой. Это ДОЛЖНО быть преобразованием, которое ведет себя непоследовательно.)

Любые идеи о том, почему это происходит и как с этим бороться? Я имею в виду, что это не единственный способ добиться того, что мне нужно, я использовал ->each() и array_push все время до этого, но хотел избавиться от него ради использования помощников Laravel (или возможности) — ручная итерация и процесс отправки массива отрабатывались без проблем раньше, и даже другие части приложения хорошо работают с преобразованием коллекции по сравнению с итерацией и отправкой массива. Почему здесь нет?

Обновление: метод сбора ->map() ведет себя точно так же. Map, в отличие от transform, не изменяет саму коллекцию. Однако в любом случае это не должно быть важной частью данного приложения. Я действительно не могу понять, что происходит. Возможно, это вина Laravel?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
0
119
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Обратите внимание, что метод transform возвращает Illuminate\Support\Collection.

Лучше вызывать all() после преобразования, чтобы получить результат массива.

Так:

...
return response()->json(['data' => $cAllRecords->transform(function ($oDatabaseRecord) {
            /** @var $oDatabaseRecord DatabaseRecord */
            $sActionsHtml = '<a href = "#">edit</a>';
            $sUrl = route('some.route', ['iDatabaseRecordId' => $oDatabaseRecord->getAttribute('od')]);
            return [
                $oDatabaseRecord->getAttribute('id'),
                $oDatabaseRecord->getAttribute('updated_at')->toDateTimeString(),
                $oDatabaseRecord->getAttribute('created_at')->toDateTimeString(),
                $sActionsHtml
            ];
        })->all()]);

Спасибо за предложение. Это дало мне надежду уже во время чтения, но оказалось неверным, поскольку возвращает то же самое (что для меня не имеет смысла).

Johnny 30.05.2019 17:34
Ответ принят как подходящий

Ответ @Cvetan Mihaylov заставил меня просмотреть все доступные методы сбора (https://laravel.com/docs/5.8/collections#available-methods), и я обнаружил, что ->values() возвращает переиндексированные значения. И - это сделало свое дело! :-)

return response()->json(['data' => $cAllRecords->transform(function ($oDatabaseRecord) {
        /** @var $oDatabaseRecord DatabaseRecord */
        $sActionsHtml = '<a href = "#">edit</a>';
        $sUrl = route('some.route', ['iDatabaseRecordId' => $oDatabaseRecord->getAttribute('od')]);
        return [
            $oDatabaseRecord->getAttribute('id'),
            $oDatabaseRecord->getAttribute('updated_at')->toDateTimeString(),
            $oDatabaseRecord->getAttribute('created_at')->toDateTimeString(),
            $sActionsHtml
        ];
    })->values()]);

Что ж, немного странно, что использование ->values() лучше, чем ->all(), потому что values() по-прежнему возвращает коллекцию, а затем все еще происходит немного «волшебства», когда она сериализуется внутри вызова response()->json(). В то время как ->all() возвращает массив и ясно, что вы сериализуете впоследствии. В любом случае, рад, что вы нашли решение. Вы могли бы проголосовать, если нашли ответ полезным.

Cvetan Mihaylov 31.05.2019 11:00

Ну, волшебство заключается в свойствах Arrayable и Jsonable, которые дает коллекция Laravel. Точно так же вы можете получить доступ к значениям коллекции с помощью $collection['key'].

Johnny 01.06.2019 09:31

Другие вопросы по теме