I have two models. A status and a SaleDetails.
Status
------
id
name
slug
I didn't define any relationship from Status to SaleDetails.
SaleDetails
-----------
id
id_statuses //connects it to the status table
id_products
price
qty
in the Model:
public function status(){
return $this->belongsTo('App\Status', 'id_statuses', 'id');
}
So what I am trying to do here is get all orders that whose status match a certain slug.
E.g dd($this->sale_details->where('status.slug', 'pending')
With what I have using this:
$orders = $this->sale_details->with('product', 'status')->today()->get();
I should be able to filter them based on the status slugs in the frontend, but I want to just do it straight from here.
What's the best way to go about this?
You should use whereHas() it allows you to specify additional filters for the related model to check.
To apply the where condition you can use whereHas() like,
$status = "pending";
$this->sale_details->whereHas('status', function($q) use ($status){
$q->where('slug', $status);
})->get();
i think, better than using query builder if you want fast proccess
Related
hello I am new to laravel and maybe I am a bit confused between eloquent and query builder way for writing a query but anyway can you please tell me what could be the best eloquent way to retrieve info like this in laravel 6 or 7
User > hasMany > Recipes
Recipe > belongsTo > User
I want to check if user id 2 present in users table then get only one post which id is 3
Query builder is for explicitly building SQL queries, and does not return instances of your models. Eloquent query builder, is similar but the result will contain the model(s) loaded with all their attributes, and has some handy functions for querying the relations you define in your models.
Given the limited information in your post, I am assuming when you say a post, you mean a recipe:
Query Builder:
DB::table('users')
->join('recipes', 'recipes.user_id', '=', 'users.id')
->select(['users.some_col', ... 'recipes.some_col'])
->where('users.id', 2)
->get();
If you have your models setup with the relations. You can use Eloquent like so:
User::where('id', 2)->with('recipes')->get();
If I understand you correctly it would be like this:
User::whereId($userId) //asuming it is 2
->with(['recipes' => function($q) use($recipeId) {
$q->where('id', $recipeId); //assuming it is 3
}])->first();
you can do this if i understand correctly:
$user = User::findOrFail(2); //auto 404 if user not found
$recipe = $user->Recipes()->where('id',3)->first();
You may use conditional eager loading for better performance.
$userId = 2;
$receiptId = 3;
$user = User::with(['receipts'=> function ($query) use($receiptId){
$query->where('id', $receiptId);
}
])->find($userId)
I have a query which selects some relationships and groups by the accommodations to remove the duplicate accommodations. But now when I want to load the discount realtion on rooms, it doesn't work because I select only the accommodation_id.
Here is my code:
$data = AccommodationRoom::with('accommodation.city', 'accommodation.accommodationFacilities', 'accommodation.gallery','discount')
->select('accommodation_id')
->whereHas('roomCapacityHistory', function ($query) use ($from_date, $to_date) {
$query->whereDate('from_date', '<=', $from_date);
$query->whereDate('to_date', '>=', $to_date);
})
->whereHas('accommodation', function ($query) use ($searchCity) {
$query->where('city_id', $searchCity);
})
->groupBy('accommodation_id')
->get();
Now if I add the id to the select it would be fine, but my groupBy doesn't work and gives me an error in this case. So I need a solution to get all my accomodations with the listed relations.
As you are looking for all your accommodations with some related models, you should actually select from your Accommodation model. This will work out of the box for the first three relations but will require some tweaking for the discount relation. The simpliest solution is to create a HasManyThrough relation on the Accommodation model:
class Accommodation extends Model
{
public function discounts()
{
return $this->hasManyThrough(Discount::class, AccommodationRoom::class);
}
}
Note: this expects your models to use foreign key columns named by convention; for different names you will need to pass the custom foreign key names as additional parameters according to the documentation.
With this relation set up, you can then use a query like the following:
$data = Accommodation::with('city', 'accommodationFacilities', 'gallery', 'discounts')
->whereHas('accommodationRooms.roomCapacityHistory', function ($query) use ($from_date, $to_date) {
$query->whereDate('from_date', '<=', $from_date);
$query->whereDate('to_date', '>=', $to_date);
})
->where('city_id', $searchCity)
->get();
Further explanation as asked for in the comments:
HasManyThrough builds a virtual relation between two models using a third model in between. Imagine you have Post, Comment and Like as models. One Post can have many Comments and one Comment can have many Likes:
has many has many
Post -----------------> Comment -----------------> Like
1 n 1 n
In this case we also know that one Post can have many Likes. And this is exactly the knowledge we utilize when using HasManyThrough. We simply tell Eloquent that a Post has many Likes, connected by the Posts in between:
has many has many
Post -----------------> Comment -----------------> Like
1 n 1 n
has many through Comment
Post ------------------------------------------------> Like
1 n
I have 3 tables with foreign keys installed.
customers {customer_id, customer_name}
products {product_id, product_name}
customer_products {id, customer_id (foreignkey), product_id (foreignkey)}
My Controller code:
$CustomerProducts = ModelName::where('customer_id', 'somevalue')
->Join('customer_products', 'product_id', '=', 'customer_id')
->get();
My Model code:
class ModelName extends Model {
protected $table = 'hd_products';
public $primaryKey = 'id'; }
What is wrong in my code, since I'm getting wrong results. I want to show customer information and its related products.
This is where Laravel makes life easy. By adding the relationships on the model, you can then simply call the relationship via an eager load. You don't need the join, you can just pull the relationship. So
On your Customer model, set up the product relationship (you look like you have the right database structure for a many-to-many):
public function products(){
return $this->belongsToMany("\App\Product");
}
And then in your Controller, when you go to load your customers, you can grab the products at the same time:
$customer = Customer::with("products")->first();
I'm just taking the first customer as an example - you could get all of them and loop on customer as well as products if you wish.
And finally when you want to call the data like in a blade view, you can access it by chaining on the $customer model. :
{{ $customer->products->first()->name }}
If you want to loop through the products on the customer in a blade view:
#foreach($customer->products as $product){}
And, you still have the primary data for the $customer:
$customer->name // Etc.
HTH
If you want to show customer information and its related products, you gotta select the data from the tables.
In your code, in the controller, to get all data from all tables
you add:
->select(['customers.*' ,'products.*' ,'customer_products.*'])->get();
and edit the join statement so the controller will be like:
$CustomerProducts= DB::table('customer_products')
->join('customers','customers.customer_id','customer_products.customer_id')
->join('products','products.product_id','customer_products.product_id')
->select(['customers.*' ,'products.*' ,'customer_products.*'])
->get();
do not forget to add (if not added)
use DB;
At the beginning of your file (in the namespace area or the imports area), so it is like:
namespace App\Http\Controllers;
use DB;
use App\ //"your_file";
use Illuminate\Http\Request;
Hope this is helpful :)
I have three tables which are users, loans, statuses
The relationship is like this:
A user can have many loans. a loan has many status steps. in the statuses table I have a column called status, basically it telsl this step yes, no. pending sort of situation.
the table structure look like this:
users table
->id
->...
loans table
->id
->...
->user_id (it is the foreign key ->references('id')->on('users');
statuses table
->id
->...
->status (can be "yes", "no", "pending")
->...
->loan_id (it is the foreign key ->references('id')->on('loans');
the models look like this:
in the User model :
public function loans(){
return $this->belongsToMany('App\Loan');
}
in the Loan model:
public function users(){
return $this->belongsToMany('App\User');
}
public function statuses(){
return $this->hasMany('App\Status');
}
in the Status model:
public function loan(){
return $this->belongsTo('App\Loan');
}
My question is how to get the status yes number for each user. say I have five users, each user have multiple loans. each loan have, say 20 steps. but different loan many have different yes steps . I would like to use laravel eloquent ORM to get a array tell me each user get how many yes at certain time. So I would be able to loop through this array in my front end blade file to display users progress. thanks!
Laravel's Collection, which you get when you use Eloquent, is great for this kind of operation. Say you want to get the 'yes' statuses of one user:
//yesStatuses is itself a Collection, but it can be used in a foreach like an array
$yesStatuses = $user->loans
->map(function ($loan) {
return $loan->statuses;
})
->filter(function ($status) {
return $status->status === 'yes';
});
//Number of statuses === 'yes'
$yesStatuses->count();
//If you need it as a regular array
$yesStatuses->toArray();
When you query your users table you should take care of loading your loans and statuses eagerly, otherwise you'll be performing many queries for this operation. Something like this:
$users = App\User::with('loans.statuses')->get();
More on this:
Eloquent Collections
Eager loading of Eloquent Models
Thanks for your help, SrThompson!
I think map() function in the collection really helps. since my goal is to loop through the users to show each user's attributes plus count the "yes" status for that user. So I added my 'statuscount' into the user collection as:
$user['statuscount']=$yesStatusesCount;
my working code block like this:
$users=User::with('loans')->with('loans.statuses')
->map(function ($user){
$yesStatuses = $user->loans->map(function ($loan) {
return $loan->statuses->where('status','yes')->count();
});
$yesStatusesCount=array_sum($yesStatuses->toArray());
$user['statuscount']=$yesStatusesCount;
return $user;
});
Then, in my balde file, I would be able to display the number of "yes" status of the user in the #foreach loop. Thanks again!
If I have a Laravel 5.5 model called User that hasMany Posts and each Post hasMany Hits, is there an aggregate function I can call to get the total number of Hits for a User across all Posts, where the Hit was created in the last week?
It seems like there may be a clever way to do it besides doing something like
$hits = $user->posts()->hits()
and then looping over those hits to check created date.
In this case it seems like raw sql would be better, but I figured there may be an Eloquent way to handle a situation like this.
I think the right solution is just to use a HasManyThrough relationship to grab all the Hit rows, joined through the posts table.
So it'd look like this on the User model (roughly):
return $this->hasManyThrough(
Hit::class,
Post::class
// if you have non-standard key names you can specify them here-- see docs
);
Then when you have your User model you can just call $user->hits to get a collection of all the associated hits through all the user's Posts
You can add the code below to your Post model.
protected static function boot()
{
parent::boot();
static::addGlobalScope('hitCount', function ($builder) {
$builder->withCount('hits');
});
}
It automatically provides a field hits_count whenever you fetch a post.
$post = Post::first();
$hits = $post->hits_count; //Count hits that belongs to this post
You can read the documentation here to customize it to your need.
Set HasManyThrough relation in the User model:
public function hits()
{
return $this->hasManyThrough('App\Models\Hits','App\Models\Posts','user_id','post_id','id');
}
then you can do this:
$reults = $user->hits()->where('hits_table_name.created_at', '>=', Carbon::today()->subWeek())->count();
HasManyThrough Link
Use DB::enableQueryLog(); and DB::getQueryLog(); to see if executed SQL Query is correct;