Laravel - Getting count of nested relationship - php

I have an application where users can control their properties and leases.
These are the relationships defined:
//Team.php
/**
* A team can have many properties.
*/
public function properties():
{
return $this->hasMany(Property::class);
}
//Property.php
/**
* A property can have many leases.
*/
public function leases():
{
return $this->hasMany(Lease::class);
}
As you can see here, a Team can have many properties, and each property can also have many leases.
I am trying to figure out how I can get the number of leases that is associated with a Team:
$team = Auth::user()->currentTeam;
return $team->properties->leases->count();
However, the above code returns an error:
Property [leases] does not exist on this collection instance.

You could add this method to your Team.php:
public function leasesCount(){
return count(
Team::
join('properties', 'properties.team_id', '=', 'teams.id')
->join('leases', 'leases.property_id', '=', 'properties.id')
->where('teams.id', $this->id)
->get()
);
}

I ended up using a hasManyThrough relationship on the Team model, like so:
//Team.php
/**
* Get all of the leases for the team.
*/
public function leases()
{
return $this->hasManyThrough(Lease::class, Property::class);
}
Then I can simply get the number of leases created by a specific team by:
return $team->leases()->count();

Related

Laravel Model recursion supress infinite loop

Having the following Laravel recursion in my model how do I avoid accidentally registered relation. What I mean: I have users and users can have many reportees and we want to get back the tree on a given user with the following snippet, which is works fine till the point if user is not a reportee to itself
/**
* User can have many Reporters
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function hasreporters()
{
return $this->belongsToMany('App\User', 'reporting_to', 'acc_receiving_from_id', 'acc_reporting_to_id')
->where('status', 'A');
}
/**
* #return $this
*/
public function children()
{
return $this->hasreporters()->with(['children']);
}
where can I check in the loop if a given id is not equal to the parent id
In function children() you are use relation as query builder. Its not wrong way, but method->with()` search relation from its argument, then call it. Here is recursion :) Try:
public function children()
{
return $this->hasreporters();
}

Laravel 5.3 get belongsToMany and count pivot

I have next models:
class Polling extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function participants()
{
return $this->belongsToMany(Participant::class, 'participant_poll', 'poll_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function results()
{
return $this->belongsToMany(Participant::class, 'poll_results', 'poll_id');
}
}
class Participant extends Model
{
public function polls()
{
return $this->belongsToMany(Polling::class);
}
public function results()
{
return $this->belongsToMany(Polling::class);
}
}
poll_results - pivot table have structure: id, poll_id, participant_id.
I need view next table:
№|participant.name|Count vote|
1|Mike |15 |
2|................|10 |
..............................
Count vote get pivot table poll_results.
Help please, write query.
$poll = Polling::first();
$poll->participants()->get();
You may want to use withCount() method.
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models
Your query would look like this one:
Participant::withCount('polls')->get();
This will add new property to results called polls_count

Laravel whereHas count on Many-to-Many relationship

In my current project, a User may join many Organisations and vice versa - An example of a many to many relationship. I'm trying to count the number of users who are currently unverified (where the Verified column on the user table is equal to 0).
My User model:
/**
* Get the organisations that the user is a part of.
*/
public function organisation()
{
return $this->belongsToMany(
Organisation::class, 'organisation_users', 'user_id', 'organisation_id'
)->withPivot(['role'])->orderBy('name', 'asc');
}
My Organisation model:
/**
* Get all of the users that belong to the organisation.
*/
public function users()
{
return $this->belongsToMany(
User::class, 'organisation_users', 'organisation_id', 'user_id'
)->withPivot('role');
}
So if I want to count the number of unverified users I have the following method on the Organisation model:
/**
* An organisation may have unverified users attached.
*/
public function unverifiedUsers()
{
return $this->whereHas('users', function($query) {
$query->where('verified', 0);
})->get();
}
However, running dd(\App\Organisation::find($org->id)->unverifiedUsers()->count()); only shows 1 when in fact there should be 10. Am I structuring my relationships incorrectly?
whereHas() will return 0 or 1. It just tells you if such a user exists.
The sollution is much simpler:
public function unverifiedUsers()
{
return $this->users()->where('verified', 0)->get();
}
If you only need the count:
public function unverifiedUsersCount()
{
return $this->users()->where('verified', 0)->count();
}

OrderBy on Laravel polymorph one-to-one relationship

I have problem to sort results by polymorph's table relationship.
Tried many ways, but results does not sort.
I have looked in many threads, and final result code that I have tried looks like this:
return \App\Models\Advert::with(['advertable' => function ($query) {
$query->orderBy('rooms', 'DESC');
}])->get();
I want to sort all rows in "adverts" table by relationship's "room" column, but nothing happens.
My tables structure is:
Laravel models looks like this:
<?php
// Main "adverts" polymorph table with advertable_id and advertable_type
class Advert extends \Illuminate\Database\Eloquent\Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function advertable()
{
return $this->morphTo();
}
}
// "advert_flats" table
class Flat extends \Illuminate\Database\Eloquent\Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function advert()
{
return $this->morphOne(\App\Models\Advert::class, 'advertable');
}
}
// "advert_homes" table
class Home extends \Illuminate\Database\Eloquent\Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function advert()
{
return $this->morphOne(\App\Models\Advert::class, 'advertable');
}
}
UPDATE
If I dump SQL, then I see that this code does not event run into relationship
select * from `adverts` where `category_id` = 2 and `adverts`.`deleted_at` is null
Also I have tried to change whereHas function to work with polymorph relationships, as Laravel does not support whereHas with polymorph relations.
I have ran this code
return $model->whereHas('advertable', function ($q) {
$q->orderBy('rooms', 'DESC');
}, '>=', 1, ['\App\Models\Flat']);
But it also does not sort results.
I had to change Laravel builder to add support for polymorph tables.
UPDATE 2
I have resolved issue and implemented code by using example provided by #Sturm answer, after applying SortBy / SortByDesc I'm manually building LengthAwarePaginator.
But could this issue be resolved using Builder class, before calling ->get() method on my query ?
$adverts = \App\Models\Advert::query()->select(
DB::raw('
adverts.*,
CASE
WHEN adverts.advertable_type like(\'%Flat\') THEN advert_flats.rooms
WHEN adverts.advertable_type like(\'%Home\') THEN advert_homes.rooms
END
as rooms
')
)->leftJoin(
"advert_flats",
function ($join) {
/** #var \Illuminate\Database\Query\JoinClause $join */
$join->on('adverts.advertable_id', '=', "advert_flats.id")
->where('adverts.advertable_type', 'like', '%Flat');
}
)->leftJoin(
"advert_homes",
function ($join) {
/** #var \Illuminate\Database\Query\JoinClause $join */
$join->on('adverts.advertable_id', '=', "advert_homes.id")
->where('adverts.advertable_type', 'like', '%Home');
}
)->orderBy('rooms', 'ASC')->get();
Here's something that will get you running, although I'll look for a moment more to see if there's a way to do it from Builder.
$sorted = $adverts->sortBy(function ($val, $key) {
return $val->advertable->rooms;
}

Yii 2: Unable to create a relation with a condition about the related table

Simplifing, I've two tables:
Product: id, name
Datasheet: id, product_id
Where product_id points to products.id. Each Product could have 0 or 1 Datasheet.
Into my Product class (wich extends ActiveQuery) I've created this relation
/**
* #return \yii\db\ActiveQuery
*/
public function getDatasheet()
{
return $this->hasOne(Datasheet::className(), ['product_id' => 'id']);
}
I'm able now to query in this way
$products_without_datasheet = Product::find()
->with('datasheet')
->all();
What I really need is to retrieve only the products without the datasheet.
I'd like to create a 'scope' (like in yii 1) to be able to reuse the resulting condition datasheet.id IS NULL because this situation has a lot of variants and will be used all around the app.
I'm not able to understand how to create a relation with an added filter, something like getWithoutDatasheet() to be used as
Product::find()->with('withoutDatasheet')->all();
or
Product::find()->withoutDatasheet()->all();
Is it possible? And how?
You need create ActiveQuery for Product. See how gii generated ActiveRecord with ActiveQuery.
In Product:
/**
* #inheritdoc
* #return ProductQuery the active query used by this AR class.
*/
public static function find()
{
return new ProductQuery(get_called_class());
}
In ProductQuery:
public function withoutDatasheet()
{
$this->with('datasheet');
return $this;
}
Usage:
Product::find()->withoutDatasheet()->all();
To retrieve only the products without the datasheet you can do it like this:
$productsWithDatasheet = Datasheet::find()
->select('product_id')
->distinct()
->asArray()
->all();
$productIdsWithDatasheet = ArrayHelper::getColumn($productsWithDatasheet, 'product_id');
$productsWithoutDatasheet = Product::find()
->where(['not in', 'id', $productIdsWithDatasheet ])
->all();

Categories