Laravel Eloquent 'withCount' function on multiple joined tables - laravel

I have multiple hasMany relations where I want the row count only for the last table.
Users can register for workshops at a specific date and time, so the relations look like 'Workshop' -> hasMany 'Date' -> hasMany 'TimeSlot' -> hasMany 'Registration'.
//Workshop Model
public function workshop_dates(){
return $this->hasMany(WorkshopDate::class);
}
//Date Model
public function workshops(){
return $this->belongsTo(Workshop::class);
}
public function workshop_times(){
return $this->hasMany(WorkshopTime::class);
}
//Time Model
public function workshop_dates(){
return $this->belongsTo(WorkshopDate::class);
}
public function registrations(){
return $this->hasMany(Registration::class);
}
//Registration Model
public function workshop_times(){
return $this->belongsTo(WorkshopTime::class, 'workshop_time_id','id');
}
In my controller I fetch all workshops with all related dates and timelots, but I don't want the user to download all the registration data. I only want them to download the number of registrations for each timeslot.
I tried with the 'withCount' function:
return Workshop::with('workshop_dates.workshop_times.registrations')->withCount('registrations')
But this gave me the error 'Call to undefined method App\Workshop::registrations()'
So I created this method in Workshop.php, with the extended version of hasManyThrough:
public function registrations(){
return $this->hasManyDeep(Registration::class, [WorkshopDate::class,WorkshopTime::class]);
}
However, now I get the total number of registrations per workshop in stead of per timeslot.
I'm a laravel beginner and searching for a solution for many hours. All help is appreciated!

You can add the withCount() on relationship with a closure like this:
Workshop::with(['workshop_dates.workshop_times' => function($q) {
$q->withCount('registrations');
}, 'workshop_dates.workshop_times.registrations'])
->get()
Otherwise, something like this approach https://stackoverflow.com/a/41166325/4705339

Related

Querying on a pivot table using Model::query in Livewire

I have been using this table package lately, MedicOneSystems/livewire-datatables and I cannot seem to grab information from pivot tables.
public function builder()
{
return $this->model::query();
}
This is in their base livewire controller, then you can create your own controller by using some of the functions they offer you.
My problem is that once I enter a workbook(Workbook) I need to get the stations that are stored in a pivot table(StationWorkbook) and then go further and take the station(Station) names and everything else from the station table. I am able to get either all the stations, or just the pivot or just the workbook, but I cannot seem to be able to go through them
This is what I have tried so far in my controller.
public function builder()
{
return $this->current_workbook->stations->query();
}
Method Illuminate\Database\Eloquent\Collection::query does not exist
public function builder()
{
return Station::query();
}
public function builder()
{
return StationWorkbook::query();
}
public function builder()
{
return Workbook::query();
}
public function builder()
{
return Workbook::query()->where('id', $this->workbook)->stations;
}
Property [stations] does not exist on the Eloquent builder instance.
None of these was able to return the stations of the workbook.
You are trying to access the collection stations on the Eloquent QueryBuilder - that won't work until after you use get(), which you cannot do with this package, since it expects a QueryBuilder instance.
I'm not entirely sure what you need here, but if you need the station that belongs to that Workbook, then you need to return the querybuilder of the Stations model.
If we assume that you have defined a workbook relation on your Station model, then you can use whereHas() - which can be called on the querybuilder - to limit the data you need. I'm assuming that $this->workbook is the ID, and not the full instance of the model (if its the model, you need to use $this->workbook->id).
public function builder()
{
return Station::whereHas('workbook', function($query) {
return $query->where('id', $this->workbook);
});
}
You can also eager-load the workbook in that query by adding with(). Eager-loading means that Laravel will retrieve the stations joined with the workbook, so that you can access the data of both without extra queries required.
public function builder()
{
return Station::with('workbook')->whereHas('workbook', function($query) {
return $query->where('id', $this->workbook);
});
}

How to define multiple belongsTo in laravel

My table has many foreign key for example prefecture_id, gender_id and status_id.
And I made model for those table.
So I want to define multiple belongsTo method like following for get all data with query builder..
But In fact belongsTo can't use like this.
public function foreign(){
return $this->belongsTo([
'App/Prefecture',
'App/Gender',
'App/Status',
]
}
And if the only way is defining multiple method for belongs to.
How do I get all belongstos data in querybuilder.
Please give me advice.
As far as I am aware, there's not a way to get multiple belongsTo from a single method. What you have to do is make one method for each relationship and when you want to load the relationships you can do the following.
Model
public function prefecture()
{
return $this->belongsTo(\App\Prefecture::class);
}
public function gender()
{
return $this->belongsTo(\App\Gender::class);
}
public function status()
{
return $this->belongsTo(\App\Status::class);
}
Query
// This will get your model with all of the belongs to relationships.
$results = Model::query()->with(['prefecture', 'gender', 'status'])->get();

Laravel collection returning duplicate

Hi I am trying to return a Users Schedule where greater than or equal to todays date, or if that schedule contains a relation (ScheduledYard) that is not complete:
$schedules = Auth::user()
->schedules()
->where('due_at', '>=' , Carbon::now())
->orWhereHas('scheduledYards', function ($query) {
$query->where('completed', false);
})->get();
$schedules = $schedules->sortBy('due_at');
return ScheduleResource::collection($schedules);
This works perfectly fine, but for some reason it is duplicating this for each user it is attached to. i.e if 3 users have this schedule then it is returned 3 times for the Authenticated user.
User Model:
public function schedules()
{
return $this->belongsToMany('App\Models\Schedule');
}
Schedule Model:
public function users() {
return $this->belongsToMany('App\Models\User');
}
If I remove the orWhereHas() it will return a single object. What am I doing wrong here and how can I return a single object based on the requirements above?
In Schedule Model, you need to change the relationship type. i assuming its one to many relationship between users and schedules. in that case change users() to user() and method body as below.
public function user() {
return $this->belongsTo('App\Models\User');
}
In User model the correct relationship should be defined as:
public function schedules()
{
return $this->belongsTo('App\Models\Schedule');
}
Please try with distinct. For example,
public function schedules()
{
return $this->belongsToMany('App\Models\Schedule')->**distinct**();
}

Laravel eloquent not calling the relationship (returning string)

I have this relationship:
A store may have many products and each products can have many effects
stores and products -> many to many relationship
products and effects -> one to many (one product can have multiple effects)
Here are my relationships:
Store model
public function products()
{
return $this->belongsToMany('App\Models\Product');
}
Product model
public function stores()
{
return $this->belongsToMany('App\Models\Store');
}
public function effects()
{
return $this->hasMany('App\Models\Effect');
}
Effect model
public function products()
{
return $this->belongsTo('App\Models\Product');
}
Now, I can access the relationship with products ok, but when I access the effects relationships:
$store = Store::where('slug', 'cantina')->first();
dd($store->products->first()->effects);
The dd returns a string with the name of the relationship:
"effects"
I never seen this "error" before, anyone knows why the relationship are returning a string instead of the eloquent?
[Edit]
If I digit anything in the relationship call, they output what I digited:
dd($store->products->first()->effects);
//Output:
"effects"
Another example:
dd($store->products->first()->foobar);
//Output:
"foobar"

I can't make some Eloquent relations to work

Let me first explain the relations of my table, and then I will explain what I cannot do.
So, I have an entity called "Company" it has many "Incomes" which has many "IncomeUnitSale" which has one "IncomeUnitSaleUnitPrice"
Company 1->* Income 1->* IncomeUnitSale 1->1 IncomeUnitSaleUnitPrice
Models:
Company Model
public function Income(){
return $this->hasMany('App\Income');
}
Income Model (table has "company_id")
public function Company(){
return $this->belongsTo('App\Company');
}
public function IncomeUnitSale(){
return $this->hasMany('App\IncomeUnitSale');
}
IncomeUnitSale Model (table has "income_id")
public function Income(){
return $this->belongsTo('App\Income');
}
public function IncomeUnitSaleUnitPrice(){
return $this->hasOne('App\IncomeUnitSaleUnitPrice');
}
IncomeUnitSaleUnitPrice (table has "income_unit_sale_id")
public function IncomeUnitSale(){
return $this->belongsTo('App\IncomeUnitSale');
}
What I am trying to do is the following:
$company = Company::where("id","=",1)->first();
$company->Income->IncomeUnitSale->IncomeUnitSaleUnitPrice
But It says its null, it works till $company->Income->IncomeUnitSale but after that doest't show any relation.
Can somebody please tell me what I am doing wrong?
Thank you!
hasMany relationships will always return an Eloquent Collection object. If there are no related records, it will just be an empty Collection. hasOne and belongsTo relationships will always return either the related Model, or null if there is no related model.
Because some of your relationships are hasMany, you have to iterate the returned Collection to be able to go further. So, instead of $company->Income->IncomeUnitSale->IncomeUnitSaleUnitPrice, you have to:
foreach($company->Income as $income) {
foreach($income->IncomeUnitSale as $sale) {
echo $sale->IncomeUnitSaleUnitPrice->price;
}
}

Resources