Laravel конфликты в отношениях в союзе - PullRequest
8 голосов
/ 17 июня 2020

У меня следующая модель: 1- Модель пользователя

 /**
 * Define user and functional area relationship
 */
public function functionalAreas()
{
    return $this->belongsToMany('App\FunctionalArea', 'user_functional_areas', 'user_id', 'functional_area_id')->withPivot('id', 'is_primary')->withTimestamps();
}

и бизнес-модель:

 /**
 * Define business and user functional area relationship
 */
public function functionalAreas()
{
    return $this->belongsToMany('App\FunctionalArea', 'business_functional_areas', 'business_id', 'functional_area_id')->withTimestamps();
}

Теперь я должен взять все компании и пользователей и показать их в одном списке, для этого я использую из union, следующий мой запрос:

public function usersAndOrganizations()
{
    $users = $this->users();

    $organizations = $this->organizations();

    $invitees = $users->union($organizations)->paginate(10);
    
    return response()->json($invitees);
}

private function users()
{
    $users = User::byState($approved = true, 'is_approved')
        ->search()->select([
            'id',
            DB::raw("CONCAT(first_name, ' ', last_name) AS name"),
            'about',
            'address',
            'slug',
            'average_reviews',
            DB::raw("'freelancer' AS type")
        ]);

  $users = $users->with([
        "functionalAreas" => function ($q) {
            $q->select([
                'functional_areas.id',
                DB::raw("functional_areas.name_en AS name"),
            ]);
        }
    ]);
    return $users;
}
 

private function organizations()
{
    $businesses = Business::where('owner_id', '!=', auth()->user()->id)->verified()
        ->active()->search()
        ->select([
            'id',
            'name',
            'about',
            'address',
            'slug',
            'average_reviews',
            DB::raw("'business' AS type")
        ]); 
        $businesses = $businesses
            ->with([
            "functionalAreas" => function ($q) {
                $q->select([
                    'functional_areas.id',
                    DB::raw("functional_areas.name_en AS name"),
                ]);
            }
        ]);
        return $businesses;
} 

Но вышеупомянутый запрос не возвращает функциональную область бизнеса, его выходной запрос использует отношения с пользователем вместо бизнеса, этот раздел with генерирует дважды следующий запрос:

select
  `functional_areas`.`id`,
  functional_areas.name_en AS name,
  `user_functional_areas`.`user_id` as `pivot_user_id`,
  `user_functional_areas`.`functional_area_id` as `pivot_functional_area_id`,
  `user_functional_areas`.`id` as `pivot_id`,
  `user_functional_areas`.`is_primary` as `pivot_is_primary`,
  `user_functional_areas`.`created_at` as `pivot_created_at`,
  `user_functional_areas`.`updated_at` as `pivot_updated_at`
from `functional_areas`
inner join `user_functional_areas`
  on `functional_areas`.`id` = `user_functional_areas`.`functional_area_id`
where `user_functional_areas`.`user_id` in (2, 6, 7)

Но на самом деле 6 и 7 - это идентификатор компании, а не только пользователь, 2 - идентификатор пользователя, один из этих запросов должен использовать business_functional_areas вместо user_functional_areas. Еще одна обнаруженная вещь: в результате все элементы находятся внутри модели App\User, подобные businesses также являются пользовательским объектом.

Ответы [ 3 ]

1 голос
/ 24 июня 2020

Проще говоря, пока вы не сможете достичь этого, используя Eloquent Eager Loading с Unions. Это пока не поддерживается в Laravel. Один из таких сценариев, для которого они закрылись как проблема Non-Fix, - это Union with Eloquent fail ... .

Причина : только при вызове функции UNION первая модель (user) считается основной моделью, а тип модели результирующего набора другой модели (Business), переданный в качестве аргумента, будет преобразован только в основную (USER), и будет вызвано отношение основной модели все записи (не желаемая).

Из-за вышеуказанной проблемы для каждой записи набора результатов вызывается только отношение модели user. Таким образом, даже для business_id = 1 извлекается функциональная область user_id = 1.

Вы можете отладить больше об этом из файла и функции ниже.

File: 
<your_laravel_project>\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php
Function: get

Альтернативный Решение Вы можете получить оба набора результатов как есть, а затем объединить их после получения данных, используя php.

public function usersAndOrganizations()
{
    $users = $this->users()->get();
    $organizations = $this->organizations()->get();
    $invitees =  $users->toBase()->merge($organizations->toBase())->toArray();
    dd($invitees);
}
1 голос
/ 27 июня 2020

На данный момент единственный способ - использовать from map.

public function usersAndOrganizations()
{
    $users = $this->users();

    $organizations = $this->organizations();

    $invitees = $users->union($organizations)->paginate(10);
  
    $invitees = $this->getRelatedData($invitees);

    return response()->json($invitees);
}


private function getRelatedData($invitees)
{
    $invitees->map(function($object) use($functionalAreaName) {
        if($object->type == 'business') {
            $relationName = 'businesses';
            $relationKey = 'business_id';
            $attachableType = Business::MORPHABLE_TYPE;
        }
        if($object->type == 'freelancer') {
            $relationName = 'users';
            $relationKey = 'user_id';
            $attachableType = User::MORPHABLE_TYPE;
        }
        $functionalAreas = FunctionalArea::whereHas($relationName, function($q) use ($object, $relationKey){
            $q->where($relationKey, $object->id);
        })->get([$functionalAreaName.' As name', 'id']);

        $object->functional_areas =  $functionalAreas->toArray();

    });

    return $invitees;
}

И удалить with из ваших функций и вызвать его после того, как вы получите результат с разбивкой на страницы.

0 голосов
/ 17 июня 2020

Вы не можете объединять несовместимые запросы с помощью union. См. Союзы .
Ваш метод users() возвращает красноречивый построитель для User сущности. И organizations() построитель возврата для Business объекта.
Таким образом, выбирать пользователей и организации в одном запросе некорректно.
Правильный запрос такой:

SELECT City FROM Customers
UNION
SELECT City FROM Suppliers
ORDER BY City;
...