Laravel / SQL возвращает данные из отношения «многие ко многим» в виде массива данных

#php #mysql #laravel #eloquent

#php #mysql #laravel #красноречивый

Вопрос:

У меня есть следующая настройка: posts таблица, post_pics таблица и post_post_pictures таблица, которая действует как связующая таблица между сообщениями и post_pics . В одном сообщении может быть несколько фотографий. Я хотел бы выбрать все изображения, принадлежащие этому сообщению, и вернуть их в виде массива для каждого сообщения (пример данных внизу вопроса).

Настройка таблиц следующая:

Публикации:

  -------------- ----------------- ------ ----- --------- ------- 
| Field        | Type            | Null | Key | Default | Extra |
 -------------- ----------------- ------ ----- --------- ------- 
| id           | char(36)        | NO   | PRI | NULL    |       |
| ............... Other fields not important .................. |
|                                                               |
 -------------- ----------------- ------ ----- --------- ------- 
  

post_pics:

  --------- -------------- ------ ----- --------- ------- 
| Field   | Type         | Null | Key | Default | Extra |
 --------- -------------- ------ ----- --------- ------- 
| id      | char(36)     | NO   | PRI | NULL    |       |
| picture | varchar(255) | NO   |     | NULL    |       |
 --------- -------------- ------ ----- --------- ------- 
  

post_post_pictures:

  ----------------- ---------- ------ ----- --------- ------- 
| Field           | Type     | Null | Key | Default | Extra |
 ----------------- ---------- ------ ----- --------- ------- 
| post_id         | char(36) | NO   | MUL | NULL    |       |
| post_picture_id | char(36) | NO   | MUL | NULL    |       |
 ----------------- ---------- ------ ----- --------- ------- 
  

У меня есть следующий запрос, в котором я выбираю записи в заданном диапазоне на основе некоторых координат (эта часть работает нормально) — у меня возникли проблемы с выбором всех фотографий, которые есть в каждом сообщении, и возвращением этих фотографий в одной записи «post».

Соединения, которые я выполняю post_post_pictures , и post_pics , я думаю, неправильные / неполные.

 $meanRadiusOfEarthInMiles = 3959;
$poiSearchDistanceInMiles = 60;
$clientLatitude = floatval($request->get('latitude'));
$clientLongitude = floatval($request->get('longitude'));
$posts = Post::select(
    'posts.*',
    'users.id as user_id',
    'users.photo as company_photo',
    'users.name as company_name',
    'post_pics.picture as images'
    )
    ->join('users', 'posts.user_id', '=', 'users.id')                // This is incomplete I think
    ->join('post_post_pictures', 'posts.id','=', 'post_id')          // and this one, I think
    ->join('post_pics', 'post_post_pictures.post_id','=', 'post_id')
    ->where('active', '=', '1')
    ->where('account_type', '=', 'business')
    ->selectRaw("($meanRadiusOfEarthInMiles *
                    acos(cos(radians(?)) * cos(radians(latitude)) *
                    cos(radians(longitude) - radians(?))  
                    sin(radians(?)) * sin(radians(latitude)))
            ) AS miles_away", [$clientLatitude, $clientLongitude, $clientLatitude])
            ->havingRaw("miles_away < ?", [$poiSearchDistanceInMiles])
            ->get();
  

Приведенный выше запрос возвращает почти правильные данные, но похоже, что он возвращает одну строку для каждого изображения, вместо добавления всех изображений, принадлежащих записи, в эту запись.

Например, прямо сейчас я получаю много таких записей:

 {
"id": "0792c20e-498c-4aa7-a2b2-f06f4775dc82",
"title": "test",
"description": "test",
"location": "<location name>",
"latitude": "<some lat>",
"longitude": "<some lon>",
"user_id": "271564de-35fa-4023-9b13-f68d0e9100d0",
"images": "<URL>/<IMAGE 1>",
"miles_away": 5.313459784530729
},
  

и я надеюсь, что смогу возвращать строки, подобные этой:

 {
"id": "0792c20e-498c-4aa7-a2b2-f06f4775dc82",
"title": "test",
"description": "test",
"location": "<location name>",
"latitude": "<some lat>",
"longitude": "<some lon>",
"user_id": "271564de-35fa-4023-9b13-f68d0e9100d0",
"images": ["<URL>/<IMAGE 1>", "<URL>/<IMAGE 2>", ...],  // return all images for the post as array would be ideal
"miles_away": 5.313459784530729
},
  

Возможно ли это? Я хочу вернуть его таким образом, поскольку клиентскому приложению легко работать.

Комментарии:

1. dba.stackexchange.com/questions/159765 /… может быть полезно

2. Ах да, вероятно, требуется что-то вроде включения DB::raw('GROUP_CONCAT(post_pics.picture) as images') в вызов to Post::select . Спасибо

Ответ №1:

Спасибо @Nick Maroulis за комментарий, который навел меня на правильный путь для решения этой проблемы.

Мой запрос в итоге выглядел так:

 $posts = Post::select(
    'posts.*',
    'users.id as user_id',
    'users.photo as company_photo',
    'users.name as company_name',
    DB::raw('GROUP_CONCAT(post_pics.picture) as images')  // < ---- change
)
    ->join('users', 'posts.user_id', '=', 'users.id')                
    ->join('post_post_pictures', 'posts.id','=', 'post_id')          
    ->join('post_pics', 'post_post_pictures.post_picture_id','=', 'post_pics.id')
    ->where('active', '=', '1')
    ->where('account_type', '=', 'business')
    ->selectRaw("($meanRadiusOfEarthInMiles *
                    acos(cos(radians(?)) * cos(radians(latitude)) *
                    cos(radians(longitude) - radians(?))  
                    sin(radians(?)) * sin(radians(latitude)))
            ) AS miles_away", [$clientLatitude, $clientLongitude, $clientLatitude])
            ->havingRaw("miles_away < ?", [$poiSearchDistanceInMiles])
            ->groupBy('post_post_pictures.post_id') // < ---- change
            ->get();
  

Что дает результат, аналогичный следующему:

 {
"id": "c4df92c6-1abf-4aeb-974c-b06b75dd45d2",
"title": "test post",
"latitude": "...",
"longitude": "...",
"availability": "...",
....
"user_id": "62fa9a40-3bea-48e5-badb-87d2c8d2ce35",
"views": 1,
"images": "<URL 1>,<URL 2>",                        // comma seperated URLs for the images
"miles_away": 0.5339829289762683
}
  

Изображения в конечном итоге разделяются запятой, а не в массиве, когда я отправляю его обратно клиенту в формате JSON, но это нормально 🙂