Laravel eloquent without() in nested relationship - laravel

I am trying to exclude a few relations from my API call.
Right now I'm returning the records as following:
return response()->json(Ticket::where('status_id', '!=', 5)->get());
In the ticket model I have a belongsTo relation with customer.
In the customer model I have a hasOne relation with Address.
Now, I have included the address in the customer model, and the customer in the ticket model usin $with.
I want to get the customer with the ticket, but not the address. I tried using the without function as followed: Ticket::where('status_id', '!=', 5)->without(['customer.address'])->get().
This is not working, however the following does work: Ticket::where('status_id', '!=', 5)->without(['customer'])->get() I do get the ticket without any relations.
I assumed the first function would work, considering this also works in the with() function, but it does not.

To remove the relation address while keeping the address relation in the $with attribute of the customer class, use this:
Ticket::where('status_id', '!=', 5)->with(['customer' => function($customer){
$cutomer->without(['address']);
}])->get()
But I suggest you remove the address from the $with attribute or you will end up with more without(['address']) than with('address') if you remove it.

It should work provided there's no typo in the relation name (like address instead of addresses).
Another approach - try the following
return response()->json(
Ticket::with('customer', fn($query) => $query->without('address'))
->where('status_id', '<>' 5)
->get()
);
I think even this should work (without typos in relation name)
return response()->json(Ticket::without('customer.address')->where('status_id', '<>' 5)->get());
This should remove/unset address relationship from customer

Related

Laravel eloquet check non existing many to many relation

I'm having trouble to write query in laravel eloquent ORM.
I have a proyect table, where you can assign users, in a many to many relationship
In the view to asign users, I have a selector, but I want to show only the users not already assigned to the proyect and checking also that the user belongs to the company that created the proyect (user.company_id=proyect_id)
In a normal query should me something like this, having $company_id and $proyect_id from the controller.
select * from users u left join proyect_user pu on u.id=pu.user_id and
pu.proyect_id = $proyect_id where u.company_id=$company_i and
proyect_id is null;
The query works, but I would like to use Eloquent. ¿Any idea how to do it?
It depends on how you declared the relationship in the User model. But I would do something like this:
$users = User::whereHas('company', function ($query) use ($companyId) {
$query->where('id', $companyId)
})->whereDoesntHave('proyects', function ($query) use ($proyectId) {
$query->where('id', $proyectId);
})->get();

Laravel: With and whereHas to filter second relation hasOne

i'm trying to filter the table using "with" and "whereHas" for the relation and have it follow a second second relation.
Is it possible to do it with "with" or would it only be possible with "Joins"?
Ticket >> StatusHistory (Last record) >> StatusName = 'new'
ticket
-id
-name
status_history
- ticket_id
- status_name_id
- timestamps
status_names
- id
- name (new, close, paused)
<?
class Ticket extends Model
{
public function latestStatus()
{
return $this->hasOne(StatusHistory::class, 'ticket_id', 'id')->latest();
}
class StatusHistory extends Model
{
public function statusName()
{
return $this->hasOne(StatusName::class, 'id', 'status_name_id');
}
This usually works well if there is only one Status history record, but if there are more, it returns values that should not be there.
example: ticket_id 1 has in history first status new and them status paused
With this sentence he returned the ticket to me even so he no longer has the last status in "new".
Ticket::with('latestStatus')
->whereHas('latestStatus.statusName', function($q){
$q->where('name', 'new');
})
According to the documentation (https://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads) it is possible. It would look like this:
Ticket::with(['latestStatus' => function($q){
$q->where('name', 'new');
}])->get();
So that the subquery is linked to the relation you are trying to load
To access the first relationship you just use:
$ticket = Ticket::find($id);
$ticket->latestStatus
By having a "hasOne" relationship established, this will return the related record, which from what I see also has a hasOne relationship, so you can do the following:
$ticket->latestStatus->statusName
In this way, you are accessing the second relationship and working it as usual.
However, this is not the only way, as Laravel also offers access to chained relationships through the "has-one-through" method, which according to the documentation is defined as:
"...this relationship indicates that the declaring model can be matched with one instance of another model by proceeding through a third model."
class Ticket extends Model{
public function statusName()
{
return $this->hasOneThrough(StatusName::class, StatusHistory::class);
}
}
Take into account that for this you must follow the conventions established by Laravel. I leave here the related links, I am sure they will be very helpful. Greetings.
Relationships: one-to-one
Relationships: has-one-through

how to get list of users who are not under belongsToMany relation under a table in laravel?

Consider the following case.
we have the Users table and Tasks table. they are in relation with belongsToMany with table task_user.
How to get the list of all users who are not under any task? i.e. their user_id is not at all under that given task or even in the task_user table.
why I need this is because like this we can only provide a list of users who are yet to be assigned a task. the task will be assigned to users and not a single user at a time.
Editing_____________
also how to filter with users based on group table? below is not working
$users = Group::with(['subscribers' => function ($q){
$q->doesntHave("tasks");
}])->whereId($gid)->latest()->get();
Assuming you've named your relationships properly, you should be able to use doesntHave("tasks"):
$tasklessUsers = User::doesntHave("tasks")->get();
doesntHave() checks for the non-existence of the supplied relationship ("tasks", in this case) and returns all objects that pass this check.
If your function name is different, use that, but the relationship should be:
User.php:
public function tasks(){
return $this->belongsToMany(Task::class, "task_user");
}
Edit: doesntHave() is the simple version, whereDoesntHave() allows a custom query. See https://laravel.com/docs/5.8/eloquent-relationships#querying-relationship-absence for full details.
Second Edit:
As stated in the comments below, with() will not filter the Model it is being called on, so this query won't work as you'd expect:
$users = Group::with(['subscribers' => function ($q){
$q->doesntHave("tasks");
}])->whereId($gid)->latest()->get();
To fix this, use a chained doesntHave() query:
$query = Group::doesntHave('subscribers.tasks')
->where('id', '=', $gid)
->latest()
->first();
// OR
$query = Group::whereHas('subscribers', function($subQuery){
$subQuery->doesntHave('tasks');
})->where('id', '=', $gid)
->latest()
->first();
$users = $query->subscribers; // Return `users` (aliased to `subscribers`)
Either approach will check the existence of subscribers that don't have any associated tasks relationship, and also only return where id is $gid.
Note: Used first() for the queries, as using id in a query should only ever return a single Group record, and get() is for returning multiple records in a Collection

Laravel: Limit columns retrieve in an eagerly loaded relationship, with constraints

When I eagerly load a straightforward Eloquent relationship I can limit the columns retrieved using the following syntax:
MyModel::with(myRelation:id,col_2,col_3);
When I want to constrain the members of the relationship, I do this:
MyModel::with(['myRelation' => function ($query) {
$query->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}]);
But that loads all columns of the related models. I tried replacing the key myRelation with the full syntax, myRelation:id,col_2,col_3, but it throws an error which says the relation name is not found.
I also tried adding the following methods to the $query:
->select('id', 'col_2', 'col_3')
or
->addSelect('id', 'col_2', 'col_3')
or
->get('id', 'col_2', 'col_3')
None of these were successful.
One option could be keeping all the columns (except id, col_2, col_3) hidden in your original model MyRelation is pointing to.
protected $hidden = ['col_4', 'col_5',...];
You could also change the relationship definition in your model MyModel:
public function myRelation()
{
return $this->belongsTo('MyRelation')->select(array('id', 'col_2', 'col_3'));
}
But, in general, this should work:
MyModel::with(array('myRelation'=>function($query){
$query->select('id','col_2', 'col_3)
->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}))->get();
Remember, you have to include the joining key (say id) in the select list above.
Ok, so to get this to work, I had to:
include the foreign_key in the list of selects (thanks, #ankitPatel), and
pass the parameters to the select statement as an array, as follows:
->select(['id','foreign_key','col3','col4'])

Form Model Binding with relationship

I already looked at this post but I can't seem to make it right:
Laravel form model binding
I get this error:
https://gyazo.com/2ea7b7bb6a19d588829447ee1a92053e I use laravel 5.2
for this.
some screenshots:
https://gyazo.com/1b2c35e660dfe1aae69a02703733d083
https://gyazo.com/3d6f294473f6e54650a4a4403dc2777e
https://gyazo.com/a59aebc7362f51f9ac27852ea032f962
To expand on my comment:
Your problem is here:
$user = User::where('id', $id) // Here more specifically, Laravel does not know if you mean id of users table or details table
->leftJoin('user_details', user_details.user_id', '=', 'users.id')
->first();
Rewrite your where statement like this:
$user = User::where('users.id', $id)....
And it should work. Basically since you're joining 2 tables and they both got id you need to specify which id you want to query by.

Resources