How to order my query by most duplicates in Laravel? - php

On my website, users can post images.
Images can have tags.
There's 4 tables for this, the images table, the images_tag pivot table, the tag table, and of course the users table.
A user can have multiple images with the same tag(s).
I can pull up the tags a user has used across all his images with this query:
$userTags = Tag::whereHas('images', function($q) use($user) {
$q->where('created_by', $user->id);
})->get();
However, I want to make it so that I can order these tags based on how frequently a user uses them. In other words, I want to order by duplicates. Is this possible?

To achieve this, you're going to need to join the images_tags and images tables, count the number of tags, and order by those tags.
$tags = Tag::selectRaw('tags.*, COUNT(images.id) AS total')
->join('images_tags', 'tags.id', '=', 'images_tags.tag_id')
->join('images', 'images.id', '=', 'images_tags.image_id')
->where('images.created_by', $user->id)
->groupBy('tags.id')
->orderBy('total', 'desc')
->get();
The above query will only work in MySQL if the only_full_group_by option is disabled. Otherwise, you're going to need to either rewrite this to use a sub query, or do the ordering in the returned Laravel Collection. For example:
$tags = Tag::selectRaw('tags.*, COUNT(images.id) AS total')
->join('images_tags', 'tags.id', '=', 'images_tags.tag_id')
->join('images', 'images.id', '=', 'images_tags.image_id')
->where('images.created_by', $user->id)
->groupBy('tags.id')
->get();
$tags = $tags->sortByDesc(function ($tag) {
return $tag->total;
});
If you want to add this to your user model, per your comment, create a function similar to the following:
public function getMostUsedTags($limit = 3)
{
return Tag::selectRaw('tags.*, COUNT(images.id) AS total')
->join('images_tags', 'tags.id', '=', 'images_tags.tag_id')
->join('images', 'images.id', '=', 'images_tags.image_id')
->where('images.created_by', $this->id)
->groupBy('tags.id')
->orderBy('total', 'desc')
->limit($limit)
->get();
}

Related

Display best selling items by status

Explaining my problem, I have two tables in my database called order and order_details.
On my dashboard, I display the three best-selling items (currently work!). However, I would like to display only the best selling items THAT have the status = delivered.
Today, it works like this:
$top_sell_items = OrderDetails::with(['product'])
->select('product_id', DB::raw('SUM(quantity) as count'))
->groupBy('product_id')
->orderBy("count", 'desc')
->take(3)
->get();
The problem is that the order status is stored in another table, called orders, column order_status.
How can I create this rule and include it in my $top_sell_items?
if you relationship is done between this table already, you can use this code, if not you have to go to the OrderDetails Model and add new method orders
$top_sell_items = OrderDetails::with(['product', 'orders'])
->whereHas('orders', function($query) {
$query->where('status', 'delivered');
})
->select('product_id', DB::raw('SUM(quantity) as count'))
->groupBy('product_id')
->orderBy('count', 'desc')
->take(3)
->get();
You could either define a relationship between Orders and OrderDetails or use a join like so...
<?php
$top_sell_items = OrderDetails::with(['product'])
->join('orders', 'orders.id', '=', 'order_details.order_id')
->select('product_id', DB::raw('SUM(quantity) as count'))
->where('orders.order_status', 'delivered');
->groupBy('product_id')
->orderBy("count", 'desc')
->take(3)
->get();
More info here: https://laravel.com/docs/8.x/queries#joins
Depending if you desire this, the following solution might be the most efficient:
$products = Product
::whereHas('orderDetails.order', function ($query) {
$query->where('orders.order_status', 'delivered');
})
->withSum('orderDetails', 'quantity')
->orderBy('order_details_sum_quantity', 'desc')
->take(3)
->get();
It will directly return instances of Product. In addition it puts everything in a single query instead of the two that with produces.

Get last record on GROUP BY using Laravel & MySql

I have two table (users and messages) .. I wrote a query to get all messages that users sent to me or I sent, using JOIN .. to get all the users I have contacted or they did.
as in the code below:
$users = Message::join('users', function ($join) {
$join->on('messages.sender_id', '=', 'users.id')
->orOn('messages.receiver_id', '=', 'users.id');
})
->where(function ($q) {
$q->where('messages.sender_id', Auth::user()->id)
->orWhere('messages.receiver_id', Auth::user()->id);
})
->orderBy('messages.created', 'desc')
->groupBy('users.id')
->paginate();
The problem here is when records grouped, I'm getting the old message not the new one according to its created_at .. So, I want to get the last record of the grouped records.
It seems like it would make more sense to make use of Eloquent's relationships here so that you can eager load the relationships instead of having to use join and group by:
$messages = Message::with('sender', 'receiver')
->where(function ($query) {
$query->where('sender_id', auth()->id())
->orWhere('receiver_id', auth()->id())
})
->orderByDesc('created') // is this meant to be 'created_at'?
->paginate();

I want to use multiple table use in Advanced Join Clauses

I need some help on my query. I use multiple tables with advanced join clauses but it shows invalid count and both has same value:
$parents = DB::table('users')
->select('users.id','users.full_name', 'users.email', 'users.avatar', 'users.signup_date', (DB::raw('count(children.id) as children_no')), (DB::raw('count(invitations.id) as invitations_no')))
->leftJoin('children', function ($join) {
$join->on('users.id', '=', 'children.userid')
->where('children.is_deleted', '=', 0);
})
->leftJoin('invitations', function ($join) {
$join->on('users.id', '=', 'invitations.user_id')
->where('invitations.is_deleted', '=', 0);
})
->where('users.is_admin', '=', 0)
->groupBy('users.id')
->get();
I think you can solve this problem with basic relationships in Laravel + soft deletes on the invitations and children table.
This will make your query less complex and you have the Laravel benefits.
<?php
$users = User::where('is_admin', false)
->has(['invitations', 'children'])
->withCount(['invitations', 'children'])
->get();
This wil select all none admins, with invitations and children.
Make sure you have soft deletes setup on children and invitations.
The withCount will add a count for the related relations.

How can I retrieve row from another table in Laravel Eloquent?

I have 3 tables: posts, votes and users. I have written a neat code with Eloquent to retrieve just the posts where the user has not voted yet on.
$posts = Post::whereDoesntHave("votes")
->where('user_id', '=', $user_id)
->whereBetween('posts.created_at', array(Carbon::now()->subHours(48), Carbon::now()))
->take(20)
->get();
return response()->json(['posts' => $posts])->withCallback($request->input('callback'));
But also I want to retrieve user name from table users. I want to pass the data with json.
If I try to do it with query builder, it is hard to eliminate posts that have been voted already by the user.
You can do a join manually to the user table
$posts = Post::join('users', 'users.id', '=', 'posts.user_id')
->whereDoesntHave("votes")
->where('user_id', '=', $user_id)
->whereBetween('posts.created_at', array(Carbon::now()->subHours(48), Carbon::now()))
->take(20)
->get();
Or you could define a relationship in the Post model class.
public function user()
{
return $this->belongsTo('App\User');
}
Then you use with('user') to retrieve data from user table.
$posts = Post::with('user')
->whereDoesntHave("votes")
->where('user_id', '=', $user_id)
->whereBetween('posts.created_at', array(Carbon::now()->subHours(48), Carbon::now()))
->take(20)
->get();

Laravel Eloquent query get users from another model

I have another table called tableb and it has a user relationship defined through the user_id field.
I want to run a query against tableb where a certain date is within a certain range but then I want to grab the user table associated with that row but I only want it to grab the user if it's not been grabbed yet. I'm trying to do this all in 1 DB query. I have most of it done, but I'm having trouble with the unique part of it.
Here's what I have right now:
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->get();
So right now I have 3 entries in tableB from the same user, and ideally I'd like to only get 1 entry for that user.
How would I go about doing this?
Since you're selecting only users data, just add a groupBy clause in your query.
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->groupBy('users.id')
->get();
You should just add groupBy like this :
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->groupBy('users.id')
->get
Try This Code
App/user.php
public function getrelation(){
return $this->hasMany('App\tableB', 'user_id');
}
In Your Controller
Controller.php
use App/user;
public funtion filterByDate(user $user)
{
$date = '2016-02-01';
$result = $user->WhereHas('getrelation', function ($query) use($date) {
$query->whereDate('tableb.start_date', '>', $date)
->first();
});
}

Categories