Pagination creates an error - laravel

Im trying to paginate a search to bring back pages of 18 items. Previously i had this code working code:
$productsQuery = Product::where('approved', '=', 1)->leftJoin('reviews', 'reviews.products_id', '=', 'products.id')->select('products.*', DB::raw('AVG(ratings) as ratings_average' ))->groupBy('id')->orderBy('ratings_average', 'DESC');
I add on paginate to this as shown in the docs
$productsQuery = Product::where('approved', '=', 1)->leftJoin('reviews', 'reviews.products_id', '=', 'products.id')->select('products.*', DB::raw('AVG(ratings) as ratings_average' ))->groupBy('id')->orderBy('ratings_average', 'DESC')->paginate(18);
And get an error
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in group statement is ambiguous
Any ideas on how to paginate the statement?

It's not neccesarily the pagination which is causing the issue, the error is caused by your group by.
You should place the table name / alias name prior to the id EG:
$productsQuery = Product::where('approved', '=', 1)->leftJoin('reviews', 'reviews.products_id', '=', 'products.id')->select('products.*', DB::raw('AVG(ratings) as ratings_average' ))->groupBy('product.id OR reviews.id')->orderBy('ratings_average', 'DESC');
Please see the ammended statement above, note that copy pasting it won't work, you'll see to change that groupby argument.
You should also visit the docs on Eloquent, you have approached this in a way where you're fighting laravel. I would suggest getting a tighter grip on the ORM used.
Good luck!

The error is pretty self explanatory here:
Column 'id' in group statement is ambiguous
so you need to make
`groupBy('id')`
more explicit, by telling which id (from which table, as each table used in your query features id column) you want to be used for grouping.

Related

Select joined table with same column name but different value using eloquent

I'm trying to call 2 columns from 2 different tables. applicants.ic_no and agents.ic_no. It have different values.
Using the following codes only displayed only ic_no from agents.ic_no
$claimLists = ClaimDetail::join('applicants', 'applicants.ic_no', '=', 'claim_details.ic_no')
->join('agents', 'agents.id', '=', 'claim_details.agent_id')
->where('claim_date', $cutt_off)
->groupBy('agents.id', 'claim_details.id', 'applicants.id')
->orderBy('agents.id')
->orderby('amount', 'desc')
->get();
How do i get both columns to display?
This is because, you have ic_no in both tables. By default MYSQL picks one of it to display, it is the same as having id in both tables and from your results, how would you know which table's ic_no you are accessing while they have the same name?
Alternatively you can use select and DB::raw to change the name of one of the ic_no fields, and similiarly for any other similiar fields. For example;
$claimLists = ClaimDetail::join('applicants', 'applicants.ic_no', '=', 'claim_details.ic_no')
->join('agents', 'agents.id', '=', 'claim_details.agent_id')
->select('claim_details.*', DB::raw('agents.ic_no as agents_ic_no'), 'agents.XXX', 'agents.YYYY', 'applicants.XXX')
->where('claim_date', $cutt_off)
->groupBy('agents.id', 'claim_details.id', 'applicants.id')
->orderBy('agents.id')
->orderby('amount', 'desc')
->get();
instead of XXX and YYY, you can put the fields that you would like to get and you can get as many as you want or remove them, if you don't want to get any field from the second table, but the main thing here is you are access the agents.ic_no as agents_ic_no or whatever name you would like to give it.
I solve this issue by adding select to rename the conflict column name. So this is the code:
$processed = ClaimDetail::join('applicants', 'applicants.ic_no', '=', 'claim_details.ic_no')
->join('agents', 'agents.id', '=', 'claim_details.agent_id')
->select('*')
->selectRaw('applicants.ic_no as nokp, agents.ic_no as agic')
->where('claim_details.agent_id',$agent->id)
->orderBy('claim_details.claimable', 'desc')
->orderby('claim_details.amount', 'desc')
->get();
Thanks guys for your time.

Integrity constraint violation: 1052 Column 'subject' in where clause is ambiguous

I am trying to get result for specific subject, test and student but i am having the above error so far i had tried this thing and got the above error.
$result = DB::table('results')
->where([
['subject',$request->subject],
['test',$request->test],
['user_id',$request->name]
])
->join('users','results.user_id','=','users.name')
->join('tests','tests.id','=','results.test')
->join('subjects','subjects.id','=','results.subject')
->select('results.*','users.name','tests.test_name','subjects.subjects')
->get();
Both the subjects and results tables have a column called results, so the MySQL error is telling you that the database is unsure as to which one you want to select. You may try:
->where([
['subjects.subject', $request->subject],
['test', $request->test],
['user_id', $request->name]
])
Note that this same problem exists in your select clause. You might want to alias subjects.subjects as something else, e.g.
->select('results.*', 'users.name', 'tests.test_name',
'subjects.subjects AS s_subjects')

How to count specific table column using laravel

I have here a laravel query:
DB::table('notifications')
->leftJoin('domains', 'domains.domain', '=', 'notifications.data->via_domain')
->select('notifications.data->via_domain as domain_name')
->groupBy('notifications.data->via_domain')
->get();
This shows domain names that exists between the tables 'notifications' and 'domains'. However, I want to count notifications.notifiable_id field in 'notifications' by:
DB::table('notifications')
->leftJoin('domains', 'domains.domain', '=', 'notifications.data->via_domain')
->selectRaw('count(notifications.notifiable_id), notifications.data->via_domain')
->get();
However it shows error in SQL syntax.
(You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near
via_domain from notifications left join domains on
domains.domain = not at line 1 (SQL: select
count(notifications.notifiable_id), notifications.data->via_domain
from notifications left join domains on domains.domain =
notifications.data->'$."via_domain"')')
Thanks for the help, minna-san.
I already got it!
Here's my code:
DB::table('notifications')->leftJoin('domains', 'domains.domain', '=', 'notifications.data->via_domain')
->select('notifications.data->via_domain as domainName', DB::raw('COUNT(notifications.notifiable_id) AS leadsCount'))
->groupBy('notifications.data->via_domain')
->get();
This results to an array showing the domain name and its leadsCount.
Thanks everyone.
for example, assume we have data as like below
Couponview::whereBetween('created_at', [$start_date, $end_date])
->select('store_id', DB::raw('COUNT(store_id) as store_count'))
->groupBy('store_id')
->orderBy(DB::raw('COUNT(store_id)'), 'DESC')
->take(8)
->get();
then We got OutPut Like below example image

Order by relationship column

I have the following query:
$items = UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->get();
I need to order it by item.type so I tried:
$items = UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy('item.type')
->get();
but I get Unknown column 'item.type' in 'order clause'
What I am missing?
join() worked fine thanks to #rypskar comment
$items = UserItems
::where('user_id','=',$this->id)
->where('quantity','>',0)
->join('items', 'items.id', '=', 'user_items.item_id')
->orderBy('items.type')
->select('user_items.*') //see PS:
->get();
PS: To avoid the id attribute (or any shared name attribute between the two tables) to overlap and resulting in the wrong value, you should specify the select limit with select('user_items.*').
Well, your eager loading is probably not building the query you're expecting, and you can check it by enabling the query log.
But I would probably just use a collection filter:
$items = UserItems::where('user_id','=',$this->id)
->where('quantity','>',0)
->get()
->sortBy(function($useritem, $key) {
return $useritem->item->type;
});
You can use withAggregate function to solve your problem
UserItems::withAggregate('item','type')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy('item_type')
->get();
I know it's an old question, but you can still use an
"orderByRaw" without a join.
$items = UserItems
::where('user_id','=',$this->id)
->where('quantity','>',0)
->orderByRaw('(SELECT type FROM items WHERE items.id = user_items.item_id)')
->get();
For a one to many relationship, there is an easier way. Let's say an order has many payments and we want to sort orders by the latest payment date. Payments table has a field called order_id which is FK.
We can write it like below
$orders = Order->orderByDesc(Payment::select('payments.date')->whereColumn('payments.order_id', 'orders.id')->latest()->take(1))->get()
SQL Equivalent of this code:
select * from orders order by (
select date
from payments
where order_id = payments.id
order by date desc
limit 1
) desc
You can adapt it according to your example. If I understood right, order's equivalent is user and payment's equivalent is item in your situation.
Further reading
https://reinink.ca/articles/ordering-database-queries-by-relationship-columns-in-laravel
I found another way of sorting a dataset using a field from a related model, you can get a function in the model that gets a unique relation to the related table(ex: table room related to room category, and the room is related to a category by category id, you can have a function like 'room_category' which returns the related category based on the category id of the Room Model) and after that the code will be the following:
Room::with('room_category')->all()->sortBy('room_category.name',SORT_REGULAR,false);
This will get you the rooms sorted by category name
I did this on a project where i had a DataTable with Server side processing and i had a case where it was required to sort by a field of a related entity, i did it like this and it works. More easier, more proper to MVC standards.
In your case it will be in a similar fashion:
User::with('item')->where('quantity','>',0)->get()->sortBy('item.type',SORT_REGULAR,false);
$users
->whereRole($role)
->join('address', 'users.id', '=', 'address.user_id')
->orderByRaw("address.email $sortType")
->select('users.*')
you can simply do it by
UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy(
Item::select('type')
->whereColumn('items.useritem_id','useritems.id')
->take(1),'desc'
)
->get();

Using Laravel 5, how do I get all() and order by belongsToMany relationship by the pivot table

I have Deals and Faq's. I have functional relationships working and I can reference $deal->faqs() and it returns the right faqs.
The problem I am trying to solve comes up as I administer the faqs related to a deal. In my Deal admin view (new / edit) I am getting all the Faq's.
$faqs = \App\Faq::all();
This works great, and I am even able to check if an faq is related to a deal through my checkbox: in the view:
{!! Form::checkbox('faqlist[]', $faq->id, $deal->faqs->contains($faq->id) ? true : false) !!}
So now we have a list of all the faqs and the correct ones are checked.
I have setup an order column on the pivot table (deal_faq). That table consists of:
deal_id
faq_id
timestamps
order
In my form, I have a drag and drop ordering solution (js) built and working. By working I mean, I can drag/drop and a hidden field value is updated to reflect the correct order.
When creating a deal, this is no problem. Get all the faq's, check a few to associate, drag to order, then save.
When editing a deal, I need to load based on the order column in the deal_faq table. This is my issue.
I have tried a few things but always get an error. An example of what I have tried is:
$faqs = \App\Faq::orderBy('deal_faq.order', 'asc')->get();
This returns an error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'deal_faq.order' in 'order clause' (SQL: select * from `faq` order by `deal_faq`.`order` asc)
I think the issue is that I am trying to get all, but order by a field that only exists for the related faqs since the order field is on the deal_faq. Just not sure how to solve.
In essence you need to join the pivot table and then apply the order
$faqs = \App\Faq::join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc')
->get();
You may need to adjust table and column names to match your schema.
Now you can extract this logic into a scope of the Faq model
class Faq extends Model
{
...
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
}
and then use it like this
$faqs = \App\Faq::ordered()->get();
UPDATE:
This works to get the FAQ's in order but it only get the ones that
have been associated. There will be FAQ's that are not associated and
thus not ordered.
In this case you just need to use an outer join - LEFT JOIN. The scope definition would then look like this
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id', 'left')
->orderBy('deal_faq.order', 'asc');
}
or a bit more expressively
public function scopeOrdered($query)
{
return $query->leftJoin('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
Please consider adding a secondary order (i.e. by id, or any other field(s) in faqs table that make sense). This way you'd have a deterministically ordered resultsets each time regardless of whether you have an explicit order defined in a pivot table or not.

Resources