SQL Raw Expressions¶
The AdaiasMagdiel\Rubik\SQL
class is a value object representing a literal SQL fragment that must be inserted into generated SQL verbatim, without quoting or binding.
This is useful when you need to use SQL functions, constants, or expressions such as CURRENT_TIMESTAMP
, NOW()
, gen_random_uuid()
, or similar — anywhere Rubik would otherwise escape or bind a value.
🧩 Importing¶
⚙️ Purpose¶
SQL::raw()
lets you embed literal SQL fragments in:
- Column definitions (e.g.,
DEFAULT CURRENT_TIMESTAMP
) - Update queries
- Where conditions
- Schema constraints
Everywhere else, Rubik escapes and binds values normally — this object simply signals "do not quote this value".
🧱 In Column Definitions¶
The Column
builder fully supports SQL::raw()
for literal defaults or onUpdate
clauses.
Example:
use AdaiasMagdiel\Rubik\Column;
use AdaiasMagdiel\Rubik\SQL;
class User extends Model
{
protected static string $table = 'users';
protected static function fields(): array
{
return [
'id' => Column::Integer(primaryKey: true, autoincrement: true),
'name' => Column::Varchar(length: 100, notNull: true),
'email' => Column::Varchar(length: 150, notNull: true, unique: true),
'created_at' => Column::Datetime(default: SQL::raw('CURRENT_TIMESTAMP')),
'updated_at' => Column::Datetime(default: SQL::raw('CURRENT_TIMESTAMP')),
];
}
}
Generated SQL (SQLite):
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);
✅ All validators in Column
accept SQL::raw()
wherever literal defaults are allowed.
🔍 In Queries¶
SQL::raw()
can be used in queries through the Query
builder.
Example — computed columns¶
$users = User::query()
->select([
'id',
'name',
SQL::raw('LENGTH(name) AS name_length'),
])
->all();
Generated SQL:
Example — WHERE conditions¶
Generated SQL:
✏️ In Update Statements¶
You can safely use SQL::raw()
when updating with expressions.
Generated SQL:
Rubik will detect the SQL
object and inject the raw fragment directly instead of binding it.
🧮 In Model save()
and update()
¶
When you assign SQL::raw()
to a model field and call save()
or update()
, Rubik also respects it:
→ The ORM will generate an UPDATE
that includes CURRENT_TIMESTAMP
verbatim.
🧰 How It Works¶
The SQL
class is a simple immutable value object:
final class SQL
{
private string $expr;
public function __construct(string $expr)
{
$this->expr = $expr;
}
public function __toString(): string
{
return $this->expr;
}
public static function raw(string $expr): static
{
return new static($expr);
}
}
Internally, Rubik checks if a value is an instance of AdaiasMagdiel\Rubik\SQL
.
If it is, Rubik converts it to a string and skips parameter binding.
Places that handle this:
Column
validation methods (validateDecimal
,validateInteger
, etc.)SchemaTrait::escapeDefaultValue()
Query::addCondition()
(WHERE clauses)Query::update()
(SET clauses)
⚠️ Security Warning¶
SQL::raw()
completely bypasses quoting and escaping. Never use it with user input or untrusted data.
Safe examples ✅:
Unsafe examples 🚫:
🧭 Summary¶
Context | Example | Behavior |
---|---|---|
Column default | Column::Datetime(default: SQL::raw('CURRENT_TIMESTAMP')) |
Default literal value |
Schema onUpdate | Column::Datetime(onUpdate: SQL::raw('CURRENT_TIMESTAMP')) |
Literal ON UPDATE |
Query select | SQL::raw('LENGTH(name) AS len') |
Computed column |
Query where | ->where(SQL::raw('DATE(created_at)'), '>=', SQL::raw('DATE("2025-01-01")')) |
Raw condition |
Query update | ->update(['updated_at' => SQL::raw('CURRENT_TIMESTAMP')]) |
Unquoted expression |
Model save/update | $model->field = SQL::raw('NOW()') |
Injects raw SQL value |