В моем интерфейсе 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?
Обратите внимание, что метод 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()]);
Ответ @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()
возвращает массив и ясно, что вы сериализуете впоследствии. В любом случае, рад, что вы нашли решение. Вы могли бы проголосовать, если нашли ответ полезным.
Ну, волшебство заключается в свойствах Arrayable и Jsonable, которые дает коллекция Laravel. Точно так же вы можете получить доступ к значениям коллекции с помощью $collection['key'].
Спасибо за предложение. Это дало мне надежду уже во время чтения, но оказалось неверным, поскольку возвращает то же самое (что для меня не имеет смысла).