I have a nested relationship and I would like to filter them, using the result that comes from the first query run by Eloquent.
My Eloquent query is:
$entry = Entry::with([
'products.unit',
'products.area'
])->find($id);
The query log shows me that Eloquent runs this query first:
select * from entry where entry.deleted_at is null and entry.id = ? limit 1
And I would like to use its result in this second query, also run by Eloquent:
select area.*,
area_product.product_id, area_product.quota as pivot_quota,
area_product.month as pivot_month
from area inner join area_product
on area.id = area_product.area_id
where area.deleted_at is null and area_product.product_id in (?)
I would like to put a constraint inside it using the month value that comes from the first query.
Is there a way to do it using Eloquent?
I was thinking in something like this:
$entry = Entry::with([
'products.unit',
'products.area' => function ($q) {
// using month here!
}
])->find($id);
Related
I want to find all data from database with this query:
$dataSearch = House::find()
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->andWhere([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([House::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->asArray()->all();
If I perform the query with all() return only a few part of the results, but if I perform the raw SQL query on the database, I get all the results. I don't understand where the problem is.
This is raw SQL:
SELECT `number`, DATE(reg_date) AS date, ((value) > 100) AS result, `info`
FROM `ho_house` INNER JOIN `ho_owner` ON ho_owner.number_ow = oh_ho_house.number_ow
WHERE (`ho_house`.`number_ow`=2100174106) AND (`ho_house`.`space`='m')
ORDER BY `ho_house`.`reg_date` DESC, `info`
That is because ActiveQuery tries to remove duplicated models from results of query with JOIN (for example if house have 2 owners, house data will be repeated twice in results set). In your case, if you want to raw data from database, you should use Query:
$dataSearch = (new Query())
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->from(House::tableName())
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->andWhere([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([House::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->all();
But if you really need ActiveQuery, you should configure $indexBy to return unique ID for every row.
Replace andWhere with Where Check this code
$dataSearch = House::find()
->select(["number","DATE(reg_date) AS date","((value) > 100) AS result","info"])
->join("INNER JOIN", Owner::tableName(), Owner::tableName().'.number_ow= '.House::tableName().'.number_ow')
->where([House::tableName().'.number_ow'=>$house->number_ow])
->andFilterWhere([House::tableName().'.space'=>$space])
->orderBy([ClosedOperation::tableName().'.reg_date'=>SORT_DESC,'info'=>SORT_ASC]);
$data = $dataSearch->asArray()->all();
How to convert this query to laravel db query.
SELECT * FROM {
Select * from organizers
Order by organizers.rank
} Group by t.department
This is simplified version of query. In real the inner query has more where clause and built using laravel db query.
Edit: I am aware of raw query. But that's not what I am looking for. Inner query is complex and has lots of conditional where clause. I would like to retain the db query object I used there.
You can have 2 different query builders and merge their binding like below :
$innerQuery = DB::table('organizers')->orderBy('organizers.rank');
$mainQuery = DB::table(DB::raw('(' . $innerQuery->toSql() . ') as t'))
->mergeBindings($innerQuery->getQuery())
->groupBy('t.department')
->get();
This will also help you retail the $innerQuery builder instance for your later use as you have mentioned in the question.
I think you will have to execute a raw query.
$result = DB::select("SELECT * FROM (
Select * from organizers
Order by organizers.rank
) Group by t.department");
reference: https://laravel.com/docs/5.7/queries#raw-expressions
With Eloquent I get two results like this from my db:
$deliveries = table1::select('place_id','product_id',DB::raw("SUM(amount) as amount"))->groupBy('place_id', 'product_id');
$purchases = table2::select('place_id','product_id',DB::raw("SUM(quantity) as amount"))->groupBy('place_id', 'product_id');
I want to get a table where all the amount entries of table2 are substracted from table1 if
place_id from table1 == place_id from table2
AND
product_id from table1 == product_id from table2
I want to use Eloquent if possible but I am also fine with some raw SQL inside of it or even some fast php-array-method to do something like:
$inventory = $deliveries - $purchases;
edited/added
So right now I do this:
foreach ($deliveries as $delivery => $delivValue) {
foreach ($purchases as $purchase => $purchValue) {
if ($delivValue['place_id']==$purchValue['place_id'] && $delivValue['product_id']==$purchValue['product_id']) {
$deliveries[$delivery]['amount'] = $delivValue['amount'] - $purchValue['amount'];
}
}
}
This works as expected but I think it's very inefficient when the array sizes do increase.
I say this is as close you can get, even if you use laravel's helper method like diff(), etc they still use the same approach internally so your best bet would be to do it at the database level
Try writing this logic at the db level, like
DB::select('
SELECT table1.place_id,table1.product_id,amount,quantity, amount-quantity
FROM table1 JOIN table2
ON (table1.place_id = table2.place_id
and table1.product_id = table2.product_id)
');
http://sqlfiddle.com/#!9/2d5775/6/2
used DB::select - assuming you aren't taking any user input
hy I'm new in laravel 4 and I have found code like this
$sub = Abc::where(..)->groupBy(..); // Eloquent Builder instance
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
->mergeBindings($sub->getQuery())
->count();
my quetion is
1. what the meaning mergeBindings($sub->getQuery()) and give me example for using mergeBindings
assume your first query is like this:
$sub = Abc::where('type', 1)->groupBy(..);
then when we convert it to sql:
$sub->toSql();
this will return query string some thing like this:
SELECT * FROM abc where abc.type = ? GROUp BY ...
you can see the "?" as the type value which will be binded (replaced by 1) when the PDO executes that query string
So when we use that sub query in another query as you do in
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
->mergeBindings($sub->getQuery())
->count();
you have converted the first sub query to sql string, BUT the second query does not know any thing about your first sub query binding which in this example the value 1
so we need to merge the binding from the first sub query into the last query
so that the last query when executes it will know the value 1 and binds it to the
where clause in replace of "?", and your final executed query will be something like this
(SELECT count(*) from abc where type = 1 GROUP BY ...) as sub
and thats it the use of mergeBindings() method
i hope this makes things clear for your question.
thanks,
Hi i have following query where it's use joininner statement to get all possible businesses. But when a business is created for first time only 1 category will be updated the rest 2 will remain null
public function searchBusinessByCategoryString($str = null, $city=null,$start,$perpage)
{
$select = $this->getDbTable()->getAdapter()->select();
$select->from('business as b', array('b.business_name','b.business_url','b.reviews_num','b.cat_id','b.business_id','b.rating','b.business_phone','b.business_add1','b.business_add2','b.x','b.y','b.photo_url'))
->joinInner('business_category as bc','b.cat_id = bc.cat_id',array('bc.cat_name'))
->joinInner('business_sub_category as bsc','b.sub_cat_id = bsc.b_sub_cat_id',array('bsc.b_subcat_name','bsc.b_sub_cat_id'))
->joinInner('business_sub_category as bsc2','b.sub_cat2_id = bsc2.b_sub_cat_id',array('bsc2.b_subcat_name','bsc2.b_sub_cat_id'))
->joinInner('business_sub_category as bsc3','b.sub_cat3_id = bsc3.b_sub_cat_id',array('bsc3.b_subcat_name','bsc3.b_sub_cat_id'))
->where("bsc.b_subcat_name like '".$str."%'")
->orWhere("bsc.b_subcat_name like '%".$str."'")
->orWhere("bsc.b_subcat_name= '".$str."'")
->orWhere("bsc2.b_subcat_name like '%".$str."'")
->orWhere("bsc2.b_subcat_name = '".$str."'")
->orWhere("bsc2.b_subcat_name like '".$str."%'")
->orWhere("bsc3.b_subcat_name like '%".$str."'")
->orWhere("bsc3.b_subcat_name = '".$str."'")
->orWhere("bsc3.b_subcat_name like '".$str."%'");
$result = $this->getDbTable()->getAdapter()->fetchAll($select);
return $result;
}
Now the issues is how can i not doing joininner query if the rest 2 categories are null? My above statement return empty result event though there is businesses with one category.
use leftJoin instead of innerJoin where the joined table can contain NULL value. INNER JOIN will join table, using the condition and will not keep lines when a null value is found on the joined table. LEFT JOIN will allow you to keep this line