Я хочу создать вложенный объект JSON, примерно так:
[
{
"age": 8,
"countAll": 3,
"gender": [
{
"genderName": "male",
"countGender": 2
},
{
"genderName": "female",
"countGender": 1
},
]
},
{
"age": 10,
"countAll": 1,
"gender": [
{
"genderName": "male",
"countMale": null (or "0")
},
{
"genderName": "female",
"countFemale": 1
},
]
},
| id | name | gender | age | user_id |
--------------------------------------------
| 1 | nameA | male | 8 | 1 |
| 2 | nameB | female | 10 | 1 |
| 3 | nameC | male | 8 | 1 |
| 4 | nameD | female | 8 | 1 |
Я использую laravel 10, это запрос, который я сделал
$test = Participant::select('age', DB::raw('COUNT(age) as countAge'), DB::raw('COUNT(gender) as countGender'))->where('user_id', $user_id)->groupBy('age')->orderBy('age', 'ASC')->get();
$output = array();
$currentAge = "";
$currentCount = "";
foreach ($test as $data) {
if ($data->age != $currentAge) {
$output[] = array();
end($output);
$currentItem = &$output[key($output)];
$currentAge = $data->age;
$currentCount = $data->countAge;
$currentItem['age'] = $currentAge;
$currentItem['countAll'] = $currentCount;
$currentItem['gender'] = array();
}
$currentItem['gender'][] = array('genderName' => $data->gender, 'countGender' => $data->countGender);
}
и результат json_encoded:
[
{
"age": 8,
"countAll": 3,
"gender": [
{
"genderName": null,
"countGender": 3
}
]
},
{
"age": 10,
"countAll": 1,
"gender": [
{
"genderName": null,
"countGender": 1
}
]
},
Я прочитал много ссылок о создании вложенного JSON и внес много изменений в запрос SQL, но пока не нашел решения.
Как я могу заставить этот запрос Eloquent возвращать вложенный объект JSON, подобный тому, который я описал выше?
Поскольку mysql является реляционной базой данных, она может возвращать только простой табличный вывод. Использование красноречия не меняет этого. Вам либо нужно довольствоваться табличным выводом из базы данных, либо вам нужно выполнить некоторую обработку пост-запроса в вашем PHP-коде, чтобы получить желаемый результат.
Нечто подобное можно сделать, как показано ниже:
$participants = Participant::select('age')
->selectRaw('COUNT(*) as countAll')
->selectRaw('SUM(CASE WHEN gender = "male" THEN 1 ELSE 0 END) as countMale')
->selectRaw('SUM(CASE WHEN gender = "female" THEN 1 ELSE 0 END) as countFemale')
->where('user_id', $user_id)
->groupBy('age')
->orderBy('age', 'ASC')
->get();
$result = [];
foreach ($participants as $participant) {
$genderData = [
[
'genderName' => 'male',
'countGender' => $participant->countMale ?? 0,
],
[
'genderName' => 'female',
'countGender' => $participant->countFemale ?? 0,
]
];
$result[] = [
'age' => $participant->age,
'countAll' => $participant->countAll,
'gender' => $genderData,
];
}
$jsonOutput = json_encode($result);
Пожалуйста, не публикуйте ответы, содержащие только код, в Stack Overflow.
Я считаю, что желаемая структура вывода немного громоздка. Возможно, было бы разумнее объявить более глубокие точки данных, специфичные для пола, более простыми ассоциативными элементами genderCounts => ['male' => 2, 'female' => 1]
. Фактически, чтобы избежать свертки вызовов методов сбора в наборе результатов, вам следует свести желаемый результат к двумерной структуре, чтобы его можно было достичь исключительно с помощью SQL.
Код: (Демо-версия PHPize)
$user_id = 1;
var_export(
DB::table('Participant')
->select('age')
->selectRaw('COUNT(1) total')
->selectRaw("SUM(gender = 'male') male")
->selectRaw("SUM(gender = 'female') female")
->where('user_id', $user_id)
->groupBy('age')
->orderBy('age', 'ASC')
->get()
->toArray()
);
чтобы подготовить эти агрегированные данные с помощью метода сбора Laravel, просто добавьте map()
после вызова get()
. (Демо-версия PHPize)
->map(fn($row) => [
'age' => $row->age,
'countAll' => $row->total,
'gender' => [
['genderName' => 'male', 'countMale' => $row->male],
['genderName' => 'female', 'countFemale' => $row->female]
]
])
Связано: Получите несколько значений из нескольких столбцов с несколькими условиями, используя всего один красноречивый запрос в Laravel