Laravel eloquent order by custom attribute - laravel

I have a total_views attribute added to my Product model like this
public function getTotalViewsAttribute(){
return (int)$this->views()->sum('count');
}
views() is a relationship on Product like this
public function views()
{
return $this->morphMany(View::class, 'viewable');
}
What I would like to do is order my Product by the total_views. Or in other words to order by the sum of the views() relationship.
I have tries to ->orderBy('total_views') on the query but it doesn't seem to work as expected.
Any help will be highly appreciated.

Eloquent getter can only work, when the query has been executed, therefor your current code is not working. I would always use withCount(), on the query;
$products = Product ::withCount(['views'])->get();
The count would be on a property named views_count;.
foreach ($products as $product) {
$product->views_count; // get view count
}
And also possible to order by the column, as it will be present in the SQL returned by the query.
Product ::withCount(['views'])->orderBy('views_count', 'desc')->get();

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Join tables in Laravel Eloquent method

How to write this code in eloquent method ?
$product = DB::table('products')
->join('purchase', 'products.id', '=', 'purchase.id')
->join('sales', 'purchase.id', '=', 'sales.id')
->select('sales.*', 'purchase.*','products.*')
->get();
Create model Product and add one to many relationship with Purchase in Product model.
public function purchases()
{
return $this->hasMany('App\Models\Purchase');
}
Create model Purchase and add one to many relationship with Sale in Purchase model.
public function sales()
{
return $this->hasMany('App\Models\Sale');
}
Create model Sale.
You can retrieve data using following statement.
$products = Product::with('purchases.sales')->get();
Note: I am assuming the relationship as one to many you can also declare as per your data, also you can define one to many inverse relationship, please refer to laravel docs https://laravel.com/docs/8.x/eloquent-relationships#one-to-many.
You will get purchases and sales data in different key so you can use below syntax to loop over it.
foreach ($products as $product) {
foreach ($product->purchases as $purchase) {
//Purchase data for current product
foreach($purchase->sales as $sale){
//Sale data for current purchase
}
}
}

Laravel - get all items in a parent-category

I have the following table, called recipe_subcategories:
each recipe can be assigned to one parent category and multiple subcategories.
Currently, I have the following code to get the all recipes within a parent category:
public function recipes(Request $request) {
$id = explode('-', $request->idName)[0];
return Category::where('id',$id)->first()->recipes;
}
The issue is, that if the same recipe belongs to different sub categories in the same parent category, it will return it multiple times.
What's the ideal solution for this issue? The goal is to return all recipes within one parent category (category_id) without duplicates.
You could start on the pivot table, like so:
public function recipes(Request $request)
{
$categoryId = explode('-', $request->idName)[0];
$recipeIds = DB::table('recipe_subcategories')
->where('category_id', $categoryId)
->groupBy('recipe_id')
->pluck('recipe_id');
return Recipe::whereIn('id', $recipeIds)
->get();
}
I'm not sure how to join these as one query.
You can try :
$category = Category::find('id', $categoryId);
$recipes = $category->recipes->unique('category_id');
Note : This will get all recipes and then filter then on collection level, not on DB query level.

Laravel Fetching one to Many Relationship

Hello I am learning laravel and I am having an issue retrieving data from my relations.
In my database there are Product and Groups filled with dummy data.
I defined my relationship like this in product model:
public function Group()
{
return $this->hasMany('App\Groups','product_id', 'id');
}
And in my group vice versa with :
public function Product()
{
return $this->belongsTo('App\Product','product_id', 'id');
}
The way I am referencing to my products table is :
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
Now I have column product_id in my database under groups, and it is linked to if from products id it seems.
The groups table contains of its auto incremented id and product_id foreign key column.
While products table has auto incremented id and name column.
The issue is here :
How do I return the products that are not null or have value (of products id) in groups table.
I tried something like this in my filter controller:
public function getProductsWithGroup()
{
$Products = Product::with('groups')->get();
return $Products ;
}
But that is giving me call to undefined relations.
I am not sure how to access belongsTo or hasMany methods and whether I need an extra group_id column in my products table.
You named the relationship wrong. It should be groups & define in lowercase as
public function groups()
{
return $this->hasMany('App\Groups','product_id', 'id');
}
And use ->has() to check existence
public function getProductsWithGroup()
{
$Products = Product::has('groups')->get();
return $Products ;
}
->with() is used to eager load and ->has() is used to check existence & filter.
To get the products don't have any groups,
$Products = Product::doesntHave('groups')->get();
To see other ways to use ->has() check, https://laravel.com/docs/5.7/eloquent-relationships#querying-relationship-existence

Laravel Eloquent model with counter

I'm trying to count all the files within a category, and I have these two relationships:
public function files() {
return $this->hasMany('App\File', 'category_id','category_id');
}
public function fileCount() {
return $this->files()->selectRaw("category_id, count(*) AS count")
->groupBy('category_id');
}
This gives me a collection of items, where the count attribute could be accessed like this:
$my_category = Category::where("category_id", category_id)->with('fileCount')->get();
$file_counter = $my_category->first()->fileCount->first()->count;
Is it possible to directly attach the count attribute to the $my_category variable? Any suggestions will be appreciated.
You can use withCount() method:
$my_category = Category::where("category_id", category_id)
->withCount(['files' => function($q) {
$q->groupBy('category_id');
})
->get();
This will put files_count attribute into results.
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

Resources