Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

362
Views
Select specific column from morph relation according to type in Laravel

I am trying to write a query that selects columns from a model then selects some columns from a morph relationship table. But I have no idea to select columns, and relation tables have different columns. So some column has no slug, some have.

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

Models

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

How can I select specific columns from morph relation with checking morph type? I am using Laravel version 8.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

The polymorphic relation is something the Eloquent aware of, and DBMS hasnot implemented this feature in it. so there cannot be a sql query which join a table to another tables based on the morph column.

so you have to use distinct queries for every polymorphic join relation on your models:

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

if you want a common interface to use one of these dynamically. you can use:

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

I am not sure which columns you want to response, but as i can guess from your question, I suppose you want id and slug from both models.

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);
}
over 4 years ago · Santiago Trujillo Report

0

You can perfom separate filters based on the morph-class. This can be achieved with the whereHasMorph (https://laravel.com/docs/8.x/eloquent-relationships#querying-morph-to-relationships).

If you need to automatically resolve the relationship you can use with. This will preload morphables automatically into your resulting collection.

The following example uses an orWhere with 2 separate queries per morphable.

$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')

  ;

An alternative way is to pass both classes to the second argument. In that case you can check the type inside your closure.

$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')

If required you can also preload nested relationships. (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'],
        ]);
    }])
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!