Select 根据 Laravel 中的类型变形关系中的特定列

Select specific column from morph relation according to type in Laravel

我正在尝试编写一个查询,其中 select 来自模型的列,然后 select 来自变形关系 table 的一些列。但我不知道 select 列,并且关系 table 有不同的列。所以有些列没有 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');
    }
}

如何 select 变形关系中的特定列检查变形类型?我正在使用 Laravel 版本 8.

多态关系是 Eloquent 知道的,而 DBMS 尚未在其中实现此功能。 所以不能一个sql查询将一个table连接到另一个tables基于变形列。

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

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

您可以根据 morph-class 执行单独的过滤器。这可以通过 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')

  ;

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

$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'],
        ]);
    }])