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.
Advertisement
Answer
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); }