x-yuri
x-yuri

Reputation: 18873

How to use named bindings with laravel's query builder?

I'd like to do the following:

MyModel::where(MyModel::sql_distance_to(), ['lat' => '...', 'lng' => '...'])->get();

Where sql_distance_to() returns some complex sql expression. But from what I can see, query builder doesn't support named bindings. Is this true? Any workaround?

Upvotes: 3

Views: 2913

Answers (2)

Skysplit
Skysplit

Reputation: 1943

I belive you mean using Query Scopes

class MyModel extends Model
{
    public function scopeSqlDistanceTo($query, $lat, $lon)
    {
        // Previously DB::raw(expr) was here.
        // No need since we're using whereRaw()
        $whereRaw = 'lat = ? AND lon = ?';

        return $query->whereRaw($whereRaw, [$lat, $lon]);
    }
}

Usage:

$latitude = 0.0;
$longitude = 180.0;
MyModel::sqlDistanceTo($latitude, $longitude)->get();

Upvotes: 1

x-yuri
x-yuri

Reputation: 18873

tl;dr Use Skysplit's solution, if possible.

Here's what I came up with. One additional complication is that laravel by default disables pdo's emulation mode. Which results in being unable to use a named parameter marker of the same name more than once in a statement.

$q = MyModel::whereRaw(MyModel::sql_distance_to() . ' < :dist');
run_query_with_params(MyModel::class, $q,
    array_merge(MyModel::sql_distance_to_params($lat, $lng), [
        'dist' => 10,
    ]));

class MyModel {
    static function sql_distance_to() {
        return 'ACOS(
            SIN(RADIANS(lat)) * SIN(RADIANS(:lat1))
            + COS(RADIANS(lat)) * COS(RADIANS(:lat2))
                * COS(RADIANS(:lng - lng))
        ) * 6371';
    }

    static function sql_distance_to_params($lat, $lng) {
        return [
            'lat1' => $lat,
            'lat2' => $lat,
            'lng' => $lng,
        ];
    }
}

function run_query_with_params($class, $q, $params)
{
    $r = \DB::select($q->toSql(), $params);
    return call_user_func([$class, 'hydrate'], $r);
}

Upvotes: 0

Related Questions