Get Data from Multiple Tables Laravel - php

I am trying to build a coupon site in Laravel. Each merchant has their own deals/coupons. I have been able to print deals/coupons for a merchant on their specific pages.
Here's my query
$deals = DB::table('deals')
-> join ('merchants', 'deals.merchant_id', '=', 'merchants.merchant_id')
-> where ('merchant_url_text', $merchant_url_text)
-> get();
So far so good.
Now this is where it starts getting complex.
Each deal has 2 more pieces associated with it. Click counts and Votes associated with deals.
The click counts are in a table called clicks which records each click on the website. The click record will have a click id associated it. So I would need to get a count of clicks each deal gets.
The second piece is votes. The votes around a deal are stored in a deal_votes table. The deal_votes table has deal_id, vote (1 or 0)
How do I combine click counts and deal votes to return in the same query so that I can display the info in my view?

Do you have models and relationships set up for merchants, deals, coupons, and clicks? This is trivial if you use Eloquent models with relationships, for which the docs are here: https://laravel.com/docs/5.2/eloquent-relationships
This would look like:
$merchant = Merchant::where('merchant_url_text', $merchant_url_text)
->with('deals','deals.votes','deals.clicks')
->first();
The with() function adds all of the nested information, ie query joins, into a single query.
In your view:
#foreach($merchant->deals as $deal)
Deal: {{$deal->name}}
Clicks: {{count($deal->clicks)}}
Votes: {{$deal->votes->sum('vote')}}
#endforeach

Related

Searching in One to Many

I'm developing a system that have a Users table and a Books table.
I need to implement a Users search, and a results page showing all Users and all Books belonging to each user.
Currently, I'm using GROUP_CONCAT to get the Books of each User.
Alternatively, using LEFT JOIN brings duplicate results; Then I have to manipulate the data in PHP(is this 'better'?) Off course, I can't use GROUP BY as I need all Books.
I dont have any problems with GROUP_CONCAT, but now I need to filter by Book's and putting 'WHERE books.name like "% name%"' will filter the GROUP_CONCAT result, then showing just the searched book in the filter(while I need all users books)
Wich is the best method to do a search with One-to-Many results in PHP / MySQL ?

Laravel using groupBy and unique

I am using Laravel and have a music table, an order_items table and a users table. The music items all have a user and a user can have multiple music items. I am trying to get the top 100 selling music, but I want to return only one music item per user (the top selling one of all their music sales).
My code is:
$items = DB::table('order_items')
->join('music', 'music.id', '=', 'order_items.music_id)
->groupBy('music_id')
->select('order_items.user_id', 'order_items.music_id', DB::raw('count(*) as sold'))
->orderBy(DB::raw('sold_count'), 'desc');
$items = collect($items)->unique('user_id')->values()->take(100);
This does seem to return the data that I need, however the first query returns more than 35K records so takes a while to run. I can place a limit on the initial query however then I can not guarantee that there will be 100 items once they are grouped by user.
Is there a better way to do this? Can it be run in one query?

Laravel 5.1 / Eloquent: How do I use a 3rd table to select from the second table

I have three tables: users, purchase_orders and approvals.
One purchase_order has to be approved by multiple users.
When a new purchase_order gets created, I also create 3 pending approvals belonging to that PO.
The approvals table has a field allowed_user_type that determines who can approve it.
I can't figure out, what is the Eloquent way of selecting the pending purchase orders that can be approved by a specific user, as these are determined from the approvals table.
So far I can pull the pending approvals from the approvals table for a user with the following in the User model.
public function approvals_pending()
{
return $this->hasMany('App\Approval', 'allowed_user_type', 'user_type')
->where('approved', '=', 0);
}
The question is, how do I combine this with a theoretical filter?
I mean ideally, I would love to write:
return $this->hasMany('App\PO')->whereIn('id', '=', $this->approvals_pending()->get()->po_id);
Or something like that...
Any ideas would be greatly appreciated.
OK, for anyone interested I found a solution:
It's very close to what I thought I would have to write.
The lists method basically creates a single array out of the selected field, so it can be plugged-in directly to a whereIn method like so:
return \App\PO::whereIn('id', $this->approvals_pending()->lists('po_id'));
I don't know if this is the most Eloquent way of doing this but it does work.

User roster, joining a table but loosing data

I need some help with a query (using Laravel framework). I'm building a user roster with a few columns that incude the users ratings. Basically, it selects all the active users who have initials and joins the user ratings table to select the ratings for the respective users. The where_in is to select only specific ratings. My issue is on the roster, it only selects one rating, rather than all of them (if the user has more than one rating). I've also tried without the group_by, but then the users are duplicated on the table depending on the number of ratings they have (example: if the user has 2 ratings, their row is displayed twice on the roster).
Query:
$users = DB::table('users')
->where('users.initials', '!=', '')
->left_join('user_ratings', 'user_ratings.uid', '=', 'users.uid')
->where_in('users_ratings.rid', array(6,17,21,20))
->group_by('users.uid')
->order_by('users.name')
->get();
Tables:
Users
=======
uid name
1 John
2 Jeff
3 Cathy
Ratings
======
rid uid
1 1
2 1
2 2
3 1
4 3
The problem is when you do a left_join, the result you are going to get is multiple rows. So without the group_by clause it will return all the results you want in rows, not columns, and with the group_by it will just return the first rating (which is the expected behavior).
I would suggest you just use Eloquent ORM and set up the models and their relationships (it's a lot easier and cleaner). I'm guessing user_rating is a many-to-many pivot table? In which case you would have two models User and Rating and their relationship will be has_many_and_belongs_to. Also, the naming conventions in laravel have the pivot table in alphabetical order, so it will be called "rating_user". Look here for how to set up relationships: http://laravel.com/docs/database/eloquent#relationships
Once the models with their relationships are setup, what I would do is
$users = User::where('initials', '!=', '')->order_by('name')->get();
Then,
foreach($users as $user) {
echo "Ratings for " .$user->name . ": ";
$ratings = $user->ratings()->pivot()->where_in('rid', array(6,17,21,20))->get();
foreach($ratings as $rating) {
however you want to display the ratings here...
}
}
This may not be the most efficient way, but it should get the job done given my assumptions are true.
A quick look at the documentation reveals that the Fluent Query Builder can only do what MySQL can do, and MySQL cannot return an array to the client.
Take a look at this question for your alternatives.
If you want Laravel to fetch the ratings for you, you need to build a model.

CakePHP Pagination with conditions on has_many

So I have a User table and a History table with User hasMany Histories, and I'm trying to implement pagination on the user table.
My problem is that I have search, and some of the things one can search by are things in the History table. Is there a way to filter pagination results based on data in a table associated by hasMany? Containable, which initially seemed like a solution, allows such filtering but only in the retrieval of associated data, not the records themselves (unless I'm missing something?)
Has anyone had to solve this before?
Since it's a hasMany relationship, that means Cake will need to make 2 separate queries: 1 on the users table, and one on the histories table to retrieve all the associations. Since the History data isn't being retrieved until the 2nd query, then your 1st query cannot be filtered via WHERE conditions for fields found in the History model.
To resolve this, you can do one of two things:
Perform pagination on History using Containable (since History belongsTo User, meaning only 1 query will be performed).
Perform pagination on User the way you're already doing, except perform an ad-hoc join to History such that it's no longer a hasMany relationship.
e.g.:
$this->User->bindModel(array('hasOne' => array('History')));
$this->paginate['User']['contain'][] = 'History';
$this->paginate('User', array('History.some_field' => 'some_value'));

Categories