how to use count in order by in laravel5.2 - laravel-5

I want to use count in order by and I have used following 2 ways
First
DB::table('user_skill')
->join('skill_category', 'user_skill.skill_category_id', '=', 'skill_category.skill_category_id')
->select(DB::raw('user_skill.skill_category_id', 'skill_category.skill_name','count(user_skill.skill_category_id) as totalIds'))
->orderBy('totalIds','desc')
->groupBy('user_skill.skill_category_id')
->get();
Second
DB::table('user_skill')
->join('skill_category', 'user_skill.skill_category_id', '=', 'skill_category.skill_category_id')
->select(DB::raw('user_skill.skill_category_id', 'skill_category.skill_name'))
->orderBy('count(user_skill.skill_category_id)','desc')
->groupBy('user_skill.skill_category_id')
->get();
But both are giving following error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'totalIds' in 'order clause'
any suggestion, how I can solve this?

If you check the raw function, you will see that it only accepts one argument.
public static function raw($value){
return \Illuminate\Database\MySqlConnection::raw($value);
}
Furthermore, what you do is passing more than one argument so that nothing is passed but the first argument.
You can fix it like below,
->select(DB::raw('user_skill.skill_category_id, skill_category.skill_name, count(user_skill.skill_category_id) as totalIds'))
Or
->select('user_skill.skill_category_id', 'skill_category.skill_name', DB::raw('count(user_skill.skill_category_id) as totalIds'))

Related

Laravel - Column not found: 1054 Unknown column in Laravel Query

I tried to run a Laravel query that selects a particular field that appears more than once, and I got the error below.
"SQLSTATE[42S22]: Column not found: 1054 Unknown column 'no_of_service' in 'having clause' (SQL: select count(*) as aggregate from cloudsubscriptions inner join service_package on cloudsubscriptions.package_id = service_package.id group by cloudsubscriptions.msisdn, service_package.title having no_of_service > 1)
$subscribers = Cloudsubscriptions::join("service_package",
"cloudsubscriptions.package_id", "=", "service_package.id")
->select("cloudsubscriptions.msisdn", "cloudsubscriptions.service_name",
"service_package.title",
DB::raw("COUNT(cloudsubscriptions.msisdn) as 'no_of_service'"))
->groupBy("cloudsubscriptions.msisdn", "service_package.title")
->having('no_of_service', '>', 1)
->get();
I expect to see fields that appear more than once.
I'm pretty sure you're using paginate() in your code and not get() as you posted. And when Laravel generates the necessary data for pagination, it overrides the SELECT part of your statement with:
SELECT count(*) as aggregate
This is used to get the total entries count. That is very obvious from the SQL part of your error message:
SQL: select count(*) as aggregate from cloudsubscriptions inner join service_package on cloudsubscriptions.package_id = service_package.id group by cloudsubscriptions.msisdn, service_package.title having no_of_service > 1
That of course overwrites your no_of_service alias definition, which can no longer be found in your HAVING statement when the total count is done by the paginator.
To work around this, you could use the aggregate function directly in your HAVING statement without the alias:
$subscribers = Cloudsubscriptions::join("service_package",
"cloudsubscriptions.package_id", "=", "service_package.id")
->select(
"cloudsubscriptions.msisdn",
"cloudsubscriptions.service_name",
"service_package.title",
DB::raw("COUNT(cloudsubscriptions.msisdn) as 'no_of_service'"))
->groupBy("cloudsubscriptions.msisdn", "service_package.title")
// Use the COUNT aggregate function here as well
->havingRaw('COUNT(cloudsubscriptions.msisdn) > 1')
->get();
It is a little annoying to have to duplicate that logic, but at least you can make use of the Laravel Pagination which is a much bigger gain.
IMPORTANT NOTE! Make sure to use bindings with havingRaw and other raw methods if the value comes from user input.
UPDATE
Since you're using Eloquent as the starting point for your query, your can make use of this great package made by Roy Duineveld, which fixes the issue present in the paginator and allows you to use the alias in your HAVING statement. You can use it by simply including a trait in your model:
use Illuminate\Database\Eloquent\Model;
use JustBetter\PaginationWithHavings\PaginationWithHavings;
class Cloudsubscriptions extends Model
{
use PaginationWithHavings;
}
And now you can use your original query code without any problems.

Invalid parameter number on Laravel subquery

When I run the below query I get the following error: Invalid parameter number: mixed named and positional parameters.
$subQuery = DB::table('earliest_count')
->select('reporting_week')
->where('vendor_name', $vendorName);
$dates = DB::table('invoice')
->select('week_beginning_date', 'week_end_date')
->whereRaw(':sql BETWEEN `week_beginning_date` AND `week_end_date`', [':sql' => DB::raw("({$subQuery->toSql()})")])
->where('week_beginning_date', '<', $date)
->orderBy('week_beginning_date')
->limit(1)
->mergeBindings($subQuery)
->get();
If I replace the whereRaw with the following it works:
->whereRaw('(SELECT reporting_week FROM earliest_count WHERE vendor_name = "My Vendor") BETWEEN `week_beginning_date` AND `week_end_date`')
How can I get the subquery to work without having to write the exact query as a string?
Edit
I did try the following, and I get no errors but I don't get any results. (When I enter the subquery as a string I do get a result):
->whereRaw('? BETWEEN `week_beginning_date` AND `week_end_date`', [DB::raw("({$subQuery->toSql()})")])
Laravel doesn't use named placeholders, you should use ? for placeholders and remove the name from the parameters array.
Like this:
->whereRaw('? BETWEEN `week_beginning_date` AND `week_end_date`', [DB::raw("({$subQuery->toSql()})"])

Pagination creates an error

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.

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.

Eloquent select where($value) issues

I am trying make select in eloquent:
$query = $query->where($value);
echo $value is:
´column´, ´<´, ´3´
BUT i have error: Unknown column ''column', '=', '3'' in... (look at quotation mark). If i write directly:
$query->where( ´column´, ´<´, ´3´);
Everything is okay
where() wants at least two arguments, but for the way you're using it it will need three. where('column', '<', $value) where $value is 3.
References:
http://laravel.com/docs/queries#advanced-wheres
http://laravel.com/api/source-class-Illuminate.Database.Query.Builder.html#268-324
$user = DB::table('users')->where('name', 'John')->first();
$users = DB::table('users')->where('votes', '>', 100)->get();
Where() required at least two parameters but it will accept three parameters.
Two parameters are compulsory:
table column name
compare value
in your example, you have only passed one parameter and will be consider as a table column name. obviously it will not match with the column name.
However, if you provide 3 parameters, ensure that you place the value parameter as 3rd parameter.
More:
http://laravel.com/docs/queries#selects

Resources