make better code in laravel many to many relation - laravel

hi i wrote this code and it works just fine but i think its not the best way to do it!
i want to get all the jobs for 1 company.
each company can have many addresses and each address can have many jobs
here is my code:
$company = Company::find($id)->with('addresses.jobDetails.job')->first();
$jobs = [];
foreach ($company->addresses as $address) {
foreach ($address->jobDetails as $detail) {
array_push($jobs, [
'id' => $detail->job->id,
'title' => $detail->job->title,
'country' => $detail->job->country,
'city' => $detail->job->city,
'type' => $detail->job->type,
'work_types' => JobType::where('job_id',$detail->job->id)->pluck('title'),
'income' => $detail->income,
]);
}
}
return $jobs;
can anyone help me to change this to better code please
thank you in advance

You do the opposite and start with JobDetails
$jobDetails = JobDetail::whereHas('address.company', function($companyQuery) use($id) {
$companyQuery->where('id', $id);
})->whereHas('jobs', function($jobQuery) {
$jobQuery->where('is_active', 1);
})->with('jobs')->get();
foreach ($jobDetails as $detail) {
array_push($jobs, [
'id' => $detail->job->id,
'title' => $detail->job->title,
'country' => $detail->job->country,
'city' => $detail->job->city,
'type' => $detail->job->type,
'work_types' => JobType::where('job_id',$detail->job->id)->pluck('title'),
'income' => $detail->income,
]);
}
return $jobs;
EDIT:
In your query
Company::find($id)->with('addresses.jobDetails.job')->first();
You run 4 queries with eager loading. one for each model. You can check in the result that you got that all the data is present in the variable $company.
The example I gave you it runs only two queries, the first one (job_details) will use joins to filter the Job results by the id of the companies table (you can make it faster by using the field company_id in the addresses table)
The second one is for the jobs relation using eager loading.

Related

Laravel eloquent query slow with 100000 records

I have this simple eloquent query, works fine, but with few records.
When database increments until 100000 records become very slow.
I read should be use chunk instead of get. How can I implement it for this query?
$collection = Contact::with('shop');
$collection = $collection->orderBy('created_at', 'DESC');
$collection = $collection->get();
$json = $collection->map(function ($contact) {
return [
'id' => $contact->id,
'name' => $contact->name,
...about 50 columns more.
'shop' => [
'id' => optional($contact->shop)->id,
'name' => optional($contact->shop)>name
],
...about 6 relations more.
];
});
$json = $json->paginate(50);
return response()->json(['contacts' => $json], 200);
You are converting getting all the data like 1M or how many records it has. Then you are mapping it and paginate it and getting only 50. There is huge performance problem with your code.
You can directly call like this:
return response()->json(['contacts' => Contact::with('shop')->orderBy('created_at', 'DESC')->paginate(50)], 200);
If you only need id and name for contacts:
return response()->json(['contacts' => Contact::select('id', 'name', 'created_at')->orderBy('created_at', 'DESC')->paginate(50)], 200);

Why stored duplicate rows to table

I have custom query for adding parsed remote posts to table:
foreach ($parsedPosts as $post) {
$hasRemotePostAlready = Post::where('remote_post_id', $post->id)->first();
if(null === $hasRemotePostAlready) {
$data = [
'title' => $post->title,
'description' => $post->description,
'remote_post_id' => $post->id
];
Post::create($data);
}
}
Variable $parsedPosts has more then 3500 posts and when I run my script to add posts then any remote posts duplicated. Why they posts duplicated and why not work my condition:
$hasRemotePostAlready = Post::where('remote_post_id', $post->id)->first();
How I can fix duplicating rows problem in my case?
You can use firstOrCreate or updateOrCreate methods for this case, take a look at the documentation
In your case try this:
$hasRemotePostAlready = Post::firstOrCreate(
[
'remote_post_id' => $post->id // columns to check if record exists
],
[
'title' => $post->title,
'description' => $post->description
]
);

Laravel Update Multiple Columns with QueryBuilder

I'm using Query builder, I successfully update na first column but on the second query the change doesnt happen, I already checked the view part the name of input and its correct. here is my code.
DB::table('area')
->where('id', $request->get('area_id'))
->update(['island_group_id' => $request->get('island_group_id')],
['region_id' => $request->get('region_id')]);
return 'test';
$updateDetails = [
'island_group_id' => $request->get('island_group_id'),
'region_id' => $request->get('region_id')
];
DB::table('area')
->where('id', $request->get('area_id'))
->update($updateDetails);
DB::table('area')
->where('id', $request->get('area_id'))
->update([
'island_group_id' => $request->get('island_group_id'),
'region_id' => $request->get('region_id')
]);
return 'test';
I think it will be helpful to you.
$area_id = $request->get('area_id');
$island_group_id = $request->get('island_group_id');
$region_id = $request->get('region_id');
$update_details = array(
'island_group_id' => $island_group_id
'region_id' => $region_id
);
DB::table('area')
->where('id', $area_id)
->update($update_details);
Because you use every time new array for update field. Please use one array for update multiple field like:
DB::table('area')
->where('id', $request->get('area_id'))
->update(array(
'island_group_id'=>$request->get('island_group_id'),
'region_id'=>$request->get('region_id')
));

Laravel Eloquent ORM relationship query

I need help, I have a problem with querying Model Relationships.
I actually know and get the job done using the query method but I'd like to know the "laravel way" of querying relationships.
Here's what's on my controller.
//HealthProfile Controller
//$id value is 2
$health_profiles = User::find($id)->with('health_profiles')->first();
problem is that the return for the query is the records for id = 1 and not id = 2. It basically ignored the "find" method. I just want to get the health profiles for a specific user_id.
[id] => 1
[firstname] => patrick
[lastname] => marino
[email] => patrick#gmail.com
[membership_code] => starpatrick
[birthdate] => 1989-05-17
[contact_number] => 1230123
[active] => 1
[created_at] => 2014-07-01 16:10:05
[updated_at] => 2014-07-01 16:10:05
[remember_token] =>
[health_profiles] => Array
(
[0] => Array
(
[id] => 1
[user_id] => 1
[parent_id] =>
[name] => patrick star
[relationship] =>
[gender] => male
[birthdate] => 1989-05-17
[marital_status] =>
[number_of_children] =>
[weigth] =>
[height] =>
[blood_type] =>
[blood_pressure] =>
[hdl] =>
[ldl] =>
[vldl] =>
[visual_activity] =>
[lifestyle] =>
[current_bmi] =>
[weight_goal] =>
[weekly_goal] =>
[rdc] =>
[created_at] => 2014-07-01 16:10:05
[updated_at] => 2014-07-01 16:10:05
)
This is my schema
//User model
public function health_profiles()
{
return $this->hasMany('HealthProfile');
}
//HealthProfile model
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');
}
First a few words of explanation:
find($id) already runs the query (it uses where(id, $id)->first() under the hood) so place it at the end, because now you unwittingly did this:
User::find($id);
User::with('health_profiles')->first();
Another problem is, like you already noticed, that Eloquent won't work with this setup:
public function health_profiles() ...
$user = User::find($id);
$user->health_profiles; // null
because when loading dynamic properties (relations) it looks for camelCased method on the model.
However eager loading will work as expected:
$user = User::with('health_profiles')->find($id);
$user->health_profiles; // related model/collection
So you definitely should comply with the naming conventions if you want Eloquent to be your friend ;)
But that's not all. It will work the other way around:
public function healthProfiles() ...
$user = User::find($id);
$user->healthProfiles; // works, returns related model/collection
$user->health_profiles; // works as well, returns the model/collection
To sum up and answer your question, each of those will work for you:
// Assuming Profile is the model
// 2 queries
$user = User::find($id);
$profiles = $user->healthProfiles;
// or 1 query
$profiles = Profile::where('user_id', $id)->get();
// or 2 queries: 1 + 1 subquery
$profiles = Profile::whereHas('user', function ($q) use ($id) {
$q->where('users.id', $id);
})->get();
You can try putting the with before the find so that the builder knows to eager load that relationship on what you are trying to find.
$user = User::with('health_profiles')->find($user_id);
$health_profiles = $user->health_profiles;
Try this
User::with('health_profiles')->find($id);
I don't think you need to call the first method because find as it's stated will only find the one row of data that you need.
I've found the culprit. L4 has problems with snake cased methods! I changed it to camelCase and it worked.
$lawly = User::where('id', '=', 2)->first();
$lawly->healthProfiles->toArray()
src: https://coderwall.com/p/xjomzg

how to build fluent query by array of criterias in Laravel

If I store in DB criterias what I want to use to build and filter queries - how to build query with Laravel Fluent Query Builder? Maybe you can give advice for refactoring this array for adding OR/AND to make complex filter by such conditions? Thanks!
For example if I read from database these criteria and made array:
$array = array(
'0' => array(
'table' => 'users',
'column' => 'name',
'criteria' => 'LIKE'
'value' => 'John'
),
'1' => array(
'table' => 'groups',
'column' => 'name',
'criteria' => 'NOT LIKE'
'value' => 'Administrator'
),
...
)
If you are really set on doing it your way, make a switch statement to set the flag for the index of the array
switch ($query)
{
case 0:
$this->get_data($x);
break;
case 1:
$this->get_data($x);
break;
case 2:
$this->get_data($x);
break;
default:
Return FALSE;
}
public function get_data($x){
$value = DB::table($array[$x]['table'])
->where($array[$x]['name'], $array[$x]['criteria'], $array[$x]['value'])
->get();
Return $value;
}
However, I would not advise this at all. Instead of storing the query filters in an array I feel you should just make them methods in your model. This will help with readability, modularity, and having reusable code.
public function get_user_by_name($name){
$user = DB::table('users')->where('name', 'LIKE', '%'.$name.'%')->get();
return $user;
}
public function get_group_by_name($name, $criteria = 'LIKE'){
$groups = DB::table('groups')->where('name', $criteria, '%'.$name.'%')->get();
return $groups;
}
http://laravel.com/docs/database/fluent

Resources