I need to alias when I do a Laravel eager loading:
$posts = Post::with(array('images as main_image' => function($query) // do not run
{
$query->where('number', '=', '0');
}))
->where('id', '=', $id)
->get();
return Response::json($posts);
I need to to this because I want the JSON response like this:
[
{
"id": 126,
"slug": "abc",
"name": "abc",
"created_at": "2014-08-08 08:11:25",
"updated_at": "2014-08-28 11:45:07",
"**main_image**": [
{
"id": 223,
"post_id": 126
...
}
]
}
]
It is possible?
Perfect! you give me the idea. Finally I've done this:
Post.php
public function main_image()
{
return $this->hasMany('FoodImage')->where('number','=','0');
}
public function gallery_images()
{
// for code reuse
return $this->main_image();
}
PostController.php
$posts = Post::with('main_image', 'gallery_images')
->where('id', '=', $id)
->get();
I don't think you can do that with Eloquent, but there would be a few work-arounds that might work.
If you are using PHP 5.6, it's possible to alias the function.
use function images as main_image;
If you are using a version of PHP less than that, you can create a main_image() function and have it call images().
public function main_image()
{
return $this->images();
}
Related
I have written as below in my code in LineSheet controller and the items function is in LineSheet model, and I have an array call $seasons. Now I need to send this $seasons array with items
LineSheet Controller:
$linesheetItems = LineSheetItem::select('linesheet_id', 'items.season', 'items.amt_item')
->join('items', function ($join) {
$join->on('items.amt_item', '=', 'linesheet_items.item_code');
$join->on('items.company', '=', 'linesheet_items.company');
$join->on('items.division', '=', 'linesheet_items.division');
})
->where('linesheet_items.linesheet_id', '=', $id)
->groupBy('items.amt_item', 'items.season')
->get();
foreach ($linesheetItems as $linesheetItem) {
$seasons[] = Season::where('code', $linesheetItem['season'])->first();
}
$linesheet = LineSheet::where('linesheet.id', '=', $id)
->select('linesheet.*')->with(['items', 'creator:id,username', 'updater:id,username'])
->first();
LineSheet Model:
public function items()
{
return $this->hasMany('\App\Models\LineSheetItem', 'linesheet_id', 'id')
->join('items', function ($join) {
$join->on('items.amt_item', '=', 'linesheet_items.item_code');
$join->on('items.company', '=', 'linesheet_items.company');
$join->on('items.division', '=', 'linesheet_items.division');
})
->join('inventory_items', function ($join) {
$join->on('inventory_items.item', '=', 'linesheet_items.item_code');
$join->on('inventory_items.company', '=', 'linesheet_items.company');
$join->on('inventory_items.division', '=', 'linesheet_items.division');
})
->select('linesheet_items.linesheet_id', 'items.id', 'items.amt_item', 'items.image_name',
'items.company', 'items.division', 'items.color_description', 'items.item_description',
'items.season', 'items.wholesale_price', 'items.retail_price', 'items.color_code',
'items.vendor_desc', 'items.vendor_code', 'inventory_items.on_hand', 'inventory_items.cost',
'items.brand', 'items.category_code', 'items.category', 'items.fabric_code', 'items.fabric_desc')
->groupBy('linesheet_items.item_code', 'linesheet_items.company', 'linesheet_items.division');
}
with items I need to attach my $seasons array also and get a response as below
I expect a final output as below:
"items": [
{
"linesheet_id": 44,
"id": 61,
"amt_item": "PS839730WT",
"image_name": "image_name",
"company": "01",
"division": "PAP",
"color_description": "colordes1",
"item_description": "itemdes1",
"season": "S1",
"wholesale_price": "100",
"retail_price": "100",
"color_code": "colorcode1",
"vendor_desc": "vendor_desc",
"vendor_code": "vendor_code",
"on_hand": "40",
"cost": "3.70",
"brand": "brand",
"category_code": "category",
"category": "category1",
"fabric_code": "code1",
"fabric_desc": "fabric_desc",
seasons: [
//array elements
]
}
]
How to achieve this?
You can use belongsTo relationship and then eager load them. A LineSheetItem seems to belong to a Session.
So you can do LineSheetItem::with('season')->get().
https://laravel.com/docs/8.x/eloquent-relationships#one-to-many-inverse
https://laravel.com/docs/8.x/eloquent-relationships#eager-loading
I have two models TeamleaderCompany which : has many TeamleaderCompanyTag
TeamleaderCompany
public function teamleaderCompanyTags()
{
return $this->hasMany('App\TeamleaderCompanyTag');
}
TeamleaderCompanyTag
public function teamleaderCompany()
{
return $this->belongsTo(TeamleaderCompany::class);
}
when I TeamleaderCompany::all() I have this results :
(...)
"teamleader_company_tags": [
{
"id": 7,
"tag": "hot lead",
"teamleader_company_id": 3,
"created_at": "2019-09-03 09:23:51",
"updated_at": "2019-09-03 09:23:51"
},
{
"id": 8,
"tag": "reseller",
"teamleader_company_id": 3,
"created_at": "2019-09-03 09:23:51",
"updated_at": "2019-09-03 09:23:51"
}
]
(...)
What I'm trying to do is to show TeamleaderCompany results where teamleaderCompanyTags has only one tag which is 'reseller' (if there is another tag except 'reseller' don't show)
$companies->whereHas(
'teamleaderCompanyTags',
function ($query) use ($condition) {
$query->where('tag',
(...)
);
}
);
thanks
Try this method
TeamleaderCompany::with('teamleaderCompanyTags:id,tag')
->whereHas('teamleaderCompanyTags',function(\Illuminate\Database\Eloquent\Builder $query){
$query->where('tag', "reseller");
})->get();
Try this query:
TeamleaderCompany::has('teamleaderCompanyTags', '=', 1) // companies that have only one tag
->whereHas('teamleaderCompanyTags', function ($query) { // companies that have `reseller` tag
$query->where('tag', 'reseller');
})
->get()
You have two conditions.
it need to have the "reseller" tag
it's the only tag that it has
TeamleaderCompany::whereHas('teamleaderCompanyTags', function ($query) {
$query->where('tag', 'reseller');
})
->whereDoesntHave('teamleaderCompanyTags', function ($query) {
$query->where('tag', '!=', 'reseller');
})
->get()
I am trying to query a pivot table to show how many exercises have one day, but there is something wrong with that.
I need to get all the exercises by Monday or any day.
I have three tables: routines, exercises and exercise_routine.
routines exercise_routine exercise
-------- ---------------- --------
id id id
name routine_id name
description exercise_id description
user_id week_day_id
sets
reps
I would like to get all the exercises by week_day_id, hope you understand my problem.
I tried these examples from other people on stackoverflow but it does not work.
Exercise::whereHas('routines', function($q) {
$q->where('routines.week_day_id', 1);
})
->get();
return \DB::table('exercises')
->join('exercise_routine', 'exercise_routine.exercise_id', '=', 'exercises.id')
->where('exercise_routine', 1)
->get();
dd( DB::table('exercises')
->where('exercises.id', '=', 1)
->select('exercises.id'));
// Routine Model
public function exercises()
{
return $this->belongsToMany('App\Models\Exercise');
}
// ExerciseModel
public function routines()
{
return $this->belongsToMany('App\Models\Routine')->as('plan')->withPivot('sets', 'reps', 'week_day_id')->withTimestamps();
}
// Controller
public function show(WeekDay $weekday)
{
Exercise::whereHas('routines', function($q, $weekday) {
$q->where('routines.week_day_id', $weekday);
})
->get();
}
// api routes
Route::group(['prefix' => '/{weekday}/exercises'], function () {
Route::get('/', 'WeekDayExerciseController#show')->middleware('auth:api');
});
I expected to get all the exercises by Monday for example like this:
{
"id": 236,
"name": "Upright Row (Barbell)",
"description": "Description for Upright Row (Barbell)"
},
{
"id": 237,
"name": "Upright Row (Cable)",
"description": "Description for Upright Row (Cable)"
},
{
"id": 238,
"name": "Upright Row (Dumbbell)",
"description": "Description for Upright Row (Dumbbell)"
},
You are wrong here
Exercise::whereHas('routines', function($q) {
$q->where('routines.week_day_id', 1);
})
->get();
Because week_day_id column isn't on routines table,
You need to query pivot table using wherePivot method in this way
Exercise::whereHas('routines', function($q) {
$q->wherePivot('week_day_id', 1);
})
->get();
see: https://laravel.com/docs/master/eloquent-relationships#many-to-many
In your Controller you've got a slight error in your closure
// Controller
public function show(WeekDay $weekday)
{
Exercise::whereHas('routines', function($q) use ($weekday) {
$q->where('week_day_id', $weekday->id);
})
->get();
}
Define a simple query for getting three tables data
table users,
table user_details,
table user_device_datas
On my LoginController define a function to get data from UserDetails model .
$userInformation = UserDetails::userDetail($user->id);
on User model define a relationship
public function UserDeviceData() {
return $this->hasMany(UserDeviceData::class);
}
public function UserDetails()
{
return $this->hasOne(UserDetails::class);
}
On UserDetails model my query for getting data look like
public static function userDetail($id){
$result = User::whereHas('userDetails',function($query) use ($id) {
$query->where('user_id',$id);
})->whereHas('UserDeviceData',function($query) use ($id) {
$query->where('user_id',$id);
})->where('user.id', $id)
->with('userDetails','UserDeviceData')
->first();
}
Response i am getting :
"userInfo": {
"id": 2,
"name": null,
"email": "test#test.com",
"phone": null,
"address": null,
"company": null,
"description": null,
"device_id": null,
"system_role": "User",
"status": "Active",
"otp_verify_id": "253526851",
"parent_id": 0,
"last_login": "2018-04-23 12:03:43",
"profile_picture": null,
"business_card_pic": null
}
I am not getting my userDetails Data on the query.what is the mistake in my code?
public static function userDetail($id){
$result = User::whereHas('userDetails',function($query) use ($id) {
$query->where('user_id',$id);
})->whereHas('UserDeviceData',function($query) use ($id) {
$query->where('user_id',$id);
})->where('users.id',$id)
->with(['UserDetails' => function($query) use ($id){
$query->where('user_id',$id);
},
'UserDeviceData' => function($query) use ($id){
$query->where('user_id',$id);
}
])
->first();
return $result ;
}
You have to use with() to get the relations too.
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;
}