I'm kinda stuck with this here and don't know how to move forward with this one.
I have two Models: user and child and they are in a Relationship.
( Keep in mind that this only illustrate the problem )
class Child extends Model{
protected $primaryKey = 'id_child';
public $appends = ['is_alive'];
public function user(){
return $this->belongsTo('Models\User','id_user');
}
public function getIsAliveAttribute(){
if (!is_null($this->lifetime_updated_at))
return (Carbon::parse($this->lifetime_updated_at)->addMinute($this->lifetime) >= Carbon::now());
else
return false;
}
}
class User extends Model{
protected $primaryKey = 'id_user';
public $appends = ['is_alive'];
public function childs(){
return $this->hasMany('Models\Child','id_user');
}
public function getIsAliveAttribute(){
if (!is_null($this->lifetime_updated_at))
return (Carbon::parse($this->lifetime_updated_at)->addMinute($this->lifetime) >= Carbon::now());
else
return false;
}
}
Now I want to use Eager Loading in the Controller to retrieve my Childs data from User.
But my User Model Object comes from an MiddleWare in my Application. So I only have the User Model Object to use and I don't want to Query the User again using "with()".
$user = User::where('name','DoctorWho')->first();
return user->childs()->find(3);
What this operation returns:
{
"id_child": 3,
"name": "JayZ",
"last_name": "Etc",
"lifetime": 1,
"lifetime_updated_at": null,
"created_at": "2017-05-29 21:40:02",
"updated_at": "2017-05-29 21:40:02",
"active": 1
}
What I needed ( With Attribute Appended)
{
"id_child": 3,
"name": "JayZ",
"last_name": "Etc",
"lifetime": 1,
"lifetime_updated_at": null,
"created_at": "2017-05-29 21:40:02",
"updated_at": "2017-05-29 21:40:02",
"active": 1,
"is_alive": true
}
Is even possible to retrieve Child Data with Appended Attributes using Eager Loading ?
Notes: This user object come from an Middleware, I must use the User Model Object to get its child's with the appended attribute.
Thanks in Advance,
LosLobos
The thing is I was doing something wrong that is not related to Eloquent acessing the model like that does come with the appended attributes!
What you're doing here is not eager loading. Your childs relationship is wrong. Should be return $this->hasMany('Models\Child', 'id_user'); based on the model and the other relationship defined.
Here are some ways you can access the child information. These by default should respect the $appends property and load the field.
$childId = 3;
$user = User::with('childs')
->where('name', 'DoctorWho')
->first();
return $user->childs()->where('id_child', $childId)->first();
// Or
$user = User::with(['childs' => function ($query) use ($childId) {
$query->where('id_child', $childId);
}])
->where('name', 'DoctorWho')
->first();
return $user->childs->first();
// Or
$child = Child::whereHas('user', function ($query) {
$query->where('name', 'DoctorWho');
})
->find(3);
return $child;
Edit :
If you already have the user model then you can do this.
$child = Child::where('id_user', $user->id_user)
->where('id_child', 3)
->first();
return $child;
Related
I'm returing a model with all the related models.
My problem is that perhaps some models don't have records so they are returned in my $property variable as empty and I have to evaluate their existence in blade (not done yet)
Is there any way in controller or parent model to return only the child relationships which have records? Or any blade directive to evaluate these cases?
All relationships are 1:M.
Regards!
Controller Code
class PropertyController extends Controller
{
public function details($id)
{
$property = Property::with('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
Sir u have the same result with this code?? (just trying to help, im new in this world)
return view('pages.processes.offerdemand.matchs.propertymodal', compact(array('property')));
class PropertyController extends Controller
{
public function details($id)
{
$relations = ['attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services']
$property = Property::with($relations)->whereHas($relations)
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
SOLUTION
I changed code as follows and recieved the expected result:
public function details($id)
{
$property = Property::with(['attributes' => function ($builder) {
$builder->where('pa_value', '!=', '');
}])
->with('addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->withCount('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->first();
dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
I have a list with gamers and another table with game stats.
My list code is:
$gamers = Gamer::with(['lastGameStat' => function($query) {
$query->orderBy('total_points', 'DESC');
}])->paginate(20);
relation:
public function lastGameStat() {
return $this->hasOne(GameStat::class, 'gamer_id', 'id')->orderBy('created_at', 'DESC');
}
in relation table I have field: total_points and with this code I thought it's possible to sort list of gamers by total_points $query->orderBy('total_points', 'DESC');
It doesn't work, can somebody give me an advice here how can I sort the result on a field from relation table?
I guess you'll need either another relation or custom scopes to fetch various game stats of a gamer.
Second relation
Gamer.php (your model)
class Gamer
{
public function bestGameStat()
{
return $this
->hasOne(GameStat::class)
->orderBy('total_points', 'DESC');
}
}
Custom scopes
Gamer.php
class Gamer
{
public function gameStat()
{
return $this->hasOne(GameStat::class);
}
}
GameStat.php
use Illuminate\Database\Eloquent\Builder;
class GameStat
{
public function scopeBest(Builder $query)
{
return $query->orderBy('total_points', 'DESC');
}
}
In your controller:
$gamersWithTheirLatestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->latest();
}])->paginate(20);
$gamersWithTheirBestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->best();
}])->paginate(20);
Be aware as this is untested code and might not work.
I have this Accessor bellow which returns one string result (example: 'Ready' or 'Ongoing' ...):
public function getMinimumStatusAttribute()
{
$statusIds = [];
$tasks = $this->tasks()->get();
foreach ($tasks as $task) {
array_push($statusIds, $task->task_status_id);
}
return taskStatus::orderBy('order', 'asc')
->where('operational_status', '=', true)
->whereIn('id', $statusIds)->value('name');
}
I have TeamleaderDeal model :
return TeamleaderDeal::all();
which returns:
[
{
"id": 4,
"teamleader_id": "8b03da5a-af1f-051d-ad6d-b6199c7f7c35",
"created_at": "2019-09-09 11:41:46",
"updated_at": "2019-09-14 20:57:03",
"title": "Brand identity #2",
"reference": "12",
"lead_company_id": "adeddc13-6962-0a19-ac72-6713d1cf1455",
"teamleader_company_id": 1,
"dealphase_id": "199a34fc-de5c-038d-a655-c61fa4d97d17",
"estimated_closing_date": null,
"po_number": "az215487",
"teamleader_deal_phase_id": 6,
"dealPhase": "Refused",
"tasksCount": 5,
"companyLanguage": "nl",
-------------------------
"minimumStatus": "ready", ||||||accessor results
-------------------------
"invoiced": 1,
(...)
I would like to filter TeamleaderDeal results by MinimumStatus to show for example only results where MinimumStatus equal to 'Ready' ?
Thanks
You may use
public function getMinimumStatusAttribute($minimumStatus = NULL) {
if($minimumStatus === 'Ready') {
// do the magic here
$statusIds = [];
$tasks = $this->tasks()->get();
foreach ($tasks as $task) {
array_push($statusIds, $task->task_status_id);
}
return taskStatus::orderBy('order', 'asc')
->where('operational_status', '=', true)
->whereIn('id', $statusIds)->value('name');
}
// else return default value
return $this->attributes['minimum_status'];
}
Edit
Regarding your updates you may use reject method provider by Laravel Collection
E.g
$teamleaderDeals = TeamleaderDeal::all();
return $teamleaderDeals->reject(function ($teamleaderDeal) {
return $teamleaderDeal->minimumStatus !== 'Ready';
});
I think you need this simple where clause:
return taskStatus::orderBy('order', 'asc')
->where('operational_status', '=', true)
->where('name', 'Ready')
->whereIn('id', $statusIds)->value('name');
What are accessors?
Accessors create a dummy attribute on the object which you can access as if it were a database column. So if your database has user table and it has FirstName and LastName column and you need to get full name then it will be like.
Syntax of accessors:
get{Attribute}Attribute
Example :
public function getFullNameAttribute(){ return $this->FirstName. " ".$this->LastName;}
After that you can get full user name with below accessors.
{{ $user->full_name }}
After that you can get full user name with below accessors.
{{ $user->full_name }}
What is Mutator ?
Mutator is use to set value of attribute, a mutator transforms an eloquent attribute value when it is set.
How to define a mutator,
set{Attribute}Attribute
Above method on your model where {Attribute} is the "studly" cased name of the column you wish to access.
Example :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Register extends Model
{
/**
* Set the user's first name.
*
* #param string $value
* #return void
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = strtolower($value);
}
}
Now you can use this in your controller like.
use App\Models\Register;
$register= Register::find(1);
$register->name = 'Websolutionstuff';
I want this object only if it has all the necessary relationships.
At the moment my code:
StudentController
$student = Student::with('inscriptions','inscriptions.classroom')
->find($request->user()->id);
Student
public function inscriptions()
{
return $this->hasMany('App\InscribedStudent');
}
InscribedStudent - Note: "Registration Open"
public function classroom()
{
return $this->hasOne('App\Classroom', 'id')->where('registration_open', true);
}
Json Return When haven't registration opened
{
"inscriptions": [
{
"id": 1,
"student_id": 1,
"classroom_id": 1,
"deleted_at": null,
"created_at": "2019-07-04 23:34:48",
"updated_at": "2019-07-04 23:34:48",
"classroom": null
}
]
}
I want to do something like that, because I don't need the object InscribedStudent if I haven't a classroom.
public function inscriptions()
{
return $this->hasMany('App\InscribedStudent')
->hasOne('App\Classroom', 'id')
->where('registration_open', true);
}
You can use has() or whereHas() to check that the classroom exists.
https://laravel.com/docs/5.8/eloquent-relationships#querying-relationship-existence
// this will only get students that have a classroom through inscriptions
$students = Student::has('incriptions.classroom')
->with('inscriptions.classroom')
->get();
// this will get students, but only fetch inscriptions if there is a classroom
$students = Student::with(['inscriptions' => function($inscriptionQuery) {
$inscriptionQuery->has('classroom')->with('classroom');
}])
->get();
You can also make a custom scope on the Student model if you want to use that instead.
// this will only get students that have a classroom through inscriptions
public function scopeHasClassroom($query)
{
$query->has('inscriptions.classroom')
->with('inscriptions.classroom');
}
// this will get students, but only fetch inscriptions if there is a classroom
public function scopeHasClassroom($query)
{
$query->with(['inscriptions' => function($inscriptionQuery) {
$inscriptionQuery->has('classroom')->with('classroom');
}]);
}
Then you can call the custom scope like this:
$students = Student::hasClassroom()->get();
https://laravel.com/docs/5.8/eloquent#query-scopes
I'm building an api using eager loading so i can simply return the user model with its deep relations and it automatically be converted as json. Here's the set up.
users
id
..
clients
id
..
user_clients
id
user_id
client_id
..
campaigns
id
..
client_campaigns
id
client_id
campaign_id
..
campaign_activities
id
campaign_id
..
client_campaign_activity_templates
id
campaign_activity_id
client_id *(templates are unique per client)*
..
I've setup the models' relationships.
User
public function clients() {
return $this->belongsToMany('App\Client','user_clients');
}
Client
public function campaigns() {
return $this->belongsToMany('App\Campaign','client_campaigns');
}
Campaign
public function activities() {
return $this->hasMany('App\CampaignActivity');
}
CampaignActivity
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')
}
I have a simple api endpoint to provide a JSON of a User object including its deep relations using eager loading.
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates')->find($request->user()->id);
}
Testing this using postman, I can get the user including its deep relations.
{
"user": {
"id": 1,
"name": "user1",
"clients": [
{
"id": 1,
"name": "client1",
"campaigns": [
{
"id": 1,
"name": "campaign1",
"activities": [
{
"id": 1,
"name": "activity1",
"templates": [
{
"id": 1,
"name": "template1 for client1",
"client_id": 1,
"body": "this is a template.",
}, {
"id": 2,
"name": "template1 for client2",
"client_id": 2,
"body": "This is a template for client2"
}
]
}, {
"id": 2,
"name": "activity2",
"templates": []
}, {
"id": 3,
"name": "activity3",
"templates": []
}
]
}
]
}
]
}
}
However, on the user->clients->campaigns->activities->templates level, it will list all the templates for that activity. I know based on the code of the relationships of the models above that it's supposed to behave like that.
So the question is How would you filter the templates to filter for both campaign_activity_id and client_id?
I've been experimenting on how to filter the templates so it will only list templates for that activity AND for that client as well. I have a working solution but it's N+1, I'd prefer eloquent approach if possible. I've been scouring with other questions, answers and comments for a closely similar problem, but I had no luck, hence I'm posting this one and seek for your thoughts. Thank you
I think what you need are eager loading constraints.
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates',
function($query) use($request) {
$client_ids = Client::whereHas('users', function($q) use($request){
$q->where('id', $request->user()->id);
})->pluck('id');
$query->whereIn('templates.client_id', $client_ids);
})->find($request->user()->id);
}
Not tested but it should only require one additional query.
What I am doing is: define a constraint for your eager loading, namely only show those templates that have a client_id that is in the list (pluck) of Client IDs with a relation to the User.
Try using closures to filter through related models:
$users = App\User::with([
'clients' => function ($query) {
$query->where('id', $id);
},
'clients.campaigns' => function ($query) {
$query->where('id', $id);
}
])->get();
Here's my working solution, but I'm still interested if you guys have a better approach of doing this.
On the CampaignActivity model, I added a public property client_id and modified the relationship code to
CampaignActivity
public $client_id = 0
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')->where('client_id', $this->client_id);
}
and on my controller, limit the eager loading to activities only (actually, there are more sqls executed using eager loading[9] in this case vs just iterating[7], and also eager loading doesn't make sense anymore because we're iterating lol)
public function getLoggedInUser(Request $request) {
foreach ($user->clients as $client)
foreach( $client->campaigns as $campaign)
foreach ($campaign->activities as $activity) {
$activity->client_id = $client->id;
$activity->templates; //to load the values
}
return $user;
}