【问题标题】:Select specific column from morph relation according to type in Laravel根据 Laravel 中的类型从变形关系中选择特定列
【发布时间】:2021-10-06 12:23:00
【问题描述】:

我正在尝试编写一个查询,该查询从模型中选择列,然后从变形关系表中选择一些列。但我不知道选择列,关系表有不同的列。所以有些列没有 slug,有些则有。

public function index()
{
    $menus = Menu::whereActive(true)
        ->with([
            'menuable' => function ($q) {
                // This gives error if  there is no relation Pages model
                $q->whereActive(true)->select('pages.id', 'pages.slug');

                // Below not working
                // if($q->type === Page::class){
                //    $q->whereActive(true)->select('pages.id', 'pages.slug');
                // } else if($q->type === Category::class){
                //     $q->whereActive(true)->select('categories.id', 
                           'categories.slug');
                // }
            }
        ])
        ->get(['id', 'menuable_id', 'menuable_type', 'name']);

    $response = [
        'menus' => $menus,
    ];

    return $this->sendResponse($response);
}

模型

class Menu extends Model
{
    public function menuable()
    {
        return $this->morphTo();
    }
}

class Page extends Model
{
    public function menu()
    {
        return $this->morphOne(Menu::class, 'menuable');
    }
}

class Category extends Model
{
    public function menu()
    {
        return $this->morphOne(Menu::class, 'menuable');
    }
}

如何通过检查变形类型从变形关系中选择特定列?我正在使用 Laravel 版本 8。

【问题讨论】:

    标签: php laravel eloquent relationship laravel-8


    【解决方案1】:

    多态关系是 Eloquent 知道的,而 DBMS 并没有在其中实现这个特性。 所以不可能基于 morph 列将一个表连接到另一个 的 sql 查询。

    因此您必须对模型上的每个多态连接关系使用不同的查询:

    //you can retrieve distinct menu based on their relation 
    Menu::whereActive(true)->hasPage()->with('pages');
    
    //and having the ralations in the menu model:
    public function posts
    Menu::whereActive(true)->hasCategory();
    
    //scope in menu class can be like:
    public function scopePage($query){
        return $query->where('menuable_type',Page::class);
    }
    public function scopeCategory($query){
        return $query->where('menuable_type',Category::class);
    }
    
    //with these you can eager load the models
    Menu::whereActive(true)->hasPage()->with('page');
    Menu::whereActive(true)->hasCategory()->with('category');
    
    public function page(){
        return $this->belongsTo(Page::class);
    }
    public functioncategory(){
        return $this->belongsTo(Category::class);
    }
    

    如果您想要一个通用接口来动态使用其中之一。 你可以使用:

    $menu->menuable->id
    $menu->menuable->slug
    

    我不确定您要回复哪些列,但我可以从您的问题中猜到,我想您需要两个模型中的 idslug

    public function index(){
        $pagedMenu = Menu::whereActive(true)->hasPage()->with('page');
        $categoriedMenu = Menu::whereActive(true)->hasCategory()->with('category');
    
        $menues = $pagedMenu->merge($categoriedMenu);
        
        $response = [
            'menus' => $menus,
        ];
    
        return $this->sendResponse($response);
    }
    

    【讨论】:

    • 这对我没有帮助
    • 你到底想要什么?你有什么要求?
    • 你能告诉我索引函数的完整代码吗?
    • 这对您有帮助吗?
    • 谢谢你,这对我很有帮助
    【解决方案2】:

    您可以根据变形类执行单独的过滤器。这可以通过whereHasMorph (https://laravel.com/docs/8.x/eloquent-relationships#querying-morph-to-relationships) 来实现。

    如果您需要自动解决关系,您可以使用with。这会自动将变形预加载到您的结果集合中。

    以下示例使用 orWhere,每个可变形对象有 2 个单独的查询。

    $menus = Menu::whereActive(true)
    
        ->whereHasMorph('menuable', [Page::class], function (Builder $query) {
            // perform any query based on page-related entries
        })
    
        ->orWhereHasMorph('menuable', [Category::class], function (Builder $query) {
            // perform any query based on category-related entries
        })
    
        ->with('menuable')
    
      ;
    

    另一种方法是将两个类都传递给第二个参数。在这种情况下,您可以检查闭包内的类型。

    $menus = Menu::whereActive(true)
    
        ->whereHasMorph('menuable', [Page::class, Category::class], function (Builder $query, $type) {
    
            // perform any query independently of the morph-target
            // $q->where...
    
            if ($type === (new Page)->getMorphClass()) {
                // perform any query based on category-related entries
            }
    
            if ($type === (new Category)->getMorphClass()) {
                // perform any query based on category-related entries
            }
        })
    
        ->with('menuable')
    
    

    如果需要,您还可以预加载嵌套关系。 (https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading-morphto-relationships)

    $menus = Menu::whereActive(true)
    
        ->with(['menuable' => function (MorphTo $morphTo) {
            $morphTo->morphWith([
                Page::class => ['calendar'],
                Category::class => ['tags'],
            ]);
        }])
    

    【讨论】:

    • 这只有助于过滤数据。我需要与关系(在响应中显示相关模型),并从关系中只选择特定的列。
    • @Benfactor 在这种情况下,您可以使用with。我已经更新了我的答案。另外提供了一个示例,您可以如何根据每个可变形加载关系。这没有被问到,但可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 2019-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    • 2018-07-25
    • 1970-01-01
    相关资源
    最近更新 更多