工会中的Laravel关系冲突

Posted

技术标签:

【中文标题】工会中的Laravel关系冲突【英文标题】:Laravel relationship conflicts in union 【发布时间】:2020-10-07 04:47:05 【问题描述】:

我有以下型号: 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();

现在我应该将所有企业和用户显示在一个列表中,为此我从联合中使用,以下是我的查询:

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 是企业 ID 而不是用户只有 2 是用户 ID,其中一个查询应该使用 business_functional_areas 而不是 user_functional_areas。 发现的另一件事是,结果中的所有项目都在App\User 模型中,它像businesses 也是作为用户对象。

【问题讨论】:

你在 :private function users 中有错字)应该是 users() 能否将最终查询的结果用->toSql( ) 输出并分享? 我怀疑DB::raw("CONCAT(first_name, ' ', last_name) AS name"),DB::raw("functional_areas.name_en AS name"), 存在冲突。为两者使用不同的name 别名 我认为你不能在 laravel 中混合使用 UNION 和急​​切加载 (with)。执行 UNION 查询后,laravel 不知道 id 来自哪个表。恐怕您将不得不实现自己的急切加载逻辑或使用延迟加载(这不会是 10 条记录的世界末日)。 @jones,如果您能与我们共享数据库模式,那就太好了。等待你的答复。您可以创建一个fiddle,并附上一些数据以便快速了解。 【参考方案1】:

简而言之,目前您无法使用Eloquent Eager LoadingUnions 来实现它。 Laravel 尚不支持此功能。他们作为Non-Fix 问题关闭的此类场景之一是Union with Eloquent fail...。

原因:在调用UNION 函数时,只有第一个模型(user)被认为是主模型,其他模型的结果集的模型类型(Business)作为参数传递将是仅转换为主要的(USER),并在所有记录(不是所需的记录)上调用主要模型关系。

由于上述问题,仅在结果集的每条记录上调用user 模型的关系。所以即使business_id = 1,user_id =1的functional_area也会被获取。

您可以从下面的文件和函数中调试更多信息。

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);

【讨论】:

@jones,你能试试吗。它对你有用吗?此处也不考虑分页。 您的解决方案似乎可行,但分页问题如何,我想获取混合organizationsusers的10条记录。【参考方案2】:

目前唯一的方法是使用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,并在获得分页结果后调用它。

【讨论】:

【参考方案3】:

您不能使用联合连接不兼容的查询。 见Unions。 您的 users() 方法返回 User 实体的 eloquent builder。 并且organizations()Business 实体返回构建器。 因此,在一次查询中选择用户和组织是不正确的。 正确的查询是这样的:

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

【讨论】:

以上是关于工会中的Laravel关系冲突的主要内容,如果未能解决你的问题,请参考以下文章

laravel中的路由冲突

在 Laravel 中迁移外键与雄辩的关系

从 Laravel 中的关系中检索关系

Laravel 中的 BelongsTo 关系

Laravel 中的关系 0...1

Laravel:模型中的动态关系