Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ehask71's avatar

Laravel 10 and the DB::raw() issue

We are rebuilding our site in Laravel from CakePHP 2 .... yeah been a long time coming. Anyway we have some pretty beefy queries to check availability at a property. They are not tied to a specific table so to speak as we have properties and bookings

Properties have a total available spaces Bookings has the booked spaces (current to the future)

This query gets the available spaces count for the specified reservation date range. I am trying to figure out how to rework this to either Eloquent or DB::select(DB::raw()) I gave up doing it in Cake's Query syntax and this has worked for the last 5 years lol ........ now with Laravel 10 changing the __toString() made me run headlong into a wall.

$query = DB::select(DB::raw("SELECT
        v.selected_date,
        properties.spaces - IFNULL(SUM(bookings.spaces), 0) AS available,
        SUM(CASE WHEN DATE(bookings.checkout) = v.selected_date THEN bookings.spaces ELSE 0 END) AS checkout
    FROM
        (SELECT ADDDATE('1970-01-01', t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) AS selected_date
         FROM (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
              (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
              (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
              (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
              (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
        ) v
    LEFT JOIN
        properties ON properties.id = :property_id 
    LEFT JOIN
        bookings ON bookings.property_id = :property_id  AND bookings.status = 1 AND DATE(v.selected_date) BETWEEN DATE(bookings.checkin) AND DATE(bookings.checkout)
    WHERE
        v.selected_date BETWEEN :startdate AND :enddate
    GROUP BY
        v.selected_date, properties.spaces",['property_id'=>$property_id,'startdate'=>$start_date,'enddate'=>$end_date]));

results in

TypeError: PDO::prepare(): Argument #1 ($query) must be of type string, Illuminate\Database\Query\Expression given in file /home/socketserver/public_html/vendor/laravel/framework/src/Illuminate/Database/Connection.php on line 416

Any help would be appreciated. I saw where someone said they used an alias in app\config.php to alias DB but I cant see anywhere about a config.php in App\

0 likes
5 replies
LaryAI's avatar
Level 58

To rewrite the given query using Eloquent or DB::select(DB::raw()), you can follow these steps:

  1. Create a new model for the Property table and a new model for the Booking table if you haven't already done so.

  2. In the model for the Property table, define a relationship with the Booking model. Assuming the relationship is one-to-many, you can use the hasMany method. For example:

// Property.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Property extends Model
{
    public function bookings()
    {
        return $this->hasMany(Booking::class);
    }
}
  1. In the model for the Booking table, define a relationship with the Property model. Assuming the relationship is many-to-one, you can use the belongsTo method. For example:
// Booking.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Booking extends Model
{
    public function property()
    {
        return $this->belongsTo(Property::class);
    }
}
  1. Now, you can rewrite the query using Eloquent. Here's an example of how you can do it:
$query = Property::selectRaw('
        v.selected_date,
        properties.spaces - COALESCE(SUM(bookings.spaces), 0) AS available,
        SUM(CASE WHEN DATE(bookings.checkout) = v.selected_date THEN bookings.spaces ELSE 0 END) AS checkout
    ')
    ->fromSub(function ($query) use ($property_id, $start_date, $end_date) {
        $query->selectRaw('ADDDATE("1970-01-01", t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) AS selected_date')
            ->from('t0')
            ->crossJoin('t1')
            ->crossJoin('t2')
            ->crossJoin('t3')
            ->crossJoin('t4');
    }, 'v')
    ->leftJoin('properties', 'properties.id', '=', $property_id)
    ->leftJoin('bookings', function ($join) use ($property_id) {
        $join->on('bookings.property_id', '=', $property_id)
            ->where('bookings.status', '=', 1)
            ->whereRaw('DATE(v.selected_date) BETWEEN DATE(bookings.checkin) AND DATE(bookings.checkout)');
    })
    ->whereBetween('v.selected_date', [$start_date, $end_date])
    ->groupBy('v.selected_date', 'properties.spaces')
    ->get();

Note: Make sure to replace Property and Booking with the actual names of your models.

This rewritten query uses Eloquent's query builder methods and takes advantage of the relationships defined in the models. The selectRaw method is used to specify the raw SQL expressions. The fromSub method is used to create a subquery for generating the date range. The leftJoin method is used to perform the left join operations. Finally, the whereBetween method is used to filter the selected dates.

Remember to adjust the code according to your specific database structure and requirements.

1 like
ivanwitzke's avatar

When using the DB facade as you are doing, you can omit the DB::raw() from the select. Just put the sql as the first parameter and the bindings as the second (essentially the same from the DB::raw)

DB::select('SELECT * FROM ... WHERE id = ?', [1])

jlrdw's avatar

@ivanwitzke good catch, yes DB::raw doesn't belong in the db facade. It's used with query builder or eloquent.

I better clean my glasses. 😟

Please or to participate in this conversation.