QuentinWatt's avatar

A very challenging HasManyThrough Morphed relationship.

Hey everyone,

What I am trying to do is: Get all Categories that belong to an Author, through their Posts.

Easy

The Author has posts.

Easy

Posts have categories that are categorizable (existing in multiple categories).

This is the difficult (undocumented) part

Author has Categories through Posts, Categories.

Following Laravel's docs I can create a post that is 'categorizable' with a morphed relationship.

// Post Model
/**
* Get all of the categories for the post.
*/
public function categories(): MorphToMany
{
    return $this->morphToMany(Category::class,  'categorizable');
}

Then straight out of the docs, I can do the inverse relationship as well.

// Categories Model
/**
 * Get all of the posts for the category.
 */
public function posts(): MorphToMany
{
   return $this->morphedByMany(Post::class, 'categorizable');
}

The challenge is how to set up the relationship from the Author to the categories:

// Author Model
/**
 * Defines the relationship to posts
 *
 * @return HasMany
 */
public function posts(): HasMany
{
   return $this->hasMany(Post::class, 'author_id', 'id');
}

/**
 * Defines the relationship to categories through posts
 *
 * @return // what relationship?
 */
public function categories() 
{
    // what relationship and how?
}
````
0 likes
1 reply
QuentinWatt's avatar
QuentinWatt
OP
Best Answer
Level 3

Turns out this solves my problem.

public function __invoke(ShowCategoriesRequest $request)
{
  $categories = Category::query();

  if ($request->input('author')) {
    $author = Author::where('slug', $request->input('author'))->first();
    $authorCategories = DB::table('posts')
       ->where('author_id', $author->id)
       ->join('categorizables', 'posts.id', 'categorizables.categorizable_id')
       ->where('categorizables.categorizable_type', 'App\Models\Post')
       ->join('categories', 'categories.id', 'categorizables.category_id')
       ->select('categories.slug')
       ->distinct()
       ->get()
       ->pluck('slug')
       ->toArray();

    $categories = $categories->whereIn('slug', $authorCategories);
  }

  return $categories->get()
}

Please or to participate in this conversation.