Laravel sub query not removing results when using a nested with - php

I have a 'user' table that has a pivot table for services that a user offers:
// App\User
public function services()
{
return $this->hasMany('App\ServiceUser');
}
On the ServiceUser model I then have another relationship to get the service information:
public function service()
{
return $this->hasOne('App\Service', 'id');
}
When fetching a team (using Laravel Spark) the query I am using is:
Team::with('users')->withUserCustomerServices()->where('id', $teamId)->first();
The scope for this query is in the Team model:
public function scopeWithCustomerServices($query)
{
$query = $query;
$query->with('users.services');
$query->with(['users.services.service' => function($q) {
$q->where('visible_to_customers', 1);
}]);
return $query;
}
When outputting (using Vue.js):
{{ user.services.length }}
I get (in this example) 6 results returned. However, one of the services has a database field 'visible_to_customers' set to 0.
Initially I thought my query would work as expected and only return 5 services however it actually still returns them all, but doesn't return the relationship (service) if the field is 0.
How can I can I only return the pivot table result where the relationship has a certain field value?
EDIT
I have updated the query to use a whereHas on the first nested relation:
$query->with(['users.services' => function($q) {
$q->whereHas('service', function($q) {
$q->where('visible_to_customers', 1);
});
}]);
This works great, only returns the pivot table rows where the services table has a field value of 1 for visible_to_customers.
However, that doesn't fetch the related row itself.
If I then chain on:
$query->with(['users.services' => function($q) {
$q->whereHas('service', function($q) {
$q->where('visible_to_customers', 1);
});
}]);
$query->with(['users.services.service' => function($q) {
$q->where('visible_to_customers', 1);
}]);
It remains the same issue where it fetch all of the rows but then only the related rows where the field is 1.

Fixed this issue by using a where has on the first relationship that is the pivot:
$query->with(['users.services' => function($q) {
$q->whereHas('service', function($q) {
$q->where('visible_to_customers', 1);
})->with('service');
}]);
I then appended the ->with('service') to the end of the chain.

Related

Datatables: how to preserve id when sorting through a relation from another table (yajra, laravel)

I'm using datatables from yajra datatables and I have a problem.
I have a datatable where I obtain certain columns from other tables, for example the column "customer" is obtained through a relation from another table
But when I press the sort by customer button, the IDs change to the ID of the table where the relation belongs
This generates errors in the buttons that I have on the right since these IDs do not exist in the "meos" table, only in the "locations" table that is in the relation
How can I make it sort while keeping the same ID it had?
I want to always keep as order criteria the ID of the table I am with, which is the "meos" table belonging to the "meo" class
This is my query
public function query()
{
$languageId = Auth::user()->language_id;
return Meo::with(['businessType' => function ($query) use ($languageId) {
$query->with(['businessTypeDescriptions' => function ($subQuery) use ($languageId) {
$subQuery->where('language_id', '=', $languageId);
}]);
}])->with('location');
}
this is my function getcolumns
protected function getColumns()
{
return [
Column::make('id')->addClass('text-center')->title(__('digestReport.columns.id')),
Column::make('location.location_name')->addClass('text-center')->title(__('digestReport.columns.customer')),
Column::make('businessType')->addClass('text-center')->title(__('digestReport.columns.business_type'))->searchable(false),
Column::computed('action')->exportable(false)->printable(false)->width(160)->addClass('text-center')->title(__('digestReport.columns.actions')),
];
}
please, I need help.
Thanks :)
Try below code. Add ->select() statement
public function query()
{
$languageId = Auth::user()->language_id;
return Meo::with(['businessType' => function ($query) use ($languageId) {
$query->with(['businessTypeDescriptions' => function ($subQuery) use ($languageId) {
$subQuery->where('language_id', '=', $languageId);
}]);
}])->with('location')->select('meo-table.*');
}
Replace meo-table with your database table for Meo::class

return if id not null, laravel elequent

I have connected tables with a hasMany & belongsTo relationship.
Bridal Model:
public function plans()
{
return $this->hasMany("App\Plan");
}
Plan Model:
public function bridal()
{
return $this->belongsTo("App\Bridal");
}
And I have an query that returns those data to endpoint.
public function bridal()
{
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])
->groupBy("place_name")
->get();
return $bridals;
}
Everything is fine, except one thing. In Bridal table some of ID doesn't have plan. So when I return datas, some of bridal id comes with an empty Plans array.
I want to prevent that. If a bridal id doesn't have a plan, then I don't want to return that bridal id. How can I achieve what I want here?
You can use Has. If you just use has('relation') that means you only want to get the models that have at least one related model in this relation.
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])->has('plans')
->groupBy("place_name")
->get();
Check more info SO answer
Add whereHas() in your query : so you will get bridal records which has plans
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])
->whereHas('plans')
->groupBy("place_name")
->get();

How to query many to many relation tables in laravel?

i am trying to query many to many relation for my get api call. i have three table as shown here but i am not using pivot table.
This is my Projects model class and this the function
public function projectRewd()
{
return $this
->belongsToMany('App\Rewards','rewards','project_id','reward_id');
}
And this is my Rewards model class and function
public function projectShip()
{
return $this->belongsToMany('App\Shipping_location','shipping_location','projects_id','rewards_id');
}
This is my api controller function
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q)
{
$q->where('id', $id);
});
return $proj;
});
i am using this link for api call
http://localhost:8000/api/projects/1
i want to extract rewards data and shipping_location data associate with project_id.
i am getting this error
"message": "Object of class Illuminate\\Database\\Eloquent\\Builder could not be converted to string"
i check and tried all related error from different post.
i also search and tried many technique. Cant solve my problem.
Please suggest me how to do this??
can i do this type of query in larvel without using pivot table??
You are getting Builder model because you forgot to add ->first() or ->get().
You should write:
$proj = Projects::whereHas('projectRewd', function($q){
$q->where('id', $id);
})->first();
Your closure-based controller returns your query-builder object. Not a project. You need to retrieve results from the query by fetching e.g. the first result (->first()) or all (->get()).
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q)
{
$q->where('id', $id);
})->first();
return $proj;
});
Referencing $id:
The reason why $id is unknown, is that the closure doesn't know about it.
You can pass it to the closure using use(...).
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q) use ($id)
{
...
Further:
Your whereHas query looks incorrect to me:
$q->where('id', $id);
Apparently $id is the project id. But the 'id' column in projectRewd is the primary key of projectRewd (unless you have modified the defaults).
I assume you want to query all projects that have at least one projectRewd:
Route::get('projects/{id}', function($id) {
$proj = Projects::has('projectRewd')->first();
return $proj;
});
And if you want to eager load the joined tables:
Route::get('projects/{id}', function($id) {
$proj = Projects::with('projectRewd. projectShips')->has('projectRewd')->first();
return $proj;
});

Laravel withCount() returns wrong value

In my application there are users making pictures of items. The relationship structure is as follows:
Categories -> SubCategories -> Items -> Pictures -> Users
Now, there's also a shortcut relationship called itemPics between categories <--> pictures so that the number of pictures uploaded by a user can be quickly counted per category using withCount().
These are the relationships on the Category model:
public function subcategories()
{
return $this->hasMany('App\SubCategory');
}
public function items()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory');
}
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory')
->join('pictures','items.id','=','pictures.item_id')
->select('pictures.*');
}
My problem is getting the number of pictures that a user has gathered per category. The itemPics_count column created by withCount() always has the same value as items_count, even though the number of related models for both relations given by with() are different in the JSON output.
$authorPics = Category::with(['SubCategories', 'SubCategories.items' => function ($q) use ($author_id) {
$q->with(['pictures' => function ($q) use ($author_id) {
$q->where('user_id', $author_id);
}]);
}])
->with('itemPics') /* added this to check related models in output */
->withCount(['items','itemPics'])
->get();
dd($authorPics);
This does not make sense to me. Any help is greatly appreciated.
Withcount() function is not work properly if relations include join. it works only table to table relations.
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory')
->select('pictures.*');
}
This solution was worked for me:
//...
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory');
}
Then you can do something like this:
$authorPics = Category::with(['SubCategories', 'SubCategories.items' => function ($q) use ($author_id) {
$q->with(['pictures' => function ($q) use ($author_id) {
$q->where('user_id', $author_id);
}]);
}])
->with('itemPics') /* added this to check related models in output */
->withCount(['items','itemPics' => function($query){
$query->join('pictures','items.id','=','pictures.item_id')
->select('pictures.*');
}])
->get();
dd($authorPics);
Link to more information about Laravel withCount function here https://laravel.com/docs/8.x/eloquent-relationships#counting-related-models

Laravel Multiple Models Eloquent Relationships Setup?

I have 3 models
User
Pick
Schedule
I'm trying to do something like the following
$picksWhereGameStarted = User::find($user->id)
->picks()
->where('week', $currentWeek)
->first()
->schedule()
->where('gameTime', '<', Carbon::now())
->get();
This code only returns one array inside a collection. I want it to return more than 1 array if there is more than 1 result.
Can I substitute ->first() with something else that will allow me to to return more than 1 results.
If not how can I set up my models relationship to allow this to work.
My models are currently set up as follow.
User model
public function picks()
{
return $this->hasMany('App\Pick');
}
Schedule model
public function picks()
{
return $this->hasMany('App\Pick');
}
Pick model
public function user()
{
return $this->belongsTo('App\User');
}
public function schedule()
{
return $this->belongsTo('App\Schedule');
}
Since you already have a User model (you used it inside you find method as $user->id), you can just load its Pick relationship and load those Picks' Schedule as follows:
EDIT:
Assuming you have a schedules table and your picks table has a schedule_id column. Try this.
$user->load(['picks' => function ($q) use ($currentWeek) {
$q->join('schedules', 'picks.schedule_id', '=', 'schedules.id')
->where('schedules.gameTime', '<', Carbon::now()) // or Carbon::now()->format('Y-m-d'). See what works.
->where('picks.week', $currentWeek);
}])->load('picks.schedule');
EDIT: The code above should return the user's picks which have a schedules.gameTime < Carbon::now()
Try it and do a dump of the $user object to see the loaded relationships. That's the Eloquent way you want.
Tip: you may want to do $user->toArray() before you dump $user to see the data better.
EDIT:
The loaded picks will be in a form of Collections so you'll have to access it using a loop. Try the following:
foreach ($user->picks as $pick) {
echo $pick->schedule->gameTime;
}
If you only want the first pick from the user you can do: $user->picks->first()->schedule->gameTime
I think a foreach loop may be what you're looking for:
$picks = User::find($user->id)->picks()->where('week', $currentWeek);
foreach ($picks as $pick){
$pickWhereGameStarted = $pick->schedule()->where('gameTime', '<', Carbon::now())->get();
}
Try this and see if it's working for you

Categories