Let's assume I've got a User model and the Country model.
User could have up to four countries simultaneously (home country and three countries he'd like to visit).
Using Eloquent, I'd create a pivot able country_user with an extra field of type, that would correspond to the choice of the user, which country he'd put in the first place etc.
So once again, there is:
User
--id
Country
--id
country_user
--id
--user_id
--country_id
--type
I need to get all users who have, let's say, Canada as a country type 1, UK as a country type 2, France as a country type 3 and New Zealand as a country type 4. If I understand correctly then I can't use wherePivot to do this, only if I get a User::all() and iterate through this collection (which doesn't make much sense and puts unnecessary load on the DB).
What could be the proper way to do this with a minimum possible amount of queries to DB?
Or do I get it all wrong and there should be another approach to this task at all?
You can still search on the pivot table using the relationship you have defined between a User and Country. If for the sake of this example we call that relationship countries and that you want to find people with the following $criteria (sticking to your example):
$criteria = [
'canada' => 1,
'uk' => 2,
'france' => 3,
'new zealand' => 4,
];
So the key in the above is the Country and the value is the type.
To find Users that have those exact requirements, you could do the following:
$users = User::whereHas('countries');
collect($criteria)->each(function ($type, $country) use ($users) {
$users->whereHas('countries', function (Builder $query) use ($type, $country) {
$query->where(['name' => $country, 'type' => $type]);
})->get();
});
Then do something with your $users.
$users->get();
You can the country relationship of HasMany
So, in users it would be this
public function countries() {
return $this->hasMany('country_user_class', 'foreign_key_id_here')->latest()->take(4); // take 4 since the user can have 4 countries
}
The query would look something like this.
User::whereHas('countries', function($query) {
$query->where('type', [1,2,3,4]);
})->get();
This would give you the result. This is how you can achieve it using Query. Such a problem can always be solved by Query. Let me know if you've any more question regarding this.
Related
Assume the following, I have a:
Course Model:
belongsToMany User Model
belongsToMany Exam Model
HasMany Question Model
HasMany Answers Model
Course Exam Results Table
id
user_id
course_id
exam_id
question_id
answer_id
Is there a better way to get the results without having to directly query the table?
I am currently doing the following:
public function displayResult($user, $course, $exam) {
$course_exam_results = \App\CourseExamResult::where([
'user_id' => $user->id,
'course_id' => $course->id,
'exam_id' => $exam->id
])
->get();
dd($course_exam_results);
}
Would love to do something better like auth()->user()->course(??)->exam(??)->results. I thought about using laravel hasManyThrough but I don't think I can make it work in this case as it only accepts 2 Models while I might have to call 3-5 models at a time.
Thank you in advance.
If you want to use multiple wheres in Laravel Eloquent, you must do like example below:
You must give to where() an array.
Every search has two arguments so it is an array in an array.
$course_exam_results = \App\CourseExamResult::where([
['user_id', auth()->user()->id],
['course_id', $course->id],
['exam_id', $exam->id]
])
->firstOrFail();
i have 2 models order and city and each order has one city now what i want to do is to show the user how many orders per each city he has . and the result for example would be some thing like below :
Illuminate\Support\Collection {#2220 ▼
#items: array:17 [▼
"new-york" => 80
"city-number2" => 20
"city-number3" => 10
"city-number4" => 5
]
}
what i have tried so far is to work with the laravels withcount on order like below :
$orders = Order::withCount('orders')->where('shop_id', $shop->id)->get();
but no luck with it because it returns the count of all cities not each city individually .
thanks in advance
You can do this query from the other direction easily:
$cities = City::withCount(['orders' => fn ($q) => $q->where('shop_id', $shop->id)])->get();
foreach ($cities as $city) {
echo "{$city->name}: {$city->orders_count}";
}
If you need to restrict which cities are listed you can constrain that further if needed.
this problem is based on one to many relationships in DBMS, in laravel there is an inbuild option that comes with eloqut (one city has many orders and one order belongs to one city)
first of all, you need to implement the cities model and order model
inside cities model define a function like this
public function orders()
{
return $this->belongsTo(Order::class, 'city_id');
}
and in order model define a function like this
public function city()
{
return $this->hasMany(City::class, 'order_id');
}
then in the controller to find how many orders per each city use the query like this with laravel eloquent
$cities= City::withCount(['orders' => fn ($q) => $q->where('shop_id', $shop->id)])->get();
foreach ($cities as $city) {
echo $city->orders_count;
}
The withCount method which will place a {relation}_count attribute on the resulting models:
the controller to find how many orders per each city use the query like this with laravel eloquent.
$orders = Order::with('cities')
->select('city_id',\DB::raw('count(*) as city'))
->groupby('city_id')
->get();
I am implementing an export function in an app I have built.
The export process fetches a collection of model values and then exports this into a spreadsheet that is downloaded to the client.
On my model, I have both a user_id and a responsibility. Both of these fields contain an ID that maps back to my users table (usually to two different users).
During the export process, I want to replace the user_id and responsibility values with the actual users' names, as opposed to their user IDs.
map() and transform() seem to be the right methods to achieve what I am after, however I can't see an elegant way to achieve what I want.
This is what I have so far:
$forms = Form::where('created_at', '>=', (new Carbon('first day of last month'))->toDateString())
->where('created_at', '<=', (new Carbon('last day of last month'))->toDateString())
->get();
$forms->transform(function($form) {
return [
'responsibility' => is_null($form->responsibility) ? $form->responsibility : $form->personResponsible->name,
'user_id' => $form->user->name
];
});
However, this only returns responsibility and user_id in the collection - as opposed to just updating these values.
Now, I could just include the rest of the columns in the return array, but that seems unnecessary - surely there is a better way to do this.
How can I replace two of the values in my model collection with the users' names most efficiently?
Just modify the valules you want to transform and return the $form Model:
$forms->transform(function($form) {
$form->responsibility = is_null($form->responsibility) ? $form->responsibility : $form->personResponsible->name;
$form->user_id = $form->user->name;
return $form;
});
I am creating a search in laravel for an API but my search gives me the wrong results. I am trying to search by location and food type. I have the following tables:
foods
shops
shop_food
users
comments
Here is my search code:
public function searchShop($food, $location)
{
//
if($food == " " || $location == " "){
return $this->index();
}
//get all records where city and food are equal to
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->with(['foods'=> function($query) use($food){
$query->where('name','=', 'fish pepper'); }])
->get();
//check if empty and return all
if($shops->isEmpty()){
return $this->index();
}
return $shops;
}
my result is the below instead of just the record where location and food it shows all the shops filtered by location even where food isnt a match :
The with method that you're using does not filter in the way that you think it does. Your code actually filters the food results, telling Eloquent to retrieve all Shop and either no foods, or the foods with the name fish pepper. This is called constraining eager loads.
The method you're looking for is whereHas rather than with. This is referred to as querying a relationships existence.
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->whereHas('foods', function($query) use($food){
$query->where('name','=', 'fish pepper');
})
->get();
This will now return only Shops that have a corresponding food entry with the name fish pepper.
If memory serves, whereHas won't actually populate foods for you, but in this instance you wouldn't need it, as it's safe to assume that they all have fish pepper. If you do want to pull all foods, change with('comments.user') to with(['comments.user', 'foods']).
Documentation for whereHas and other ways of achieving this can be found here.
Documentation about what you were doing with the with method can be found here.
Hope that helps.
I have two table for post and user. I want to show post count of user in users list gridview. In yii 1 I use this in model to define a relation for this purpose:
'postCount' => array(self::STAT, 'Post', 'author',
'condition' => 'status = ' . Post::ACTIVE),
...
User:find...().with('postCount').....
But i don't know how to implement this in Yii2 to get count of post in User:find():with('...') to show in gridview.
Anyone try this in yii2?
Here is an example of what I did and it seems to work fine so far. It was used to get a count of comments on a post. I simply used a standard active record count and created the relation with the where statement using $this->id and the primary key of the entry its getting a count for.
public function getComment_count()
{
return Comment::find()->where(['post' => $this->id])->count();
}
Just a passing it along...
You can try the code below:
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id'])->count();
Or if you want to specify a user count:
//user id 2 for example
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id','user.id'=>2])->count();
Please note that, posts is a relation defined in your User model like below:
public function getPosts()
{
return $this->hasMany(Post::className(), ['user_id' => 'id']);
}
Well still I think for those who it may concern, if you JUST want the count a select and not the data it will be better use this instead imho:
$count = (new \yii\db\Query())
->select('count(*)')
->from('table')
->where(['condition'=>'value'])
->scalar();
echo $count;