From what I can tell, the only way to add a WITH clause to a query is to use raw sql.
$rawSql = <<<SQL
with recursive dates as (
select date('2021-01-01') date
union all
select dates.date + interval 1 day from dates where dates.date < '2021-01-31'
)
select dates.date, count(payments.id)
from dates
left join payments on date(payments.created)=dates.date
group by 1;
SQL;
$results = DB::select($rawSql);
If you need to change some values, you can do so with bindings.
$rawSql = <<<SQL
with recursive dates as (
select date('?') date
union all
select dates.date + interval 1 day from dates where dates.date < '?'
)
select dates.date, count(payments.id)
from dates
left join payments on date(payments.created)=dates.date
group by 1;
SQL;
$results = DB::select($rawSql, ['2021-01-01', '2021-01-31']);
EDIT
Using staudenmeir/laravel-cte, you can write the WITH Clause like this:
withRecursiveExpression('dates', function ($rec) {
$rec->selectRaw("date('2021-01-01') date")
->unionAll(function ($uni) {
$uni->selectRaw('dates.date + interval 1 day')
->from('dates')
->whereRaw("dates.date < '2021-01-31'");
});
})
The recursive part of the query kept throwing an error if I used bindings, so unfortunately, the dates are passes in raw.
In theory, this syntax should work for the complete query.
$res = DB::query()
->withRecursiveExpression('dates', function ($rec) {
$rec->selectRaw("date('2021-01-01') date")
->unionAll(function ($uni) {
$uni->selectRaw('dates.date + interval 1 day')
->from('dates')
->whereRaw("dates.date < '2021-01-31'");
});
})
->select('dates.date', DB::raw('count(payments.id)'))
->from('dates')
->leftJoin('payments', DB::raw('date(payments.created)'), 'dates.date')
->groupByRaw('1')
->get();
The generated SQL is slightly different.
with recursive `dates` as (
(select date('2021-01-01') date)
union all
(select dates.date + interval 1 day from `dates` where dates.date < '2021-01-31')
)
select `dates`.`date`, count(payments.id)
from `dates`
left join `payments` on date(payments.created) = `dates`.`date`
group by 1