Laravel Eloquent: Count items model relationship - php

I'm using Laravel and the Eloquent class. I have three models.
City.php:
public function itineraries() {
return $this->has_many('Itinerary', 'city_id');
}
Itinerary.php:
public function city()
return $this->belongs_to('City');
}
public function type()
{
return $this->belongs_to('Itinerarytype');
}
Itinerarytype.php:
public function itineraries()
{
return $this->has_many('Itinerary');
}
As you can see a city has many itineraries and an itinerary belongs to a city and an itinerary type. The itinerarytype model has many itineraries.
Using the with() method is it possible to get a count of itineraries grouped into itinerary type?
For instance here is what I have so far:
$city = City::with(array('itineraries'))->where_slug($city_slug)->first();
This gets the city which has that slug and all of it's itineraries.
I'd like to get a list like: (where the text is the itinerary type and the number is the count)
History: 10
Entertainment: 5
Outdoor: 6
...

You'd have to use join and a bit of raw sql to achieve that on a performant way:
$city = City::where_slug($slug)->first();
$types = ItineraryType::join('itineraries', 'itineraries.type_id', '=', 'itenerary_types.id')
->where('itineraries.city_id', '=', $city->id)
->group_by('itinerary_types.id')
->order_by('itinerary_count')
->get(array('itinerary_types.*', DB::raw('COUNT(itineraries.id) as itinerary_count')));
foreach ($types as $type) {
print($type->label . ': ' . $type->itinerary_count);
}

Related

Call to undefined relationship on model laravel using scope

I'm trying to get 5 posts for each category so I did a little search and ends up here Getting n Posts per category
But I'm getting a weird Call to undefined relationship on model when using with scope but it all works fine If I don't use a scope. Here is the Category Model
//Relationship with posts
public function posts(){
return $this->hasMany('App\Post');
}
scopeNPerGroup
public function scopeNPerGroup($query, $group, $n = 10)
{
// queried table
$table = ($this->getTable());
// initialize MySQL variables inline
$query->from( \DB::raw("(SELECT #rank:=0, #group:=0) as vars, {$table}") );
// if no columns already selected, let's select *
if ( ! $query->getQuery()->columns)
{
$query->select("{$table}.*");
}
// make sure column aliases are unique
$groupAlias = 'group_'.md5(time());
$rankAlias = 'rank_'.md5(time());
// apply mysql variables
$query->addSelect(\DB::raw(
"#rank := IF(#group = {$group}, #rank+1, 1) as {$rankAlias}, #group := {$group} as {$groupAlias}"
));
// make sure first order clause is the group order
$query->getQuery()->orders = (array) $query->getQuery()->orders;
array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']);
// prepare subquery
$subQuery = $query->toSql();
// prepare new main base Query\Builder
$newBase = $this->newQuery()
->from(\DB::raw("({$subQuery}) as {$table}"))
->mergeBindings($query->getQuery())
->where($rankAlias, '<=', $n)
->getQuery();
// replace underlying builder to get rid of previous clauses
$query->setQuery($newBase);
}
Calling Npergroup with relation
public function latestposts()
{
return $this->posts()->latest()->nPerGroup('category_id', 5);
}
Post Model Relationship
//Post belongs to Category
public function category(){
return $this->belongsTo('App\Category');
}
In my category controller I'm calling latestposts through
$categories = Category::with('latestposts')->get();
But I'm getting the error: Call to undefined relationship on model
What I want is:
Get the N number of posts per each category but I'm completely lost at this point. Any help would be appreciated
Reference:
Tweaking Eloquent relations – how to get N related models per parent ?
I am giving this answer based on your purpose that you want 5 posts per category.
So you have Category Model and Post Model.
And in Category Model you have relation with Post model like this
//Relationship with posts
public function posts(){
return $this->hasMany('App\Post');
}
And in Post Model you have relation with Category model like this
//Post belongs to Category
public function category(){
return $this->belongsTo('App\Category');
}
I show your question you have done SQL queries.
Instead of that, You can use two approaches
1) Give condition while eagar loading
$categories = Category::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc')->take(5);
}])->get();
Note: This approach will only work when you take only one result of parent child using first() method.
To get n number of posts per category Use this.
First, you can retrieve all categories with
$categories = Category::all();
Then you can use foreach loop and in all $category you have to give assign new attribute in it like here latestposts,
foreach ($categories as $category)
{
$category->latestposts = $category->posts()->orderBy('created_at','desc')->take(5)->get();
}
After this foreach loop you will get latest 5 posts in all categories.
Try this in your code and comment your queries and reviews.

Laravel: Count number of rows in a relationship

I have the following relationship:
A venue has many offers
A offer has many orders
I have the following Eloquent model to represent this:
class Venue {
public function orders()
{
return $this->hasManyThrough(Order::class, Offer::class);
}
}
I want to determine the total number of orders for venues with location_id = 5 using Laravel's Eloquent model.
The only way I managed to do this is as follows:
$venues = Venue::where('location_id', 5)->with('orders')->get();
$numberOfOrders = 0;
foreach($venues as $venue) {
$numberOfOrders += $venue->orders->count();
}
dump($numberOfOrders); // Output a single number (e.g. 512)
However, this is obviously not very efficient as I am calculating the count using PHP instead of SQL.
How can I do this using Eloquent model alone.
You can use Eloquent. As of Laravel 5.3 there is withCount().
In your case you will have
$venues = Venue::where('location_id', 5)->with('orders')->withCount('orders')->get();
Then access it this way
foreach ($venues as $venue) {
echo $venue->orders_count;
}
Can find reference here: https://laravel.com/docs/5.3/eloquent-relationships#querying-relations
$venues = Venue::with([
'orders' => function ($q) {
$q->withCount('orders');
}
])->get();
then use it this way for getting single record
$venues->first()->orders->orders_count();
Alternatively, you can use this way too for collections
foreach($venues as $venue)
{
echo $venue->order_count;
}
If you are using Laravel 5.3 or above you can use withCount.
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. For example:
$venues = Venue::withCount(['orders'])->get;
foreach ($venues as $venue) {
echo $venue->orders_count;
}
You can read more about withCount in the Laravel Documentation.
If you are using lower than 5.3, you can make a custom relation on your Venue model:
public function ordersCount()
{
return $this->belongsToMany('App\Models\Order')
->selectRaw('venue_id, count(*) as aggregate_orders')
->groupBy('venue_id');
}
public function getOrderCount()
{
// if relation is not loaded already, let's do it first
if (!array_key_exists('ordersCount', $this->relations)) {
$this->load('ordersCount');
}
$related = $this->getRelation('ordersCount')->first();
// then return the count directly
return ($related) ? (int) $related->aggregate_orders : 0;
}
which can then be used as: Venue::with('ordersCount');. The benefit of this custom relation is you only are querying the count rather than the querying all of those relations when they are not necessary.

Laravel Eloquent using "with" with conditions

I have two tables, say Products and Biddings where one product can be bid by many users. Naturally I have two models:
class Product extends Model
{
public function biddings()
{
return $this->hasMany('App\Bidding');
}
}
class Bidding extends Model
{
public function product()
{
return $this->belongsTo('App\Product');
}
}
So, say I want to get all products along with the highest priced bidding I did something like this.
$productBidding = DB::table('biddings')
->select('*', DB::raw('max(price) as price'))
->join('products', 'products.id', '=', 'biddings.product_id')
->groupBy('product_id')
->get();
That works well BUT I kinda want to do it Eloquent way. So how do I convert Query Builder way to Eloquent? I am currently on this but do not know how to put the "max" condition in.
$productBidding = Products::with('biddings')
->get();
$productbinding=Bidding:with('product')
->get();
foreach($productbinding as $productbind)
{
echo $productbind->product->name; // example
}
I would extract the highest bid to a separate function on the Product model, like so:
public function highestBid() {
return $this->biddings()->max('price');
}
Then fetch the products and get the highest bid:
$products = Product::get();
foreach ($products AS $product) {
echo $product->highestBid();
}

Laravel Datatables Sorting Appended Fields

My Model Items is related to Rooms, which is related to Buildings, which is related to Locations.
Short: Items belongsTo Rooms belongsTo Buildings belongsTo Locations
At the index function of the ItemController I want to show a table of Items. I use Laravel Datatables. It works for 'simple' tables, but I ran into the problem of sorting/searching custom/appended fields, because they are not in the table of course.
Basically I want to join the Room, Building and Location Name for each Item in the displaying table.
This is my Items Model:
protected $appends = [
'building_title',
'location_title',
'room_title',
];
public function getLocationTitleAttribute() {
return $this->room->building->location->title;
}
public function getBuildingTitleAttribute() {
return $this->room->building->title;
}
public function getRoomTitleAttribute() {
return $this->room->title;
}
This is my Controller:
public function anyData()
{
return Datatables::of(Item::query())->make(true);
}
What is the best approach to enable sorting/filtering for appended fields?
Thank you for reading.
This is my solution (no appended fields were used):
public function anyData()
{
$items = Item::join('rooms', 'items.room_id', '=', 'rooms.id')
->join('buildings', 'rooms.building_id', '=', 'buildings.id')
->join('locations', 'buildings.location_id', '=', 'locations.id')
->select([
'items.id',
'items.title',
'items.label_number',
'items.fibu_number',
'rooms.title as room_title',
'buildings.title as building_title',
'locations.title as location_title'
]);
return Datatables::of($items)->make(true);
}

3 models in laravel 4 - select with relation whereHas Query

I have 3 models:
User
Planet
Sunsystem
Relations:
In the User Model:
public function planets() {
return $this->hasMany('Planet');
}
In the Planet Model:
public function sunsystem() {
return $this->belongsTo('Sunsystem');
}
public function user() {
return $this->belongsTo('User');
}
And in the sunsystem Model:
public function planets() {
return $this->hasMany('Planet');
}
Now I want select all the sunsystems with all the related planets
but only the sunsystems where the related planets belongs to the actual user (in this example ID 12).
But how did I get the correct result?
This is what I try, but it gives me only ONE sunsystem, but I expect 2 sunsystem. I think my query is wrong...:
$s = Sunsystem::whereHas('Planets', function($q) {
$q->whereHas('User', function ($u) {
$u->whereUserId(12);
});
})->get();
This also doesn't work:
$s = Sunsystem::select('Sunsystems.*')
->join('planets','planets.sunsystem_id','=','sunsystems.id')
->join('users','users.id','=','planets.user_id')
->where('users.id','=',12)
->get();
If there are 2 planets with different sunsystem_id but the same user_id I get only one sunsystem, but I expect two.
Please Try it
$s = Sunsystem::select('Sunsystems.*')
->join('planets','planets.sunsystem_id','=','Sunsystems.id')
->join('users','users.id','=','planets.user_id')
->where('users.id','=',12)
->get();

Categories