Laravel Repositories whereHas -- multiple - laravel

I have the following repository Products and each product can have many Categories and many Bidders
What I am trying to achieve is the following (Without Repository)
$products = Products::whereHas('categories', function ($category) {
})->whereHas('bidders', function ($bidder) {
})->get();
This works fine, however, I am trying to make it so that repositories are in place and you can still do the whereHas query, so in my repository I created a method:
public function whereHas($attribute, \Closure $closure = null)
{
return $this->model->whereHas($attribute, $closure);
}
This works well, but only if I am using one of them in my main query, whereas if I use multiple:
$products = $this->products->whereHas('categories', function ($category) {
$category->where('id', '=', 1);
})->whereHas('bidders', function($bidders) {
})->get();
I am getting the following error:
Unknown column 'has_relation'
Column not found: 1054 Unknown column 'has_relation' in 'where
clause' (SQL: select * from products where exists (select * from
categories inner join products_categories on categories.id =
products_categories.categories_id where products.id =
products_categories.products_id and id = 1) and (has_relation
= sections))
The issue I'm seeing is that its returning a collection of items on the first whereHas which means it cannot compute the second one. Any ideas to where I am going wrong?

You can pass a closure with multiple relations to whereHas
$products = $this->products->whereHas(['categories', function
($category) {
$category->where('id', '=', 1);
},'bidders' => function($bidders) {
}])->get();
It will work as expected.

Related

how to sum in subquery in laravel

i want to sum in addselect() function but it show me error.
I have 2 model as see there:
1.jewelItem model:
protected $table = 'jewel_items';
public function buyInvoice(){
return $this->belongsTo(BuyInvoice::class,'buy_invoice_id');
}
2.buyInvoice model:
protected $table = 'buy_invoices';
public function jewelsItems(){
return $this->hasMany(JewelsItems::class);
}
and every jewelItem has weight column.
my query:
$buyInvoice=BuyInvoice::addSelect(['allWeight'=>JewelsItem::whereColumn('buy_invoices.id','buy_invoice_id')->sum('weight')
])->get();
but it show me this error:
Column not found: 1054 Unknown column 'buy_invoices.id' in 'where clause' (SQL: select sum(`weight`) as aggregate from `jewel_items` where `buy_invoices`.`id` = `buy_invoice_id`)
how can i fix this without using Raw method, cause as here says "Raw statements will be injected into the query as strings" and it's vulnerable.
In newer version of laravel you can use withSum to get sum of weights for related jewelsItems
$buyInvoice=BuyInvoice::withSum('jewelsItems', 'weight')
->orderBy('jewelsItems_sum_weight')
->get();
Or in older versions you could use withCount for sum as
$buyInvoice= BuyInvoice::withCount([
'jewelsItems as allWeight' => function ($query) {
$query->select(DB::raw("sum(weight)"));
}
])->orderBy('allWeight')
->get();
you forgot use join in this subquery
JewelsItem::whereColumn('buy_invoices.id','buy_invoice_id')->sum('weight')
Instead of doing a custom count subquery, Laravel has syntaxic sugar for doing this, utilizing the method withSum(). An example of that could be.
$data = Model::query()->withSum('relation.subRelation','weight')->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

Adding Multiple Where Clause from Model in Laravel

I am trying to clean my code up, and working on the Models
I have the following 2 tables broken down like this:
Roll Table
|id|roll_id|member_id|.......
Members table
|id|first_name|last_name|rank|
I have the following on my Roll Model
public function member()
{
return $this->belongsTo('App\Member');
}
This on my Member model
public function roll()
{
return $this->hasMany('App\Roll');
}
While the following code does return the correct results
$roll = Roll::with(['member'])
->where('status', '!=', 'A')
->get();
return ($roll);
I would like to add an extra where clause
->where('rank','<', 12)
However, I get the following error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'member.rank'
in 'where clause' (SQL: select * from Roll where roll_id = 4 and
status != A and `mem ▶"
You can use whereHas method to filter on the relations:
$roll = Roll::with(['member'])
->where('status', '!=', 'A')
->whereHas('member', function($query) {
$query->where('members.rank', '<', 12);
})
->get();
Hope this will resolve your issue.

Use Pivot Table to Fetch Data from Main Table

I am using two tables and a pivot table
Table 1 named calendars.
Table 2 named calendar_groups.
Pivot table calendar_calendar_group.
I'm trying to get data from Table 1 based on a where value in the pivot table. Where calendar_groups_id = 1 then use calendar_id to get data from table 1. I can't get it to work.
$event = new Calendar();
$event->orderBy('start', 'asc')
->whereHas('calendar_groups', function ($q) {
$q->wherePivot('calendar_groups_id', '=', '1');
})->with('calendar_groups')
->first();
This gives me the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'pivot' in
'where clause'
This is the relationship:
public function calendar_groups()
{
return $this->belongsToMany(CalendarGroup::class);
}
Your help is very much appreciated.
I think #Tpojka is right here. If you're trying to get Calendar instances that belong to desired CalendarGroup then replace the code should look like this:
$event = new Calendar();
$group = 1;
$event->orderBy('start', 'asc')
->whereHas('calendar_groups', function ($q) use ($group) {
//$q->wherePivot('calendar_groups_id', '=', $group);
$q->where('id', '=', $group);
})->with('calendar_groups')
->first();
If I'm reading the documentation correctly wherePivot() should be used like this:
$event = new Calendar();
$event->calendar_groups()->wherePivot('some_pivot_column',1)->get();
But this would return you the CalendarGroup instances.
If you'd want to do it through Eloquent but without going all the way to the CalendarGroup then you'd probably need to create a Model (let's call it CalendarCalendarGroupPivot) for the pivot table and add another relation (hasMany('CalendarCalendarGroupPivot')) to your Calendar model.
Ok finally got it working.
I used your suggestions but still don't understand a little part of it.
When I use the suggestions made:
$event = new Calendar;
$event->orderBy('start', 'asc')
->whereHas('calendar_groups', function($q) {
$q->where('calendar_group_id', '=', '1');
})->with('calendar_groups')
->first();
I get an empty collection.
But if I run this:
$event = Calendar::orderBy('start', 'asc')
->whereHas('calendar_groups', function($q) {
$q->where('calendar_group_id', '=', '1');
})->with('calendar_groups')
->first();
I get the desired results
Can anybody tell me the difference between so I can learn from it?

Resources