Laravel adding WHERE to DB::raw and Joins - mysql

Here is my code:
return ExportInvoice::join('sold_products', 'sold_products.export_invoice_id', '=', 'export_invoices.id')
->join('products', 'sold_products.product_id', '=', 'products.id')
->select(
'sold_products.product_id',
'products.name',
DB::raw('
round(round(sum(round(sold_products.sold_price * sold_products.quantity * (1 - sold_products.discount / 100), 2))
* (1 - export_invoices.discount / 100), 2) * (1 + IF(export_invoices.tax, 14, 0) / 100), 2)
as total
')
)
->groupBy('sold_products.product_id', 'products.name', 'export_invoices.discount', 'export_invoices.tax')
->orderBy('total', 'DESC')
->limit(100)
->get();
What I am trying to do:
I am trying to add whereNotNull('export_invoices.deleted_at') and whereNotNull('sold_products.deleted_at') because I am using Laravel soft delete and the above query return all data even those who are soft-deleted.

If you are not able to do just ->whereNull('sold_products.deleted_at') and ->whereNull('export_invoices.deleted_at') then try to add this after select statement.
$columns = ['sold_products.deleted_at', 'export_invoices.deleted_at']
->where(function($q) use ($columns){
foreach($columns as $column){
$q->whereNull($columns);
}
})
So it would be:
$columns = ['sold_products.deleted_at', 'export_invoices.deleted_at']
return ExportInvoice::join('sold_products', 'sold_products.export_invoice_id', '=', 'export_invoices.id')
->join('products', 'sold_products.product_id', '=', 'products.id')
->select(
'sold_products.product_id',
'products.name',
DB::raw('
round(round(sum(round(sold_products.sold_price * sold_products.quantity * (1 - sold_products.discount / 100), 2))
* (1 - export_invoices.discount / 100), 2) * (1 + IF(export_invoices.tax, 14, 0) / 100), 2)
as total
')
)->where(function($q) use ($columns){
foreach($columns as $column){
$q->whereNull($columns);
}
})->groupBy('sold_products.product_id', 'products.name', 'export_invoices.discount', 'export_invoices.tax')
->orderBy('total', 'DESC')
->limit(100)
->get();
I hope it helps!

Related

Laravel - How to create a query with where clause using subquery?

I'm trying to generate this query using Laravel:
select mygames.id, mygames.name, mygames.slug, mygames.cover from mygames
left join mygame_mygenre on mygames.id = mygame_mygenre.mygame_id
left join mygame_myplatform on mygames.id = mygame_myplatform.mygame_id
where mygame_mygenre.mygenre_id in (8, 9, 31, 32, 33)
and mygame_myplatform.myplatform_id in (3, 6, 14, 34, 37, 39, 46, 48, 72, 130)
and mygames.id <> 1990
and mygames.summary is not null
and (select count(mygame_id) from mygame_myplatform where mygame_id = mygames.id) > 1
group by mygames.id, mygames.name, mygames.slug, cover
order by RAND()
limit 6
My current code is:
$games = DB::table('mygames')
->leftjoin('mygame_mygenre', 'mygames.id', '=', 'mygame_mygenre.mygame_id')
->leftjoin('mygame_myplatform', 'mygames.id', '=', 'mygame_myplatform.mygame_id')
->select('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->when($genres_id, function ($query, $genres_id) {
return $query->whereIn('mygame_mygenre.mygenre_id', $genres_id);
})
->when($platforms_id, function ($query, $platforms_id) {
return $query->whereIn('mygame_myplatform.myplatform_id', $platforms_id);
})
->where('mygames.id', '<>', $this->id)
->whereNotNull('mygames.summary')
->where(function ($query) {
$query->selectRaw('count(mygame_id)')
->from('mygame_myplatform')
->where('mygame_id', 'mygames.id');
}, '>', 1)
->groupBy('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->inRandomOrder()
->take(6)
->get();
This code is not working because inside the closure function I was unable to pass the name of the mygames table with the id field. Laravel is interpreting as a text parameter and not as a table.field
->where(function ($query) {
$query->selectRaw('count(mygame_id)')
->from('mygame_myplatform')
->where('mygame_id', 'mygames.id'); <<<<<<<<<<<<<
}, '>', 1)
I tried to use 'use ()' but it didn't work either.
Could you help me?
Here I assume you're trying to compare 2 columns, right?
->where('mygame_id', 'mygames.id');
In that case, use the whereColumn/orWhereColumn method.
->whereColumn('mygame_id', 'mygames.id')
It's because you must use the whereColumn method to achieve this.
https://laravel.com/docs/8.x/queries#additional-where-clauses
Another solution is to use the whereRaw method.
See :
DB::table('mygames')
->select(['mygames.id', 'mygames.name', 'mygames.slug', 'mygames.cover'])
->leftJoin('mygames_mygenre', 'mygames.id', '=', 'mygame_mygenre.mygame_id')
->leftJoin('mygame_myplatform', 'mygames.id', '=', 'mygame_myplatform.mygame_id')
->whereIn('mygame_mygenre.mygenre_id', [8, 9, 31, 32, 33])
->whereIn('mygame_myplatform.myplatform_id', [3, 6, 14, 34, 37, 39, 46, 48, 72, 130])
->where('mygames.id', '<>', 1990)
->whereNotNull('mygames.summary')
->where(1, '<', function ($query) {
$query->selectRaw('COUNT(mygame_id)')
->from('mygame_myplatform')
->whereColumn('mygame_id', 'mygames.id');
})
->groupBy('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->inRandomOrder()
->take(6)
->get();
looks similar to #jascar_destin answer, but on the group by, you ll have to specify which table the cover is been picked from except if cover does not exists on other tables rather than mygames.
DB::table('mygames AS mg')
->leftJoin('mygame_mygenre AS mgmg', 'mgmg.mygame_id', '=', 'mg.id')
->leftJoin('mygame_myplatform AS mgmp', 'mgmp.mygame_id', '=', 'mg.id')
->select(['mg.id', 'mg.name', 'mg.slug', 'mg.cover'])
->whereIn('mgmg.my_genre_id', [8, 9, 31, 32, 33])
->whereIn('mgmp.my_platform_id'. [3, 6, 14, 34, 37, 39, 46, 48, 72, 130])
->where('mg.id', '<>', 1990)
->whereNotNull('mg.summary')
->where(1, '<', function ($query) {
$query->selectRaw('COUNT(mygame_id)')
->from('mygame_myplatform')
->whereColumn('mygame_id', 'mg.id');
})
->groupBy('mg.id', 'mg.name', 'mg.slug', 'mg.cover')
->inRandomOrder()
->take(6)
->get();

Yii2 query with and or in where condition

Hi iam trying to covert this query as active record format
SELECT cmaindb, eshdr1, ascsnr, takpplz, takpadr
FROM ts_stats
WHERE (cmaindb = 'tam' OR cmaindb = 'soc')
AND (nproduktiv = 1)
OR (cmaindb = 'tc')
AND (nabrfirm = 5)
AND (nproduktiv = 1)
code which i have tried
return self::find()
->andWhere(['or',
['cmaindb'=> 'tam'],
['cmaindb'=> 'soc']
])
->andWhere(['nproduktiv'=>1])
->orWhere(['cmaindb' => 'tc'])
->andWhere(['nabrfirm '=>5, 'nproduktivs'=>1])
->all();
this will fails
Try this one
$cmaindbs = array('tam'=>'tam', 'soc'=>'soc');
return self::find()
->where(['IN', 'cmaindb' , $cmaindbs])
->andWhere(['nproduktiv' => 1])
->orWhere(['cmaindb' => 'tc'])
->andWhere(['nabrfirm' => 5])
->andWhere(['nproduktivs' => 1])
->all();
Let's further optimize your query:
return self::find()
->where(['IN', 'cmaindb' , ['tam', 'soc', 'tc'])
->andWhere(['nproduktiv' => 1])
->andWhere(['nabrfirm' => 5])
->andWhere(['nproduktivs' => 1])
->all();

CakePHP 3.6 Query Builder complex OR conditions issue

I am trying to build a query using query builder with complex nested AND and OR conditions. Here is what I have written so far.
$cond_arr = array();
$cond_arr['VehicleBrandModels.status'] = 1;
$query = $this->VehicleBrandModels->find();
$query->hydrate(false);
$query->select($this->VehicleBrandModels);
$query->where($cond_arr);
$VBM_data = $query->toArray();
This will generate a query like below
SELECT * FROM vehicle_brand_models WHERE status = 1;
I want to generate a query with nested AND & OR conditions like below
SELECT * FROM vehicle_brand_models WHERE status = 1 AND ((overall_rating > 0 AND overall_rating < 2) OR (overall_rating >= 2 AND overall_rating < 4) OR (overall_rating >= 4 AND overall_rating <= 5));
Can anybody help to solve how to achieve this in CAKEPHP 3.0 Query builder?
The simplest solution is the following
$cond_arr = [
'VehicleBrandModels.status' => 1,
'OR' => [
['overall_rating >' => 0, 'overall_rating <' => 2],
['overall_rating >=' => 2, 'overall_rating <' => 4],
['overall_rating >=' => 4, 'overall_rating <=' => 5]
]
];
there is a more 'cake' way and if I'll have time I will post it.
But note that from what I see all your OR conditions overlap and you can simply do
$cond_arr = [
'VehicleBrandModels.status' => 1,
'overall_rating >' => 0,
'overall_rating <=' => 5
];
edit: as primised here's the more cake way using query expressions
$query->where(function($exp, $q) {
$exp = $exp->eq('VehicleBrandModels.status', 1);
$conditions1 = $exp->and_([])
->gt('overall_rating ', 0)
->lte('overall_rating ', 2);
$conditions2 = $exp->and_([])
->gt('overall_rating ', 2)
->lte('overall_rating ', 4);
$conditions3 = $exp->and_([])
->gt('overall_rating ', 4)
->lte('overall_rating ', 5);
$orConditions = $exp->or_([$conditions1, $conditions2, $conditions3]);
$exp->add($orConditions);
return $exp;
});
still some conditions are overlapping

Doctrine2 - using group by method to add totals for each month

I have the following table:
I require the SUM(total), SUM(cost), SUM(net) grouped as a total for each month.
So far I have the following queries:
Returns a single global total:
$qb ->add('select','SUM(u.total) as Total, Sum(u.cost) as Cost, Sum(u.net) as Net')
->where('u.status = :status')
->setParameter('status' , 1);
Returns totals grouped by the created date:
$qb ->add('select','SUM(u.total) as Total, Sum(u.cost) as Cost, Sum(u.net) as Net')
->groupBy('u.created')
->where('u.status = :status')
->setParameter('status' , 1);
How do I return by days or months or years?
Okay looks like I solved this.
Doctrine query builder supports sub-string, so if a date looks like this:
2015-08-13 14:02:15
We can use substring to pull out the parts we want and then use the orderBy method to list by them.
Making listing by, day, hour, minute and second, simple.
This being the template:
$qb ->add('select','SUM(u.total) as Total, Sum(u.cost) as Cost, Sum(u.net) as Net, SUBSTRING(u.created, 6, 2) as Month')
->groupBy('Month')
->where('u.status = :status')
->setParameter('status' , 1);
Simply replace the SUBSTRING value with:
SUBSTRING(u.created, 1, 4) for Year
SUBSTRING(u.created, 6, 2) for Month
SUBSTRING(u.created, 9, 2) for Day
Day and Month will clearly run across multiple years and months respectively, so further checks would be required.
My code is like this if anyone is interested, I use multiple day, month and year attributes to ensure the data does not aggregate.
public function getSalesBy($date)
{
if (!in_array($date,['day','month','year']))
{
return [];
}
$qb = $this->ordersRepository->createQueryBuilder('u');
if ($date == 'day')
{
$qb->add('select','SUM(u.total) as total, Sum(u.cost) as cost, Sum(u.net) as net, SUBSTRING(u.created, 9, 2) as day, SUBSTRING(u.created, 6, 2) as month, SUBSTRING(u.created, 1, 4) as year');
$qb->groupBy('year,month,day');
}
if ($date == 'month')
{
$qb->add('select','SUM(u.total) as total, Sum(u.cost) as cost, Sum(u.net) as net, SUBSTRING(u.created, 9, 2) as day, SUBSTRING(u.created, 6, 2) as month, SUBSTRING(u.created, 1, 4) as year');
$qb->groupBy('year,month');
}
if ($date == 'year')
{
$qb->add('select','SUM(u.total) as total, Sum(u.cost) as cost, Sum(u.net) as net, SUBSTRING(u.created, 9, 2) as day, SUBSTRING(u.created, 6, 2) as month, SUBSTRING(u.created, 1, 4) as year');
$qb->groupBy('year');
}
$qb->where('u.status = :status')->setParameter('status' , 1);
return $qb->getQuery()->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}

WordPress get specific posts in arbitrary order

Right now, I'm doing:
$posts = get_posts(array('post_type' => 'page', 'post__in' => array(1, 3, 2, 9, 7)));
and am having two issues:
Post 3 is of 'post_type' => 'post', so it doesn't get selected, but I want it! If I leave out 'post_type' => 'page', then only post 3 is selected (because it must assume 'post_type' => 'post'.).
I want to be able to order the posts arbitrarily by their ids. If I knew how to use MySQL, I could do:
SELECT * FROM wp_posts WHERE ID IN (1, 3, 2, 9, 7)
ORDER BY FIND_IN_SET(ID, '1,3,2,9,7');
But, how should I do this with WordPress?
First fetch all posts arbitrarily by their ids and then loop through all the posts
You can do in this way:-
$posts=$wpdb->get_results("SELECT ID FROM $wpdb->posts WHERE ID IN (1, 3, 2, 9, 7)
ORDER BY FIND_IN_SET(ID, '1,3,2,9,7')");
$count=count($posts);
for ($counter=0 ; $counter < $count; $counter++)
{
$post=get_post( $posts[$counter]->ID, $output );
//do your stuffs with posts
}
Hope this helps
Kawauso on the #wordpress IRC channel informed me that "post_type takes an array of values." From that, I found that the following also selects post 3:
So, I did the following:
$post_ids = array(1 => 0, 3 => 1, 2 => 2, 9 => 3, 7 => 4);
$posts = get_posts(array('post_type' => array('post', 'page'),
'post__in' => array_keys($post_ids)));
$ordered_posts = array(0,0,0,0,0); // size of five; keeps order
foreach ($posts as $p) {
setup_postdata($p);
$ordered_posts[$post_ids[$p->ID]] = array(
'permalink' => get_permalink($p->ID),
'title' => $p->post_title,
'excerpt' => get_the_excerpt(),
'date' => date('F j, Y', strtotime($p->post_date)));
}
According to this thread, you can use this code and then use the classic WordPress loop:
$args = array(
'post_type'=>'page',
'orderby'=>'menu_order',
'order'=>'ASC'
);
query_posts($args);