Laravel 5 select only relationship data on hasMany relationship - php

I am building an application that has projects and projects have plot_types.
I want to be able to check if a plot_type exists under the current project.
I have the following code:
$testResult = $project->with(['plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
}])->first()
This produces the following MySQL:
select exists(select * from `projects` where exists (select * from `projects_plot_types` where `projects_plot_types`.`project_id` = `projects`.`id` and `name` = ?)) as `exists`
This SQL returns rows that are NOT related to the $project object. For example when I do dd($project) I get:
#attributes: array:11 [▼
"id" => "4"
"name" => "xxx"
"number" => "1234"
"builder" => "1"
"overall_budget" => "3456.00"
"start_date" => "2016-03-31"
"end_date" => "2016-04-30"
"created_date" => "2016-03-16 15:22:05"
"updated_date" => "2016-03-16 15:22:07"
]
Yet, when I do dd($testResult); it gives;
#relations: array:1 [▼
"plotTypes" => Collection {#767 ▼
#items: array:1 [▼
0 => ProjectsPlotTypes {#770 ▼
#table: "projects_plot_types"
#fillable: array:2 [▶]
+timestamps: false
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
#attributes: array:4 [▼
"id" => "1"
"project_id" => "1"
"name" => "TYPE 1 - VENTILATION"
"budget" => "324.67"
]
Notice, the project_id above shows 1. This is not related to the current project as the current project id is 4.
Why is this happening?

This is one of those potentially confusing parts of the ActiveRecord model. All your model instances contain the same methods used to retrieve model instances, so it easy think something should work one way when it really doesn't.
Calling $project->with(), this is the exact same as calling Project::with(). Even though you're calling with() on an instance of the project, it isn't going to restrict the loaded objects to only those related to your instance.
When you call $project->with(), the first thing it does is create a new query for all projects, and then adds in the eager loading. You then call first(), which just gets the first project record, and all its eager loaded objects.
To get the plot types for your specific project, you have a couple options.
Just query the relationship. $project->plotTypes() gives you a base query for all the plot types associated with your project. You can add your constraints and get the records from there.
$plotTypes = $project->plotTypes()->where('name', $row->plot_name)->get();
dd($plotTypes);
Load the related plot types with constraints:
// assume your project doesn't have any plottypes loaded yet
$project = Project::find(1);
// load the plottypes relation with constraints
$project->load(['plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
}]);
dd($project->plotTypes);
Filter the already loaded Collection of related plot types. $project->plotTypes has all the plot types related to your project, but you can use the where() method on the Collection (different than the where() on the query) to filter through the records in the Collection.
// assume your project already has all plotTypes loaded
$project = Project::with('plotTypes')->find(1);
// you just want to get a subset of those pre-loaded plottypes
$plotTypes = $project->plotTypes->where('name', $row->plot_name);
dd($plotTypes);

Use the whereHas method to do the filtering instead of the with
$testResult = $project->whereHas('plotTypes' => function($query) use ($row) {
$query->where('name', $row->plot_name);
})->with('plotTypes')->first();
And moreover do you want to get all the related records or only the first record?
if all then change the first() to get()
Hope this helps

Related

Undefined array key 0 error when checking if an array is empty [duplicate]

This question already has answers here:
Laravel check if collection is empty
(8 answers)
Eloquent Collection: Counting and Detect Empty
(12 answers)
How can I check if collection empty in view blade laravel?
(3 answers)
How to check if a Laravel Collection is empty?
(8 answers)
Laravel Eloquent: Detect is empty or count
(4 answers)
Closed 10 months ago.
In my laravel application some of the users can have a assigned company and some don't have a assigned company.
But both type of users can update their details to the system.
If it is a user who has a company, then I'm checking his company id is a valid one or not.
If the user doesn't have an assigned company, I don't need to check that..
In order to do that I have following in my controller,
$companies = $user->companies;
This $companies will basically contains an collection like below,
Illuminate\Database\Eloquent\Collection {#2770
#items: array:1 [
0 => App\Company {#2778
#connection: "mysql"
#table: "companies"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:25 [
"id" => 635
"uuid" => "283c18b514"
"name" => "apple"
"email" => "ceo#apple.co"
"phone_number" => "6149297830"
"website" => "https://www.apple.co"
"country_id" => 232
"state" => "California"
"postal_code" => ""
"house_number" => ""
"street" => ""
"city" => "Menlo Park"
"chamber_of_commerce" => null
"vat_number" => null
"lat" => 37.4529598
....
Now I'm trying run some codes if this $companies is NOT empty. which means, the user has already assigned company.
if(!empty($companies)){
$getAuthUser_CompanyId = $companies[0]->id;
$employeeID_ExistsCompany= CompanyUser::whereExists(function ($query) use ($getAuthUser_CompanyId,$employee_Id) {
$query->select(DB::raw(1))
->from('users')
->where('company_user.user_id', '=',$employee_Id)
->where('company_id', '=', $getAuthUser_CompanyId);
})
->get();
if(!$employeeID_ExistsCompany)
abort(403);
}
Now the issue is,
When I try to run this It kept giving me an error saying,
message: "Undefined array key 0"
How can I properly check if this $companies not empty? Where should I fix?
Update
Tried using
if($companies->isNotEmpty())
Since am using laravel 9, but still getting the same exception...
if($companies->isNotEmpty())
{
$getAuthUser_CompanyId = $companies->first()->id;
$employeeID_ExistsCompany= CompanyUser::whereExists(function ($query) use ($getAuthUser_CompanyId,$employee_Id) {
$query->select(DB::raw(1))
->from('users')
->where('company_user.user_id', '=',$employee_Id)
->where('company_id', '=', $getAuthUser_CompanyId);
})
->get();
if(!$employeeID_ExistsCompany)
abort(403);
}
you can directly use count() of the collection class.
if($companies->count() > 0)
Use below code, when the relation is one-to-one
if($companies)
You can do it this way
https://laravel.com/docs/9.x/collections#method-isnotempty
$companies = $user->companies;
if($companies->isNotEmpty()){ // check if records exists
$getAuthUser_CompanyId = $companies->first()->id; // also update this line
//Logic
}
That is because a Collection is not an array.
getAuthUser_CompanyId = $companies->first()->id;

Laravel collection pluck dropping a value

I have the following Eloquent query in a Laravel 5.2 project:
$regsByCtryCollection = Organisation::join('countries_currencies', 'countries_currencies.id', '=', 'organisations.country_id')
->select(DB::raw('DISTINCT LCASE(countries_currencies.country_code) AS ctry, COUNT(organisations.id) AS val'))
->groupBy('ctry')
->get();
The raw query produces this output:
ctry val
at 1
au 5
br 1
The Eloquent call produces a collection of three rows (matching raw query output) like this:
Collection {#791 ▼
#items: array:3 [▼
0 => Organisation {#777 ▼
#table: "organisations"
#hidden: []
........
#attributes: array:2 [▶]
#original: array:2 [▼
"ctry" => "at"
"val" => 1
]
#relations: array:5 [▶]
........
}
1 => Organisation {#778 ▶}
2 => Organisation {#779 ▶}
]
}
I then pluck the values and format for Highmaps like this
$regsByCtry = $regsByCtryCollection->pluck('ctry', 'val')->map(function($country, $value) {
return [
"hc-key" => $country,
"value" => $value
];
})->values()->toJson();
And one of the values is dropped and I get this:
[
{"hc-key":"br","value":1},
{"hc-key":"au","value":5}
]
Why is the first entry getting dropped?
{"hc-key":"at","value":1}
I am using this same process with two other Eloquent queries and it works as expected, but just not on this collection.
Additionally, I also sum all the values in the array of objects like this:
$regsTotal = array_sum($regsByCtryCollection->pluck('val')->toArray());
And I get the correct value, including all three records summed:
$regsTotal = 7;
The issue is with pluck('ctry', 'val'). This will return val as key & ctry as value. In your query output at & br has same value 1. So one of it getting replaced by the other one.
Try pluck('val', 'ctry')->map(function($value, $country)
Reference

Laravel eloquent query with

This query returns all groups with an id however it also returns all the products to every group.
$groups = \App\Group::where('campaign_id', $id)->with('product')->get();
dd($groups->toArray());array:2 [▼
This is the output.
0 => array:6 [▼
"id" => 24
"campaign_id" => "TRE36934"
"group_name" => "group2"
"created_at" => "2017-05-17 16:14:55"
"updated_at" => null
"product" => array:4 [▼
0 => array:8 [▶]
1 => array:8 [▶]
2 => array:8 [▶]
3 => array:8 [▶]
I am trying to return the groups with same id. Can I somehow query? The join in the id in the groups table and the foreign key in call 'group' in the group table.
A simple inner join should suffice here, right?
$groups = \App\Group::join('product', 'group.campaign_id', '=', 'product.group')
->where('group.campaign_id', $id)
->get();
try :
$groups = Group::join('product', 'group.campaign_id', '=', 'product.group')
->where('group.campaign_id', $id)
->get();
If you want to get your product with the same campaing id, you can do the following :
$products = Product::get()->groupBy('campaign_id');
Or you can do it directly from SQL
So, if you want a collection of Product, use your product model :)
I'm not sure but you may try this:
$group = \App\Group::where('campaign_id', $id)->get();
$group->load('products');
Thanks for all your replies they all got me a bit cloer
$products = \App\Product::join('groups', 'groups.id', '=', 'products.group')->get()->groupBy('group');
The basic groupby by Mathieu Ferre got me close. I added a join with a groupby as suggested by Mozammil on the name to give me
$products = \App\Product::join('groups', 'groups.id', '=', 'products.group')->get()->groupBy('group_name');

How can I select certain columns from my recursive child call

I'm pretty new to Eloquent and I'm having issues wrapping my head around something.
Basically I have a table which I'm recursively grabbing children from within the same table.
public function children() {
return $this->hasMany(static::class, 'parent_org_id');
}
public function childrenRec()
{
return $this->children()->with('childrenRec');
}
Where childrenRec is a recursive call to all children based on 'parent_org_id'
I'm calling it from the following in a static function, as of right now I only want the id and the name_en of the org
self::select('id','name_en')->where('parent_org_id','=',0)->with('childrenRec')->get()->toArray();
which is grabbing the top level org (my top level org has a parent_org_id of 0).
My issue is that in the recursively grabbed children it doesn't limit it to the id and the name_en
My question boils down to:
How can I select only certain columns from my recursive child calls, as well is this the 'proper' way of doing things?
My returned array looks like this.
array:1 [▼
0 => array:4 [▼
"id" => 1
"name_en" => "Org Unit"
"org_type" => null
"children_rec" => array:2 [▼
0 => array:27 [▼
"id" => 2
"name_en" => "My First Orgunit."
"code" => null
"abbreviation" => null
"address1" => "222 Street Street"
"address2" => null
"city_id" => 1
"province_id" => 14
"postalcode" => "C161L7"
"country_id" => 38
"contact_name" => null
"contact_title" => null
"email" => "test#test.com"
"fax" => "902-555-5555"
"phone1" => "5125125125125"
"phone2" => null
"org_type_id" => 1
"parent_org_id" => 1
"ref_id" => 79
"has_users" => 1
"created_at" => "2016-11-02 18:47:55"
"updated_at" => "2016-11-02 18:47:55"
"org_type" => array:4 [▶]
"children_rec" => array:1 [▶]
]
1 => array:27 [▶]
]
]
]
Thanks in advance.
To access the relation query in the with() method you use an array with the name of the relationship as the key and a closure with an instance of QueryBuilder injected.
One 'gotcha' that took me forever to track down a solution to when doing this is, your parent and children queries need to include the key that associates their relationship because the models are attached/associated with each other after both queries are run separately. Those models are associated with each other using the columns defined in the relation on the model. Without the columns used in the association of the models in your query, the related models won't be attached. In your situation it would be:
self::select('id','name_en')
->where('parent_org_id','=',0)
->with(['childrenRec' => function($query) {
return $query->select('id', 'name_en', 'parent_org_id');
}])
->get()
->toArray();
If you don't include the parent_org_id in the subquery the relationships won't get attached.
Docs
Try this:
public function childrenRec()
{
return $this->children()->with(['childrenRec' => function($query){
$query->select('id','name_en');
}]);
}

Manually add item to existing object [Laravel 5]

Here is what I try to do:
$q = Question::where('id',$id -> id)->get();
$q[] = $q->push([ 'test' => true]);
dd($q);
This will output:
Collection {#220 ▼
#items: array:3 [▼
0 => Question {#225 ▶}
1 => array:1 [▼
"test" => true
]
2 => null
]
}
So 'test' => true will append as a new key, but I want to insert it in Question so latter I can access to it like this with foreach $q -> test
So here is how I want access to item:
#foreach($q as $qq)
{{ $qq->test }}
#endforeach
It can be done by using setAttribute() function of Eloquent Model (https://github.com/illuminate/database/blob/master/Eloquent/Model.php).
As You can see it stores data in protected $attributes using setAttribute(), and when we do $SomeModel->some_field it uses magic method __get() to retrieve item by association from attributes array.
Here is the resolution to Your question:
$Question = Question::find($id);
$Question->setAttribute('test', 'blablabla');
Apart from setAttribute(), you can use put() refer to this post for one item. And map() for many items, refer to this post.

Categories