Getting count from pivot table in laravel eloquent - php

I have a many to many relationship for orders and products.
<?php
class Order extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
public function products()
{
return $this->belongsToMany('Product');
}
}
?>
<?php
class Product extends Eloquent {
public function orders()
{
return $this->belongsToMany('Order');
}
}
?>
Need to fetch the number of times each product is ordered.In mysql,this task can be achieved by using the following query
SELECT products.id, products.description, count( products.id )
FROM products
INNER JOIN order_product ON products.id = order_product.product_id
INNER JOIN orders ON orders.id = order_product.order_id
GROUP BY product_id
LIMIT 0 , 30
Result of the above query is as follows:-
id description count(products.id)
1 Shoes 3
2 Bag 2
3 Sun glasses 2
4 Shirt 2
How this task can be achieved using laravel eloquent (without using query builder)????How can i fetch the number of times each product is ordered using laravel eloquent??

For future viewers, as of Laravel 5.2, there is native functionality for counting relationships without loading them, without involving your resource model or accessors -
In the context of the example in the approved answer, you would place in your controller:
$products = Product::withCount('orders')->get();
Now, when you iterate through $products on your view, there is a orders_count (or, generically, just a {resource}_count) column on each retrieved product record, which you can simply display as you would any other column value:
#foreach($products as $product)
{{ $product->orders_count }}
#endforeach
This method produces 2 fewer database queries than the approved method for the same result, and the only model involvement is ensuring your relationships are set up correctly. If you're using L5.2+ at this point, I would use this solution instead.

Mind that Eloquent uses Query\Builder under the hood, so there is no such thing in Laravel, like 'query eloquent without using query builder'.
And this is what you need:
// additional helper relation for the count
public function ordersCount()
{
return $this->belongsToMany('Order')
->selectRaw('count(orders.id) as aggregate')
->groupBy('pivot_product_id');
}
// accessor for easier fetching the count
public function getOrdersCountAttribute()
{
if ( ! array_key_exists('ordersCount', $this->relations)) $this->load('ordersCount');
$related = $this->getRelation('ordersCount')->first();
return ($related) ? $related->aggregate : 0;
}
This will let you take advantage of eager loading:
$products = Product::with('ordersCount')->get();
// then for each product you can call it like this
$products->first()->ordersCount; // thanks to the accessor
Read more about Eloquent accessors & mutators,
and about dynamic properties, of which behaviour the above accessor mimics.
Of course you could use simple joins to get exactly the same query like in you example.

If you already have the $products object, you can do the following:
$rolecount = $products->roles()->count();
Or if you are using eager loading:
$rolecount = $products->roles->count();
Cheers.

I am using Laravel 5.1 and i am able to accomplish that by doing this
$photo->posts->count()
And the posts method in Photo model looks like this
public function posts(){
return $this->belongsToMany('App\Models\Posts\Post', 'post_photos');
}

Related

how to load nested relationships in laravel

i have 3 models that i want to start from a and load the relation with b and i want to load the relation betwen b and c there in a too is that possible ?? here is what i want to do in code :
AccommodationRoomModel which is the B model:
public function accommodation(){
return $this->belongsTo(Accommodation::class);
}
public function roomPricingHistory(){
return $this->hasMany(RoomPricingHistory::class);
}
and in The accomodation model :
public function accommodationRoom()
{
return $this->Hasmany(AccommodationRoom::class);
}
and finaly in the room pricingHistory :
public function accommodationRoom(){
return $this->belongsTo(AccommodationRoom::class);
}
now in my accomodation controller i want to get All the accomodation with the room and from room i want to get the price so here is it
A = Accomodation
B = Room
C = price
and i want to call somehow like this
From A get B and The relation Of it with C and show all in A
You can use Laravels nested eager loading for this:
From the docs:
To eager load nested relationships, you may use "dot" syntax. For
example, let's eager load all of the book's authors and all of the
author's personal contacts in one Eloquent statement:
$books = App\Book::with('author.contacts')->get();
In your case:
$accomodations = Accomodation::with('accommodationRoom.roomPricingHistory')->get();
nested-eager-loading
A::with('B.C');

Laravel: Is there a way to remove DB query clauses?

Let us say my shop model has a products relationship as follows:
// shop model class
public function products()
{
return $this->hasMany(Model\Product::class)
->orderBy('name');
}
Is there a way that client code, such as my controller, can remove the orderBy() clause?
Yes there is a way to do that, say in your controller you want to fetch some of the products
\App\Products::latest()->getQuery();
getQuery() is a query builder method that contains all the groupings, selects, orders, wheres, joins etc for the query that you are accessing or trying to build.
So you could reset the ordering like so:
\App\Products::latest()->getQuery()->orders= [];
since in Laravel eloquent query builder selects, grouping, ordering are stored as arrays of key values you simply set that to an empty array to reset al the previous states.
For example grouping:
\App\Products::latest()->getQuery()->groupings =[];
For reset
$qry->getQuery()->groups = [];
$qry->getQuery()->wheres = [];
For remove last where or group by column
array_pop($qry->getQuery()->groups);
array_pop($qry->getQuery()->wheres);
public function products($isOrder = true)
{
$hasMany = $this->hasMany(Model\Product::class);
return ($idOrder) ? $hasMany->orderBy('name') : $hasMany;
}
but you can use this only having $shop instance;
$shop->products(false)->...; // your query to get result

MySQL relation with multiple tables

I have 4 mysql tables, as the following:
Makes:
id - make_name
Models:
id - model_name - make_id
Trims:
id - trim_name - model_id
Forsale_Cars:
id - trim_id - year - price
in the Makes table I have ~700 records, so my question is, how can get a list of Makes which only have a child trim in the forsale table?
I have 20 records in the forsale table, I want to get the list of Makes for these cars.
I am using Laravel, so if anybody has achieved that previously using eloquent it will be great
Eloquent way:
// Makes that have forsale nested relation
Make::whereHas('models', function ($q) {
$q->whereHas('trims', function ($q) {
$q->has('forsales');
});
})->get(); // returns Eloquent Collection
Models with correct relations (hasMany can be replaced with hasOne if that's actual relation somewhere):
// Make model
public function models()
{
return $this->hasMany('CarModel');
}
// CarModel (as you can't use Model name)
public function trims()
{
return $this->hasMany('Trim');
}
public function make()
{
return $this->belongsTo('Make');
}
// Trim model
public function forsales()
{
return $this->hasMany('Forsale');
}
public function carModel()
{
return $this->belongsTo('CarModel');
}
// Forsale model
public function trim()
{
return $this->belongsTo('Trim');
}
You could use this query for the first question you asked. Not sure if the this completely answers your question.
SELECT * FROM Makes m left join Foresale_Cars fc on (m.id = fc.id) left join Trims t on (fc.trim_id = t.id) where t.trim_name = "child trim"
I assume you have all your models set up, e.g.:
class Makes extends Eloquent {
protected $table = 'makes';
public function models() {
return $this->hasMany('Model', 'makes_id');
}
}
and so on.. (you have to do this with all your models, of course)
Now, if you want to get all the cars for sale you'd simply chain some foreach loops:
foreach( $makes->models as $model ) {
foreach( $model->trims as $trim ) {
{{ $trim->forsale_cars }}
...
}
}
Edit: Yes you can use raw queries, of course, but using models and the power of eloquent is much more elegant and useful...
For more information on this topic: http://laravel.com/docs/eloquent#relationships

laravel 4 one to many relationships - iteration

I'm trying to improve some code in my app in laravel 4 and trying to implement relationships between models.
I have a table called reservations which has a one to many relationship with items. Items has a one to one relationship with Products. Basically as a reservation the items that are included in the reservation are added to the items table. The specs of the items comes from the products table (type, value etc)
I've set up the relationships in the models as follows:
in the reservations Class:
public function item() {
return $this->hasMany('Item');
}
in the items Class:
public function reservation() {
return $this->belongsTo('Reservation');
}
public function product() {
return $this->hasOne('Product');
}
in the product Class:
public function item() {
return $this->belongsTo('item');
}
I'm trying to now query the reservations for a calendar view. I'm retrieving all the reservations in a month using the following:
$events = Reservation::where('play_date','>=',$start->format('Y-m-d'))
->where('play_date','<=', $end->format('Y-m-d'))
->get();
I'm then trying to iterate through the collection (is it a collection or a result set?) using the following:
$events->each(function($event) { }
I then want to iterate through all the items of the reservation and it's this bit that's confusing me.
$items = $event->item()->get();
which does create an object I can then iterate through this sub collection using another call back but I'm then struggling to get the product information using:
$item->product()->type
I get an error:
Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$type
How can I iterate through the items correctly using laravel relationships? What is the best practice in this and retrieve details relating to the item from the product table
Thank you
The main point to do what you want is understand the difference between $event->item and $event->item(). Basically, $event->item is the same as $event->item()->get(). Knowing this, you should be doing something like this
$events = Reservation::where('play_date', '>=', $start->format('Y-m-d'))
->where('play_date', '<=', $end->format('Y-m-d'))
->get();
$events->each(function ($event) {
$items = $event->item;
$items->each(function ($item) {
$type = $item->product->type;
});
});
You may also want to take a look at Eager Loading your relationships, as to reduce the number of queries ran.

How can I combine two tables with my Eloquent query?

We work on a database where table names are named locally.
table.product
-- id
-- picture
-- category_id
table.category
-- id
-- caterory_name
-- category_directory
There are some products in table. product.category_id should point category.id so system will understand that product belongs to desired table.
We could do this by using INNER JOIN but we can't do it with Laravel. We probably need to configure our models with has/belongs_to etc.
This is where we struggle.
//Controller
$products = Product::all();
return View::make('theme-admin.product_list')
->with('products', $products);
//View (in foreach)
{{ URL::to('uploads/categories/[NEED_CATEGORY.CATEGORY_DIRECTORY_HERE]/' . $products[$k]->picture) }}
We can't get Category.category_directory info in our views since we pass Product::all() only.
How can we make it so the $products array also contains category.category_directory for each value and we can access it like; $products[$k]->category_name?
Thanks!
Create a category relation in your Product model:
class Product extends Eloquent {
private $table = 'myProductsTableName';
public function category()
{
return $this->belongsTo('Category', 'myCategoryColumnID');
}
}
class Category extends Eloquent {
private $table = 'myCategoriesTableName';
}
Unless you need to select all products from a particular category, you don't need to create a hasMany relation on your Category model.
And you just need to use myCategoryColumnID if your products table doesn't follow the singular_table_name_id rule (product_id).
Then just use it:
//View (in foreach)
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/' . $products[$k]->picture) }}
I would setup a relationship...
class Product extends Eloquent {
public function category()
{
return $this->belongsTo('Category');
}
}
class Category extends Eloquent {
public function product()
{
return $this->hasMany('Product');
}
}
The name you use in the realtionship is the Model name.....so be sure if you're using a different table name than your model name, that you set that in your model
protected $table = "yourtablename";
Then use it like so...
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/'
You still end up querying the database multiple times this way... This is called the N+1 effect. Example, if you have 5 products, one query will be executed to get those products. Then in the loop we are executing a query to get the category. This results in 6 total queries.
To solve this problem, use eager loading, which reduces those 6 queries in our example down to 2.
$products = Product::with('category')->get();
Then send that to your view, where you can do your foreach loop..
foreach($products as $val){
//To output the directory
echo $val->category->category_directory;
}
Or in blade...
#foreach($products as $val)
{{URL::to('uploads/categories/'.$val->category->category_directory.'/'.$val->picture)}}
#endfor

Categories