Laravel query join child table as array - php

I have this data
$result = DB::table('zone_regions')
->where('zone_regions.id', '=', $request->input('zone'))
->join('areas', function ($join) use($area) {
$join->on('zone_regions.id', '=', 'areas.zone_id')
->where('areas.id', '=', $area);
})
->join('hthree_regions', function ($join) use($city) {
$join->on('areas.id', '=', 'hthree_regions.area_id')
->where('hthree_regions.id', '=', $city);
})
->join('segments', function ($join) use($segment) {
$join->on('hthree_regions.id', '=', 'segments.hthree_id')
->where('segments.id', '=', $segment);
})
->join('links', function ($join) use($link) {
$join->on('segments.id', '=', 'links.segment_id')
->where('links.id', '=', $link);
})
->join('titik_closurs', function ($join) use($closure) {
$join->on('links.id', '=', 'titik_closurs.link_id')
->where('titik_closurs.id', '=', $closure);
})
// ->leftjoin('core_histories', 'titik_closurs.id', '=', 'core_histories.titik_id')
// ->select('titik_closurs.*', 'core_histories.*')
->groupBy('titik_closurs.id')
->get();
I want to add titik_closurs histories (core_histories) as an array into my returned data.
Sample data
Current data
This is what I have currently
data: [{...}]
0: {...}
id: 3
latitude: "6.2088000"
longitude: "106.8456000"
site_name: "98987454-54741115"
user_id: 1
zone_id: 2
And this is what I want to have
data: [{...}]
0: {...}
id: 3
latitude: "6.2088000"
longitude: "106.8456000"
site_name: "98987454-54741115"
user_id: 1
zone_id: 2
histories: [{......}]
histories: [{......}] array added to the results.
Note
This data (current data) are a bit messed up as is include data of all joined tables in my function, what I really need is just data of table titik_closurs and child of it table core_histories the rest aren't really useful for me in this case.
Sample of final data I wish to have:
data: [{...}]
0: {...}
// just data of `titik_closurs` exclude data of `zone_regions, areas, hthree_regions` etc.
id: 3
latitude: "6.2088000"
longitude: "106.8456000"
site_name: "98987454-54741115"
histories: [{....}]
Any idea how to do that?
Update
If I change my query (last part) to something like this:
->join('titik_closurs', function ($join) use($closure) {
$join->on('links.id', '=', 'titik_closurs.link_id')
->where('titik_closurs.id', '=', $closure)
->leftjoin('core_histories', function ($joinn) {
$joinn->on('titik_closurs.id', '=', 'core_histories.titik_id');
});
})
->groupBy('titik_closurs.id')
->get();
It does join core_histories to titik_closurs BUT,
It doesn't show them as child array
It only gets first core_histories not all.

Solved
I was playing with codes and came up with union solution then it led me to array_merge which fixed my issue.
So what I did basically was getting all histories in separate variable and push it into my default array.
//get histories
$histories = DB::table('core_histories')->where('titik_id', $closure)->get();
// default function
$result = DB::table('zone_regions')......
//merge them into one
$res = array_merge($result, ['histories' => $histories]);
Result
data: {
0: {...} // data
histories: [ // histories
0: {...}
1: {...}
]
}
Hope it help others as well.

For this particular situation you seem to have 3 (or 4?) related models. It's far easier (and much more readable) to define them as relationships and then do a nested retrieval with aggregation say:
$zone = Zone::with('region.segment.link.titik')
->where('id', $request->input('zone');
$titik = $zone->region->flatMap(function ($region) { return $region->segment })
->flatMap(function ($segment) { return $segment->link })
->flatMap(function ($link) { return $segment->titik });
What could also work (with yout current approach):
$result = DB::table('zone_regions')
->where('zone_regions.id', '=', $request->input('zone'))
->join('areas', function ($join) use($area) {
$join->on('zone_regions.id', '=', 'areas.zone_id')
->where('areas.id', '=', $area);
})
->join('hthree_regions', function ($join) use($city) {
$join->on('areas.id', '=', 'hthree_regions.area_id')
->where('hthree_regions.id', '=', $city);
})
->join('segments', function ($join) use($segment) {
$join->on('hthree_regions.id', '=', 'segments.hthree_id')
->where('segments.id', '=', $segment);
})
->join('links', function ($join) use($link) {
$join->on('segments.id', '=', 'links.segment_id')
->where('links.id', '=', $link);
})
->join('titik_closurs', function ($join) use($closure) {
$join->on('links.id', '=', 'titik_closurs.link_id')
->where('titik_closurs.id', '=', $closure);
})
// ->leftjoin('core_histories', 'titik_closurs.id', '=', 'core_histories.titik_id')
// ->select('titik_closurs.*', 'core_histories.*')
->get()
->groupBy('titik_closurs.id'); // Note the order change
This will use the collection's groupBy rather than the SQL GROUP BY (which does not provide any groups to the result)

You can do this with nested relationship. If you have model for each table its easier for you. see below code:
ZoneRegion Model:
public function area() {
return $this->belongsTo(Area::class);
}
in Area model:
public function hthree() {
return $this->belongsTo(HthreeRegions::class);
}
and so on...
when you want to use and append array as you want you can do:
ZoneRegion::where('your condition')->with('area.hthree.segments.etc')->get();
Maybe my relationship is wrong because I don't know exactly what your relationship is like, and if you give an example or tell the relationship, maybe I can answer better.
But in the end, this method simply eliminates your need.
Hope it help

Related

Data Duplication PHP and Laravel

below is my code that duplicates database entries, what could the problem be?
When I try to trigger the end point that fetches employees it duplicates the entries multiple times
$all_employees = Employee::with([
'department',
'sub_department',
'first_supervisor',
'second_supervisor',
'reportingTo',
])
->select(
'employees.*',
'attendance_times.start_time as attendance_time',
'cities.city',
'countries.country',
'pay_frequencies.frequency as pay_frequency',
'duty_types.name as duty_type',
'rate_types.name as rate_type',
'positions.name as position_name',
'departments.department as sub_department',
'supervisors.name as supervisor_name'
)
->leftJoin('positions', function ($join) {
return $join->on('employees.position_id', '=', 'positions.id');
})
->leftJoin('countries', function ($join) {
return $join->on('employees.country_id', '=', 'countries.id');
})
->leftJoin('supervisors', function ($join) {
return $join->on('employees.first_supervisor_id', '=', 'supervisors.id');
})
->leftJoin('cities', function ($join) {
return $join->on('employees.city_id', '=', 'cities.id');
})
->leftJoin('attendance_times', function ($join) {
return $join->on('employees.attendance_time_id', '=', 'attendance_times.id');
})
->leftJoin('departments', function ($join) {
return $join->on('employees.sub_department_id', '=', 'departments.id');
})
->leftJoin('pay_frequencies', function ($join) {
return $join->on('employees.pay_frequency_id', '=', 'pay_frequencies.id');
})
->leftJoin('duty_types', function ($join) {
return $join->on('employees.duty_type_id', '=', 'duty_types.id');
})
->leftJoin('rate_types', function ($join) {
return $join->on('employees.rate_type', '=', 'rate_types.id');
})
->orderBy('employees.id', 'DESC')
->get();
return $this->customSuccessResponseWithPayload($all_employees);
Use with('Relation') while getting One to Many relation's data, because if you use leftJoin, you will get all the relation's data again.
Example:
Every Employee has many attendance_times, so if you create an SQL Query which gets all Employees and joined the attendance_times, the returned data will contain a duplicated EMP data every time it has attendance_times.
Basically use ->with('Relation').
Try to use the distinct with the query to fetch non-repetitive data.
DB::table('table')
->select('job_id')
->distinct()
->get();
It can be solved by adding the groupBy(employees.id) at the end of the query that's how I solved it.

How to get first entry from the record in Laravel relation?

I got this query, it fetch results from my database, based on relationships.
The query works fine, but it grabs the first entry from the database/table and not the first value of the array.
How can I fix that?
$motor = Motor::query()
->where([
['effect_id', '=', $request->effect_id],
['poles', '=', $request->poles],
['voltage_id', '=', $request->voltage_id],
['active', '=', 1],
])
->whereHas('MotorSize', function (Builder $query) {
$query->whereIn('mounting', ['B5', 'B14', 'B34', 'B35']);
})
->firstOrFail();
I have tried different things, such as:
Adding the orderBy to the Motor model and created a "priority" field in my database, giving each a number between 1 and 100:
(i actually thought this would work, but it still grabs the first entry from the table)
public function MotorSize()
{
return $this->belongsTo('App\MotorSize')->orderBy('priority', 'ASC');
}
I also tried something like this, but it's a mess and never got it to work:
$query->where(function ($query) {
$query->where('mounting', 'like', 'B5');
})
->orWhere(function($query) {
$query->where('mounting', 'like', 'B14');
})
->orWhere(function($query) {
$query->where('mounting', 'like', 'B34');
})
->orWhere(function($query) {
$query->where('mounting', 'like', 'B35');
});
Any suggestions will be welcome :)

Laravel Collection Query, Where I am going wrong?

My tables looks like this
area_trip
|id|dispatch_id|trip_id|status|
equipment_trip
|equipment_id|trips_id|dispatch_id|
trips
|id|dispatch_id|status
I am trying to pass collection to my resource. Can someone check my query and tell me what I am doing wrong as following query returning all the data matches dispatch_id whether it matches equipment_id or not. Btw I am new to laravel.
return
Resources::collection(
area_trip::where('dispatch_id', $request->dispatch_id)
->where('status', 1)
->orWhere('status', 9)
->whereHas('equipment_trip', function($query) use ($request) {
$query->where('equipment_trip.equipment_id', '=', $request->equipment_id);
})
->with(['equipment_trip', 'createdBy', 'updatedBy', 'area', 'trips'])
->orderBy('tripStartDate', 'ASC')
->orderBy('status', 'ASC')
->get());
Here is the relationship set up in area_trip model
public function equipment_trip()
{
return $this->belongsTo(equipment_trip::class, 'trip_id', 'trips_id');
}
I believe your whereHas sub query is incorrect also instead of where and orWhere use where in and you can define all statuses necessary, try this:
Resource::collection(area_trip::where('dispatch_id', $request>dispatch_id)
->whereIn('status', [1, 9])
->whereHas('equipment_trip', function($query) use ($request) {
return $query->where('equipment_id', '=', $request->equipment_id);
})
->with(['equipment_trip', 'createdBy', 'updatedBy', 'area', 'trips'])
->orderBy('tripStartDate', 'ASC')
->orderBy('status', 'ASC')
->get());

Laravel 4, messy Eloquent join query

I'm working on somehow messy project where structure of db is:
Object
id, title, description, url ... [and some more columns]
ObjectMeta
object_id, variable, value
So let's give you:
Object: 1, 'Cool Book', 'This cool book is just for you!', ...
Object Meta:
1, 'author_name', 'John Doe'
1, 'released', 2014
1, 'price', 23.22
1, 'url', 'http://amazon.com/coolbook'
1, 'genre', 3
So I need to perform query which will:
Pull all Objects that has genre 3, then sort those Objects by their released date.
$objs = static::Active()
->where('object', '=', $object)
->where('category','=',$category)
->whereIn('site_id', array(self::$site_id, 0))
->leftJoin('object_meta', function($join)
{
$join->on('object.id','=', 'object_meta.content_id');
})
->where('object_meta.variable', 'genre')
->where('object_meta.value', 3);
->where('object_meta.variable', 'released');
->orderBy('object_meta.value', 'desc');
->groupBy('object.id')
->paginate(20);
This query has 0 results even if there are plenty of those books out there. I know that the second object_meta.variable where is guilty here. How can I write this query to make it work?
Really appreciating your help.
-- Edit
I've created workaround but it's really, really bad workaround (funny thing is that I swear the query above was working for month or so).
$objs = static::Active()
->where('object', '=', $object)
->where('category','=',$category)
->whereIn('site_id', array(self::$site_id, 0))
->leftJoin('object_meta', function($join)
{
$join->on('object.id','=', 'object_meta.content_id');
})
->where('object_meta.variable', 'genre')
->where('object_meta.value', 3);
->groupBy('object.id')
->get();
foreach($objs as $obj)
{
$obj->id = $obj->object_id;
$objs_meta = ObjectMeta::where('object_id',$obj->object_id)->get();
foreach($objs_meta as $obj_meta)
{
$variable = $obj_meta->var;
$obj->$variable = $obj_meta->value;
}
}
$objs = $objs->sortBy(function($role){
return $role->released;
});
$objs = Paginator::make($objs->toArray(), sizeof($objs), $limit);
You need inner join (join()) not leftJoin(), and you need it twice:
$objs = static::Active()
->where('object', '=', $object)
->where('category','=',$category)
->whereIn('site_id', array(self::$site_id, 0))
->join('object_meta as genre', function ($join) {
$join->on('object.id', '=', 'genre.content_id')
->where('genre.variable', '=', 'genre')
->where('genre.value', '=', 3);
})
->join('object_meta as released', function ($join) {
$join->on('object.id', '=', 'released.content_id')
->where('released.variable', '=', 'released');
})
->orderBy('released.value', 'desc');
->select('object.*', 'released.value as released')
->distinct()
// or group by, doesn't matter in your case
// ->groupBy('object.id')
->paginate(20);
Then your objects will have additional property released with appropriate value.
According to the comments:
If you want to dynamically add the joins, then I suggest scopes like below:
public function scopeGenre($query, $value)
{
$query->join('object_meta as genre', function ($join) use ($value) {
$join->on('object.id', '=', 'genre.content_id')
->where('genre.variable', '=', 'genre')
->where('genre.value', '=', $value);
});
}
then:
$query->where(..)->genre(3)->...

Laravel Eloquent inner join with multiple conditions

I have a question about inner joins with multiple on values.
I did build my code like this in laravel.
public function scopeShops($query) {
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
// $join->on('kg_shops.active', '=', "1"); // WRONG
// EDITED ON 28-04-2014
$join->on('kg_shops.active', '=', DB::raw("1"));
});
}
Only problem is, it gives this outcome:
Column not found: 1054 Unknown column '1' in 'on clause' (SQL: select `kg_feeds`.* from `kg_feeds` inner join `kg_shops` on `kg_shops`.`id` = `kg_
feeds`.`shop_id` and `kg_shops`.`active` = `1`) (Bindings: array ( ))
As you can see, the multiple conditions in the join go fine, but it thinks the 1 is a column instead of a string. Is this even possible, or do I have to fix it in the where.
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
})
->select('required column names')
->where('kg_shops.active', 1)
->get();
You can see the following code to solved the problem
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
$join->where('kg_shops.active','=', 1);
});
Or another way to solved it
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
$join->on('kg_shops.active','=', DB::raw('1'));
});
//You may use this example. Might be help you...
$user = User::select("users.*","items.id as itemId","jobs.id as jobId")
->join("items","items.user_id","=","users.id")
->join("jobs",function($join){
$join->on("jobs.user_id","=","users.id")
->on("jobs.item_id","=","items.id");
})
->get();
print_r($user);
Because you did it in such a way that it thinks both are join conditions in your code given below:
public function scopeShops($query) {
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
$join->on('kg_shops.active', '=', "1");
});
}
So,you should remove the second line:
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id');
});
Now, you should add a where clause and it should be like this:
return $query->join('kg_shops', function($join)
{
$join->on('kg_shops.id', '=', 'kg_feeds.shop_id')->where('kg_shops.active', 1);
})->get();
You can simply add multiple conditions by adding them as where() inside the join closure
->leftJoin('table2 AS b', function($join){
$join->on('a.field1', '=', 'b.field2')
->where('b.field3', '=', true)
->where('b.field4', '=', '1');
})
More with where in (list_of_items):
$linkIds = $user->links()->pluck('id')->toArray();
$tags = Tag::query()
->join('link_tag', function (JoinClause $join) use ($linkIds) {
$joinClause = $join->on('tags.id', '=', 'link_tag.tag_id');
$joinClause->on('link_tag.link_id', 'in', $linkIds ?: [-1], 'and', true);
})
->groupBy('link_tag.tag_id')
->get();
return $tags;
Hope it helpful ;)
This is not politically correct but works
->leftJoin("players as p","n.item_id", "=", DB::raw("p.id_player and n.type='player'"))

Categories