Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Laravel Eloquents HasManyThrough relation with multiple relations through polymorphism

I got a rather simple application where a user can report other users comments and recipes. I use a polymorphic relation to store the reports. Which works fine; however, I am now trying to get the offences that a user has made.

Getting users reports is not a problem, this can be done directly using user->reports() but I would very much like to get the reports in which other people has reported said user. I can get this to work using either the hasManyThrough relation or queries BUT only on one model at a time.

ex.

public function offenses() {
    return $this->hasManyThrough('Recipe', 'Reports');
}

Or

->with('user.recipe.reports')

The problem is that my reportable object is not just recipes, it could be comments, images, etc. So instead of having to use multiple functions, the logical way would be to parse the relationship between hasManyThrough various parameters somehow.

Theoretically looking like this:

public function offenses() {
    return $this->hasManyThrough(['Recipe', 'RecipeComments'], 'Reports');
}

Is this in any way possible? With some undocumented syntax? If not is there any clever workarounds/hacks?

Possible solution?

Would an acceptable solution be to add another column on my report table and only add offender_id like this?

ID | User_id | Offender_id | Reportable_type | Reportable_id

That would mean I could just make a relation on my user model that connects offences through that column. But would this be considered redundant? Since I already have the offender through the reportable model?


Models

Polymorphic Model

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

    public function User() {
        return $this->belongsTo('App\User');
    }
}

Recipe Model

class Recipe extends Model {
    public function user() {
        return $this->belongsTo('App\User');
    }

    public function reports() {
        return $this->morphMany('App\Report', 'reportable');
    }
}

Comment Model

class RecipeComment extends Model {   
    public function user() {
        return $this->belongsTo('App\User');
    }

    public function reports() {
        return $this->morphMany('App\Report', 'reportable');
    }
}
like image 476
Nicklas Kevin Frank Avatar asked Apr 23 '15 13:04

Nicklas Kevin Frank


1 Answers

Using your current model, you can receive all the reported models for a user by the following code:

$recipeCommentReport = RecipeComment::whereHas('reports',function($q){
   return $q->where('user_id','=',Auth::user()->id) 
});

$recipeReport = Recipe::whereHas('reports',function($q){
   return $q->where('user_id','=',Auth::user()->id) 
});

//Get all reports into one
$reports = $recipeReport->merge([$recipeCommentReport]);

This is messy at best, because:

  1. We can't sort the results, given that we are using two separate db queries.
  2. If you have other models who have a report relationship, just imagine the chaos.

The best solution, is as you have figured out above:

Add an offender_id column to your report table.

It is cleaner and follows the DRY principle.

Typical Case Scenarios

Get all Recipe Comment reports for User

Report::where('offender_id','=',Auth::check()->id)
->where('reportable_type','=','RecipeComment')
->get();

Count offense by type for User

Report::where('offender_id','=',Auth::check()->id)
->grouBy('reportable_type')
->select(Db::raw('count(*)'),'reportable_type')
->get();
like image 105
Mysteryos Avatar answered Oct 22 '22 11:10

Mysteryos