Laravel querybuilder withCount relationship - laravel

I'm encountering a problem with a script.
I'm working with Laravel 6.x (but there is no big difference).
So, I have a model Order, which has relationship with another model Group like this:
Model Order :
public function groups()
{
return $this->hasMany('App\Models\Order\Group', 'order_id')->orderBy('order_group');
}
Model Group :
public function order()
{
return $this->belongsTo('App\Models\Order\Order', 'order_id');
}
I want all the Orders that have a group where the column 'is_recurrent' inside the group is true AND there are at least 2 line 'is_recurrent' true.
Basically this :
$orders = Order::withCount(['groups' => function ($query) {
$query->where('is_recurrent', true);
}])
->having('groups_count', '>', 1)
->get();
i've try lany things, like Order::whereHas('groups', function ....).
In many cases I got :
SQLSTATE[42803]: Grouping error: 7 ERROR: column "ordergroups.order_group" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ... "is_recurrent" = $1 group by "order_id" order by "order_gro...
^ (SQL: select "orders"., (select count() from "ordergroups" where "orders"."id" = "ordergroups"."order_id" and "is_recurrent
" = 1 group by "order_id" order by "order_group" asc) as "groups_count" from "orders" having "groups_count" > 1 order by "created_at" desc)
With
$orders = Order::withCount(['groups' => function ($query) {
$query->where('is_recurrent', true)->groupBy('order_group');
}])
->having('groups_count', '>', 1)
->get();
I got :
SQLSTATE[42703]: Undefined column: 7 ERROR: column "groups_count" does not exist
LINE 1: ...roup" asc) as "groups_count" from "orders" having "groups_co...
^ (SQL: select "orders"., (select count() from "ordergroups" where "orders"."id" = "ordergroups"."order_id" and "is_recurrent
" = 1 group by "order_group" order by "order_group" asc) as "groups_count" from "orders" having "groups_count" > 1 order by "created_at" desc)
If someone has any clue :D

The withCount function adds a variable to your model, it isn't available within the query (at least to my knowledge).
Based on the little info of your table structure, this should give the correct result; using whereHas with the same query you will ensure that there is at least 1 group with is_recurrent = true, and using withCount will give you the complete count on the individual order model.
$orders = Order::withCount(['groups' => function ($query) {
$query->where('is_recurrent', true)->groupBy('order_group');
}])
->whereHas(['groups' => function ($query) {
$query->where('is_recurrent', true)->groupBy('order_group');
}])
->get();

Related

laravel Eloquent query WhereHas wrong result

I have two tables and relations as bellow
user
the user table:
id
name
active
1
abc
1
2
xyz
Null
3
abx
0
the book table:
id
user_id
name
active
1
1
book1
0
2
2
book2
0
3
1
book3
0
relation is as this
user->books (HasMany)
return $this->hasMany(Book::class,'user_id','id');
my query is as bellow
User::with('book')
->WhereHas('book', function($query) {
$query->where(['active'=> 1]);
})
->where(['id'=> 1,'active'=>1])
->get();
This query is getting zero records because active is 0 in books
But i want to see all user record and if there is matching record with active 1 in book.
second is query user for active 1 or Null and for that if use ->orwhereNull('active')
All records changes.
Thanks
I guess you need to apply filter on related records (with()) instead of applying filter on whole query. So this way you will always get a list of users along with the list of active books only
User::with(['book'=> function($query) {
$query->where('active', 1);
}])
->where(function ($query) {
$query->where('active', 1)
->orWhereNull('active');
})
->get();
I have used logical grouping here so in case if you have more filter clauses to add in your query.
Or you can define a new relation in your user model to pick only active related books as
class User extends Model
{
public function book()
{
return $this->hasMany(Book::class, 'user_id', 'id');
}
public function active_books()
{
return $this->hasMany(Book::class, 'user_id', 'id')
->where('active', '=', 1);
}
}
User::with('active_books')
->where(function ($query) {
$query->where('active', 1)
->orWhereNull('active');
})
->get();
You have id and active column on both table, so I think you need to change your query like this :
User::with('book')
->WhereHas('book', function($query) {
$query->where(['books.active'=> 1]); // books table active column
})
->where(['id'=> 1,'active'=> 1]) // users table id, active column
->get();

Laravel order by eagerly loaded column

I am using laravel eager loading to load data on the jquery datatables. My code looks like:
$columns = array(
0 => 'company_name',
1 => 'property_name',
2 => 'amenity_review',
3 => 'pricing_review',
4 => 'sqft_offset_review',
5 => 'created_at',
6 => 'last_uploaded_at'
);
$totalData = Property::count();
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$dir = $request->input('order.0.dir');
$query = Property::with(['company','notices']);
$company_search = $request->columns[0]['search']['value'];
if(!empty($company_search)){
$query->whereHas('company', function ($query) use($company_search) {
$query->where('name','like',$company_search.'%');
});
}
$property_search = $request->columns[1]['search']['value'];
if(!empty($property_search)){
$query->where('properties.property_name','like',$property_search.'%');
}
if(!Auth::user()->hasRole('superAdmin')) {
$query->where('company_id',Auth::user()->company_id);
}
$query->orderBy($order,$dir);
if($limit != '-1'){
$records = $query->offset($start)->limit($limit);
}
$records = $query->get();
With this method I received error: Column not found: 1054 Unknown column 'company_name' in 'order clause' .
Next, I tried with following order condition:
if($order == 'company_name'){
$query->orderBy('company.name',$dir);
}else{
$query->orderBy($order,$dir);
}
However, it also returns similar error: Column not found: 1054 Unknown column 'company.name' in 'order clause'
Next, I tried with whereHas condition:
if($order == 'company_name'){
$order = 'name';
$query->whereHas('company', function ($query) use($order,$dir) {
$query->orderBy($order,$dir);
});
}else{
$query->orderBy($order,$dir);
}
But, in this case also, same issue.
For other table, I have handled this type of situation using DB query, however, in this particular case I need the notices as the nested results because I have looped it on the frontend. So, I need to go through eloquent.
Also, I have seen other's answer where people have suggested to order directly in model like:
public function company()
{
return $this->belongsTo('App\Models\Company')->orderBy('name');
}
But, I don't want to order direclty on model because I don't want it to be ordered by name everytime. I want to leave it to default.
Also, on some other scenario, I saw people using join combining with, but I am not really impressed with using both join and with to load the same model.
What is the best way to solve my problem?
I have table like: companies: id, name, properties: id, property_name, company_id, notices: title, slug, body, property_id
The issue here is that the Property::with(['company','notices']); will not join the companies or notices tables, but only fetch the data and attach it to the resulting Collection. Therefore, neither of the tables are part of the SQL query issued and so you cannot order it by any field in those tables.
What Property::with(['company', 'notices'])->get() does is basically issue three queries (depending on your relation setup and scopes, it might be different queries):
SELECT * FROM properties ...
SELECT * FROM companies WHERE properties.id in (...)
SELECT * FROM notices WHERE properties.id in (...)
What you tried in the sample code above is to add an ORDER BY company_name or later an ORDER BY companies.name to the first query. The query scope knows no company_name column within the properties table of course and no companies table to look for the name column. company.name will not work either because there is no company table, and even if there was one, it would not have been joined in the first query either.
The best solution for you from my point of view would be to sort the result Collection instead of ordering via SQL by replacing $records = $query->get(); with $records = $query->get()->sortBy($order, $dir);, which is the most flexible way for your task.
For that to work, you would have to replace 'company_name' with 'company.name' in your $columns array.
The only other option I see is to ->join('companies', 'companies.id', 'properties.company_id'), which will join the companies table to the first query.
Putting it all together
So, given that the rest of your code works as it should, this should do it:
$columns = [
'company.name',
'property_name',
'amenity_review',
'pricing_review',
'sqft_offset_review',
'created_at',
'last_uploaded_at',
];
$totalData = Property::count();
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$dir = $request->input('order.0.dir');
$query = Property::with(['company', 'notices']);
$company_search = $request->columns[0]['search']['value'];
$property_search = $request->columns[1]['search']['value'];
if (!empty($company_search)) {
$query->whereHas(
'company', function ($query) use ($company_search) {
$query->where('name', 'like', $company_search . '%');
});
}
if (!empty($property_search)) {
$query->where('properties.property_name', 'like', $property_search . '%');
}
if (!Auth::user()->hasRole('superAdmin')) {
$query->where('company_id', Auth::user()->company_id);
}
if ($limit != '-1') {
$records = $query->offset($start)->limit($limit);
}
$records = $query->get()->sortBy($order, $dir);

How to fix Laravel query builder where clause integer variable translated to string

I have a function to get a pass a language number to get language categories record for API purpose. I use a database query statement to select categories table and join the category language table to get category id, parent_id and name (specified language). When execute return error and select the underlying SQL converted the language value to string (e.g. languages_id = 1). I google a lot and no ideas what's wrong. Can anyone advise how to resolve. Thanks a lot.
I tried to copy the underlying SQL to MySQL Workbench and remove the languages_id = 1 --> languages_id = 1 can working properly. I guess the 1 caused error.
Code Sample:
private function getCategories($language) {
$categories = DB::table('categories')
->select(DB::raw('categories.id, categories.parent_id, categories_translation.name'))
->join('categories_translation', function($join) use ($language) {
$join->on('categories_translation.categories_id', '=', 'categories.id');
$join->on('categories_translation.languages_id', '=', $language);
})
->where([
['parent_id' ,'=', '0'],
['categories.id', '=', $id]
])
->get();
return $categories;
}
Error return the converted SQL:
"SQLSTATE[42S22]: Column not found: 1054 Unknown column '1' in 'on
clause' (SQL: select categories.id, categories.parent_id,
categories_translation.name from categories inner join
categories_translation on categories_translation.categories_id =
categories.id and categories_translation.languages_id = 1
where (parent_id = 0 and categories.id = 1))"
You are trying to join using a comparison to an scalar value, instead of a column. I think you actually want to put that comparison as a "where" condition, rather than a "join on"
->where([
['parent_id' ,'=', '0'],
['categories.id', '=', $id],
['categories_translation.languages_id', '=', $language]
])
there is another thing i just discover with your code. when joining table, you are suppose to be joining 'categories_translation.languages_id' with another table id field. in your case, it is not so. you are not joining 'categories_translation.languages_id' with any table field. so ideally, what you are going to do is this
private function getCategories($language) {
$categories = DB::table('categories')
->select(DB::raw('categories.id, categories.parent_id, categories_translation.name'))
->join('categories_translation', function($join) use ($language) {
$join->on('categories_translation.categories_id', '=', 'categories.id');
})
->where([
['parent_id' ,'=', '0'],
['categories.id', '=', $id]
['categories_translation.languages_id', '=', $language]
])
->get();
return $categories;
}
hope this helps

Laravel Get Result Ordred By For an eager Loaded Relation

I have:
'cards' table
-id
-name
'card_categories' Table
id
card_id
category_id
'categories' Table
id
name
index
I'am Loading The Card then Eager load the Relation, what i would like to do is when doing this :
Card::with('ctrCategories.category').......;
I would like that all loaded category from categories will be sorted by Index just the categories.
I spent the hole day doing everything , but no solution:
I tried this:
$card = Card::findOrFail($id);
return $card->with('cardCategories')
->with('ctrCategories.category')
->with('ctrCategories.arguments')
->orderBy('ctrCategories.category.index')->get();
I also tried this approach:
$data = $this->card
->with([
'roles' => function ($q) {
$q->with(['tabs' => function ($q) {
$q->with(['department' => function ($q) {
$q->with(['panel' => function ($q) {
$q->orderBy('position', 'asc');
}])->orderBy('position', 'asc');
}])->orderBy('position', 'asc');
}])->orderBy('position', 'asc');
}
])
->findOrFail($id);
=====EDIT=====
I writed the SQL query and i got the result i want Now i want to transform it to Laravel Eloquent or DB query:
select cards.id,
cards.name,categories.name,categories.id,categories.index
from cards
inner join card_categories on cards.id = card_categories.card_id inner join categories on categories.id = card_categories.category_id
where cards.id = 120
AND cards.support_id= categories.support_id
order by categories.index asc
any help ? I can't figure it out after couple of hours of testing

Call to a member function paginate() on array error in laravel

I want to create pagination in laravel but it gives error.
here is error "Call to a member function paginate() on array"
this is my query
$blog = DB::select("select blog_post.*,(select img_name from blog_image WHERE blog_image.bid=blog_post.bid limit 0,1) as img_name,
(SELECT count(*) FROM likes WHERE likes.bid =blog_post.bid) AS likes,
(SELECT count(*) FROM comment WHERE comment.bid =blog_post.bid ) as comments ,
(SELECT count(*) FROM blog_share WHERE blog_share.bid =blog_post.bid ) as share
from blog_post WHERE status=1 AND is_draft=0 AND is_publish=1 AND is_delete=0")->paginate(2);
You first need to rewrite your query using Laravel Eloquent. For example:
$blog = DB::table('blog_post')->where([ 'status' => 1, 'is_draft' => 0, 'is_publish' => 1, 'is_delete' = 0 ])->paginate(2);
You don't strictly need to put all the counts / inner-queries into one query. You can use Eloquent Relationships and Mutators to get that information for you. For example, the following Relationship to get the image:
class BlogPost extends Model
{
public function image()
{
return $this->hasOne('BlogImage');
}
}

Resources