How to use withTrashed when I'm querying using eager loading? - laravel

I have some tables, one of which is a table named Users where there are soft deleted rows.
I have code like:
$appointments = Appointment::with('user')->get();
$return = array();
foreach ($appointments as $key => $appointment) {
$return[] = array(
$appointment->customer_name,
$appointment->date->format('d/m/Y'),
$appointment->time,
$appointment->status,
$appointment->user->full_name,
);
}
Because the row with the user is deleted, I get an error on the line with:
$appointment->user->full_name
because of course there is no match for this user.
I tried adding withTrashed() to the first line, both before and after with('user') but this didn't help.
How do I ensure that this query really does return all appointments with all users, even deleted ones?

withTrashed() can be applied to the query that retrieves the users like so:
$appointments = Appointment::with(array('user' => function($query) {
$query->withTrashed();
}))->get();
You can also apply withTrashed() to both the appointment and their users:
$appointments = Appointment::with(array('user' => function($query) {
$query->withTrashed();
}))->withTrashed()->get();
Alternatively, you can add withTrashed() to the association method to apply it whenever the association is loaded:
public function user() {
return $this->hasOne('User')->withTrashed();
}

Related

Laravel 5 with eloquent relation callback function returning wrong records

Here is User Model
public function userpackages()
{
return $this->hasMany('App\UserPackages');
}
Trying to get users packages form specific month and year but it returning all records.
$users = User::with(['team', 'userpackages' => function($package) use($month,$year) {
$package->whereMonth('created_at', $month)->whereYear('created_at', $year);
}])->get();
Fetching
foreach ($users as $key => $user) {
$userpackages = $user->userpackages;
}
If I'm understanding correctly, you are filtering the eager load, but this does not affect the models returned. You need to repeat the filter using whereHas() to limit the models that are returned. In addition, functions like whereDate() can be very inefficient, especially if the column is indexed. I suggest using whereBetween() when searching a date range.
$date = Carbon::createFromFormat("Y-m", "$year-$month");
$range = [$date->startOfMonth(), $date->endOfMonth()];
$users = User::with('team')
->with(['userpackages' => fn ($q) => $q->whereBetween('created_at', $range)])
->whereHas('userpackages', fn ($q) => $q->whereBetween('created_at', $range)
->get();
To explain further:
User::with('userpackages') returns all users, each with all of their packages.
User::with(['userpackages' => 'some condition']) returns all users, each with some of their packages
User::whereHas('userpackages', 'some condition') returns some users, each with all of their packages
User::(['userpackages' => 'some condition'])->whereHas('userpackages', 'some condition') returns some users, each with some of their packages.

Laravel 8 eager loading, how to access subquery fields

This is working:
$clients = Client::with([
'contacts' => function ($query) {
$query
->select('client_id', 'first_name', 'last_name')
->where('contact_type_id', '=', 1);
}])
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state']);
dd($clients);
However, I'm unsure of how to access first_name and last_name on the subquery. They're showing up in the "relations" object in the dump, but in my mind I'm envisioning a dataset that I would access like,
$client->first_name, etc.
When I try to add the fields to the get() method at the end, it doesn't recognize them, so I'm doing something wrong, or I need to access the subquery fields differently.
When you are eager loading the relationships, you are just preloading them.
If your relationship is defined as hasMany (which seems to be the case here, a Client hasMany Contact), then you'll always get a collection from eager loading.
If you defined another relationship in your model in order to get only one result, for instance:
public function contact()
{
return $this->hasOne(Contact::class)->where('contact_type_id', 1)->latest('id');
}
Your new relationship contact would return only one result:
$clients = Client::with('contact')
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state']);
foreach($clients as $client){
dd($client->contact->first_name);
}
// each Client of $clients would have a ->contact relationship
You should add field id to select() method in subquery and field contact_id to get() method
$clients = Client::with(['contacts' => function ($query) {
$query->select('id', 'client_id', 'first_name', 'last_name')->where('contact_type_id', '=', 1);
}])
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state', 'contact_id']);
foreach ($clients as $key => $client) {
dd($client->contacts->first_name);
// Or
dd($client->contacts()->first()->first_name);
}

Laravel - Get array with relationship

I have an ajax call that returns an array:
$reports = Report::where('submission_id', $submissionID)
->where('status', 'pending')
->get(['description','rule']);
return [
'message' => 'Success.',
'reports' => $reports,
];
From this array, I only want to return the fields 'description' and 'rule'. However I also want to return the owner() relationship from the Report model. How could I do this? Do I have to load the relationship and do some kind of array push, or is there a more elegant solution?
You can use with() to eager load related model
$reports = Report::with('owner')
->where('submission_id', $submissionID)
->where('status', 'pending')
->get(['id','description','rule']);
Note you need to include id in get() from report model to map (owner) related model
you will have probably one to many relationship with Reports and owners table like below
Report Model
public function owner() {
return $this->belongsTo('App\Owner');
}
Owner Model
public function reports() {
return $this->hasMany('App\Report');
}
your controller code
$reports = Report::with('owner')->
where('submission_id', $submissionID)->where('status', 'pending')->get()
return [
'message' => 'Success.',
'reports' => $reports,
];
This is what I ended up going with:
$reports = Report::
with(['owner' => function($q)
{
$q->select('username', 'id');
}])
->where('submission_id', $submissionID)
->where('status', 'pending')
->select('description', 'rule','created_by')
->get();
The other answers were right, I needed to load in the ID of the user. But I had to use a function for it to work.

UpdateExistingPivot for multiple ids

In order to update single record in pivot table I use updateExistingPivot method. However it takes $id as the first argument. For example:
$step->contacts()->updateExistingPivot($id, [
'completed' => true,
'run_at' => \Carbon\Carbon::now()->toDateTimeString()
]);
But how can I update multiple existing rows in pivot table at once?
There's an allRelatedIds() method in the BelongsToMany relation that you can access, which will return a Collection of the related model's ids that appear in the pivot table against the initial model.
Then a foreach will do the job:
$ids = $step->contacts()->allRelatedIds();
foreach ($ids as $id){
$step->contacts()->updateExistingPivot($id, ['completed' => true]);
}
You can update only by using a looping statement as there updateExistingPivot function only accept one dimensional params, See the core function for laravel 5.3.
File: yoursite\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Relations\BelongsToMany.php
Function: updateExistingPivot
public function updateExistingPivot($id, array $attributes, $touch = true)
{
if (in_array($this->updatedAt(), $this->pivotColumns)) {
$attributes = $this->setTimestampsOnAttach($attributes, true);
}
$updated = $this->newPivotStatementForId($id)->update($attributes);
if ($touch) {
$this->touchIfTouching();
}
return $updated;
}
So, You should follow the simple process:
$step = Step::find($stepId);
foreach(yourDataList as $youData){
$step->contacts()->updateExistingPivot($youData->contract_id, [
'completed' => true,
'run_at' => \Carbon\Carbon::now()->toDateTimeString()
]);
}

Can't return object with belongsToMany relationship attached

I'm trying to return a list of $users with an array of roles.
Here's my controller:
$users = DB::table('users')->take(5)->skip(2)->get();
foreach ($users as $user)
{
$user = User::with('roles')->find($user->id);
}
return Response::json(array(
'users' => $users
));
And here's the model relationship:
public function roles()
{
return $this->belongsToMany('Role')->withTimestamps();
}
But this just returns the users without the role attached to it. However, if I do this:
return Response::json(array(
'users' => User::with('roles')->get()
));
I get the full list with the roles attached to each user. So, what am I doing wrong?
Oddly, if I do this:
return Response::json(array(
'users' => User::with('roles')->find($user->id)
));
Then it returns that user with the roles as I expected, so why not return it in the foreach statement?
You are loosing the whole Eloquent relations functionality when using DB queries.
You could just
$users = User::with('roles')->take(5)->skip(2)->get();
A good practice is to not mix Eloquent model "style" queries (User::...->get()) with direct DB queries (DB::table('user')...->get).
The foreach uses pass by value. That means you are not changing the actual array item but just a copy of the value. To change that and get the item passed by reference add a &:
foreach ($users as &$user) {
$user = User::with('roles')->find($user->id);
}
However why don't you just do it this way?
$users = User::with('roles')->take(5)->skip(2)->get();
return Response::json(array(
'users' => $users
));

Resources