Limiting laravel query using whereIn - laravel

So i have a query to make but i don`k know how best to it.
I have 2 class like below:
User.class
class User {
function posts(){
return $this->hasMany(Post::class, 'user_id');
}
}
Post.class
class Post {
function user(){
return $this->belongsTo(User::class, 'user_id');
}
}
and in my controller i want to get posts per User with a limit for each user NOT for all results. So here is what i have this in my controller:
function getPosts(Request $request){
$user_ids = [1,2,3,4];
$posts = Post::whereIn('user_id', $user_ids)->latest()->take(10)->get();
return $posts;
}
So the above get will get me just 10 entries from all of them yet i want to get 10 for each user nomatter how many user IDs

You can simply limit the relationship with a sub-query:
User::with(['posts' => function($query) {
return $query->limit(10);
}]);

May be something like
DB::table('posts as p1')->leftJoin('posts as p2', function($join){
$join->on('p1.id', '=', 'p2.id')
})->whereIn(p1.user_id, $user_ids)->groupBy('p1.id')->having(COUNT(*) < 10)->orderBy([id, created_at]);
will work for you. for the reference question
The query was
SELECT user_comments.* FROM user_comments
LEFT OUTER JOIN user_comments user_comments_2
ON user_comments.post_id = user_comments_2.post_id
AND user_comments.id < user_comments_2.id
where user_comments.post_id in (x,x,x)
GROUP BY user_comments.id
HAVING COUNT(*) < 3
ORDER BY user_id, created_at
Roughly in DB query builder, it will be like
DB::table('user_comments as uc1')->leftJoin('user_comments as uc2', function($join){
$join->on('uc1.post_id', '=', 'uc2.post_id')->andOn(uc1.id < uc2.id);
})->whereIn(uc1.post_id, [x,x,x])->groupBy('uc1.id')->having(COUNT(*) < 3)->orderBy([user_id, created_at]);
I hope this is helpful for you and give you good idea to get it.

Related

Laravel: sort query results based on field of nested relationship

I have two models with relations as defined below
Order
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
User
public function company(){
return $this->belongsTo(Company::class, 'company_id');
}
company table have 'title' field.
what I want is to get all the orders sorted/order by company title. I've tried different solution but nothing seems to work. Any help or hint would be appreciated.
Recent solution that I tried is
$query = OrderModel::whereHas('owner', function($q) use ($request){
// $q->orderBy('owner');
$q->whereHas('company',function ($q2) use ($request){
$q2->orderBy('title',$request->get('orderByDirection') ?? 'asc');
});
});
but I am not getting user and company relation in query results. also the result remains same for 'ASC' and 'DESC' order.
You could sort the query after adding join like:
return Order::join('users', 'users.id', '=', 'owner_id')
->join('companies', 'companies.id', '=', 'users.company_id')
->orderBy('companies.title')
->select('orders.*')
->get();
You can define new relations in User and Company models.
User
public function orders()
{
return $this->hasMany(Order::class);
}
Company
public function users()
{
return $this->hasMany(User::class);
}
Now you can fetch companies that are in asc order and with the use of relation, you can fetch users and orders. So the ORM like be,
$companies = Company::with('users.orders')->orderBy('title', 'ASC')->get();
So these are the company-wise orders. You can use this too.

Laravel 5.5: How to get top selling items of a given shop?

My tables are like:
shops
[id]
inventories
[id, shop_id]
orders
[id, shop_id]
order_item
[order_id, inventory_id, quantity]
Models:
//Shop
class Shop extends Model
{
public function inventories()
{
return $this->hasMany(Inventory::class);
}
public function orders()
{
return $this->hasMany(Order::class);
}
}
//Inventory
class Inventory extends Model
{
public function shop()
{
return $this->belongsTo(Shop::class);
}
public function orders()
{
return $this->belongsToMany(Order::class, 'order_items')
->withPivot('quantity');
}
}
//Order
class Order extends Model
{
public function shop()
{
return $this->belongsTo(Shop::class);
}
public function inventories()
{
return $this->belongsToMany(Inventory::class, 'order_items')
->withPivot('quantity');
}
}
Now I want 5 top selling inventories of a given shop, What will be the best possible way to do that?
I'm on Laravel 5.5
select s.id,sum(oi.quantity) as total from munna.shops as s
join munna.inventories as iv on s.id=iv.shop_id
join munna.orders as o on iv.shop_id=o.shop_id
join munna.order_items as oi on o.id=oi.order_id
group by s.id
order by total desc limit 5
First, by looking at your tables on order_item, the order_id and inventory_id will bellong to the same shop for sure? I guess yes because if not you would have 2 different shops with same top order. I dont know why you are doing it like this but it's a bit confusing can't figure out why but I would try this:
public function topOrders()
{
$items = DB::table('shops')
->join('orders', 'shops.id', '=', 'orders.shop_id')
->join('inventories', 'shops.id', '=', 'inventories.shop_id')
->join('order_items', 'orders.id', '=', 'order_items.order_id')
->orderBy('quantity', 'desc')
->take(5)
->get();
return $items;
}
What I wrote should select everything from all 3 rows, if you want to select only the items or whatever you want to select you can specify it adding a select clause
Though this was my own question I found the solution on my own and I want to share the solution with the community. I wanted to solve it using Eloquent because I need the model on the view and didn't want to query the model again.
Inventory::where('shop_id', \Auth::user()->shop_id)
->select(
'inventories.*',
\DB::raw('SUM(order_items.quantity) as quantity')
)
->join('order_items', 'inventories.id', 'order_items.inventory_id')
->groupBy('inventory_id')
->get();
I hope this'll help someone with similar issue. Thanks

How to get relationship with union in eloquent?

How can I make relationship with union in laravel eloquent? I've already tried two different approaches.
User::with(['url' => function($query) use(&$some_property) {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $some_property);
$query = $query->union($favouriteUrls);
}]);
In the first attempt there wasn't any union in the query. Then I tried to move the logic to the model.
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $this->some_property);
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
It has executed successfully but $this->some_property was set inside the query to the null value.
I can't create two separate relationship in this case. It has to be one with union. How can I fix it?
If you call that relation as User::with('urls') you will get that $this->some_property doesn't exists, because the object itself doesn't exists. But if you call the urls() method on an object, it should work. Something like this:
$user = User::find(1);
$user->urls; // here $this->some_property should have a value
Assuming you're calling the urls() method from a User object, the $this->some_property should give you the value. If for some reason you cannot access a property directly on an Eloquent model you can always refer to the attributes[] array inside of the model. For example
// calling
$this->some_property
// should be the same as
$this->attributes['some_property']
Fetch all the users joining with the condition
Assuming users is the table for all the users, in your query you could change $this->some_property with 'users.some_property' and everything should work as expected, for each user it will query based on that property. Here is the code:
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', 'users.some_property');
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
And then just call the method like this:
User::with('urls')->get();

How to find inverse of Eloquent query

I've got a query to find enrolled students in a particular activity. On the User model:
public function enrolledStudents($activity)
{
$students = $activity->students()
->wherePivot('user_id', $this->id)
->get();
return $students;
}
Where the Activity model's students method is this:
public function students()
{
return $this->belongsToMany('Student', 'activity_student', 'activity_id', 'student_id')
->withPivot('user_id')
->withTimestamps();
}
I want another method to find the students who aren't enrolled in this activity - how could I go about this?
I.e. $user->students()->notEnrolled($activity)
Basically, you'd have to come from the Student angle. Something like this:
$students = Student::whereDoesntHave('activities', function($q) use ($activity){
$q->where('activity_id', $activity->id);
})->get();
Note that this method is quite new, so you might need to update Laravel with composer udpate

Where NOT in pivot table

In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();

Resources