Laravel HasManyThrough relationship while including Trashed items - php

I want to include soft deleted items with HasManyThrough relationship,
I tried adding ->withTrashed(); to it, but then I got this error:
BadMethodCallException Call to undefined method
Illuminate\Database\Eloquent\Relations\HasManyThrough::withTrashed()
Is it possible to force Laravel skip that where table.deleted_at is null in a relationship?

This is an old question, but responding for anyone that has the same issue.
if your are using HasManyThrough and want to fetch also the soft deleted records. there is a scope in the HasManyThrough relationship that can include them.
You should use it like follow :
$yourModel->yourHasManyThroughRelation()->withTrashedParents()->withTrashed();

Should work the way you are trying.
I made an example to make it more clear to understand:
Group::with('users')->get();
And in the Group model:
public function users()
{
return $this->hasManyThrough(User::class, UserGroup::class, 'group_id', 'id', 'id', 'user_id')->withTrashed();
}

Related

Laravel 8 - How to set a different relation name for scoping in Nested Resource Route

Routes:
I have a nested resource route. I use this to declare it:
Route::resource('orders.comments', \App\Http\Controllers\Backend\OrderCommentController::class)
->parameters([
'comments' => 'orderComment:id'
])
->except(['show']);
Models & Relations:
I have two models, Order and OrderComment.
Order model:
public function comments()
{
return $this->hasMany(OrderComment::class);
}
OrderComment model:
public function order()
{
return $this->belongsTo(Order::class);
}
OrderComment Controller edit method signature
public function edit(Order $order, OrderComment $orderComment)
The actual problem
Whenever I want to edit an order comment, by visiting /orders/1/comments/1/edit, I get the error:
Call to undefined method App\Models\Order::orderComments()
I assume this is based on the parameter I set in the resource. But I need the parameter to be orderComment and not comment, due to the $orderComment argument in OrderCommentController#edit. This is mainly due to the name convention.
Possible, ruled out solutions i tried/considered:
Renaming the OrderComment model to Comment. This is not possible because there are other Comment models, with different columns, so it cannot be polymorphic too.
Renaming the comments relation to orderComments. This is a solution, but not a preferred one. This is because $order->comments looks more elegant than $order->orderComments, etc
Conclusion/question
So is it possible to 'tell' Laravel it should look for a relation called comments instead of orderComments?

How can I hide a relation on an Eloquent model

I'm struggling with an Eloquent dummy issue. I have a model User which has a many to many relation with a model Like.
I'm getting my user model by its id with:
$likedUser = User::findorfail($user_id);
At some point in the code, I'm doing:
if ($likedUser->likes->where(...)->count()) {
...
}
Then, I want to return my $likedUser WITHOUT the likes (added in the IF condition above by Laravel). How can I do that? If I do that, it does not work:
return response()->json([
'is_matched' => $isMatched,
'liked_user' => $likedUser,
], 200);
I would love to have a $likedUser->without('likes') to remove the relation added by Laravel. I tried to $outputUser = $likedUser->replicate() but the unique field of my models disappears..
1) What's the elegant way of only returning my User model without its relationship?
2) Why is Laravel adding the relation even if I never set my variable but only use it in a IF?
Thanks a lot for your time guys!

Laravel Eager Loading Relation With Additional ->where()

This is my relation method in my model:
public function relation()
{
return $this->belongsTo('App\Table', 'table_2_id', 'table_2_id')->where('table_1_id', $this->table_1_id);
}
So, both the model above and Table2 have the same column, but there is the possibility of many entries, so I want to then filter on a second column that is shared by both tables, table_1_id.
It seems to work perfectly on the command line, but when eager loading the relationship is empty.
If I remove the additional ->where() from the relationship method then the eager loading works.
The way I am eager-loading is by doing
->with('relation.nestedRelation');
I've also tried
->with(['relation' => function ($q) {
$q->with('nestedRelation');
}])
I've just tried adding it as an attribute to see if that helped but still no joy.
You can use the whereColumn() function :
->whereColumn('table_1.table_1_id', 'table_2.table_1_id')
Or you can use that :
public function filterRelation(){
return $this->relation->where('table_1_id', $this->table_1_id);
}

Eager load ONE from Many to Many relationship

I have a many to many relationship for users and roles. A user can have multiple roles, but I only want with to grab the FIRST role.
Consider the following code:
User::with('roles')->get()
Works great for all roles, but I only want the first role.
I've set this up in my model but doesn't work:
public function role()
{
return $this->roles()->first();
}
How do I load with for only the first result?
You should be able to call first directly on the eager loaded relationship like this:
User::with(['roles' => function ($query) {
$query->first();
})->get();
first() actually executes the query and returns the results as a collection. Relationships must return a query builder, which can then be chained or executed, so using first() in a relationship won't work.
UPDATE
I realised you want to use role in with, so you need to create a relationship to do that. Create a new relationship on your User model (you can use any limit described in the docs, not just oldest()):
public function role()
{
return $this->hasOne('App\Role')->oldest();
}
And then you can use it in with:
$users = User::with('role')->get();

Eloquent - Call to undefined relationship

I have a users table and a permissions table. It's a many-to-many relationship so I also have a users_permissions table with a user_id & module_permission_id column.
The user model has the following relationship:
public function permissions()
{
return $this->belongsToMany(Permission::class, 'users_permissions', 'user_id', 'module_permission_id');
}
When I run my query, the result contains an empty permissions array.
User::with('permissions');
If I echo the query in the with, I get the error: Call to undefined relationship [] on model [App\Models\User]
User::with(['permissions', function($q) {
echo $q->toSql();
die();
}]);
The rest of the query works, it's just trying to get permissions which is failing.
In my case it was a coding convention issue related to camelCase vs. snake_case:
In the Model there was
public function getPermissions()
and in the query there was
User::with(['get_permissions'])
When I changed this to
User::with(['getPermissions'])
it started to work, although I'd say the Laravel way would be to use snake_case instead.
This confused me for a couple of days since frameworks like Symfony and AngularJs has a mixed conventions that somewhere you need to write parameters in snake_case and somewhere else in camelCase. I didn't find any documentation on Laravel site how they handle this, so I tried it the Straight Way and it seemed to be the case here :)
Maybe you just forgot the ->get() after User::with('permissions')->get() ?
https://laravel.com/docs/5.0/eloquent#eager-loading
Slapping this here for anyone who may be trying to refactor from an eager load that selects columns to an eager load with a scoped query.
You HAVE to move the selecting of columns into the scoped query, trying to select columns with the usual colon notation will fail and throw the undefined relationship error.
For example going from this eager load that selects a pivot table column, and a column from the permissions table User:with('permissions:permission_tag:tag_set_id,permission_name') to a scoped query that selects the same columns but also orders the results looks like this
User::with([
'permissions' => function ($query) {
$query->select('permission_tag:tag_set_id', 'permission_name');
$query->orderBy('permission_name');
},
]);
Notice I pulled out the : notation and it lives right in the scoped query.

Categories