DB

object

%DB

/phlo/resources/DB/DB.phlo

Database engine class

The shape every driver fills in, and the reason a model can move between MySQL, PostgreSQL, SQLite and a JSON file untouched. Reads come in named flavours, so ask for what you want back: record for one, records keyed by id, rows in order, column, pair and item. Anything you pass as an argument is bound, never pasted into the SQL, so a value from a visitor is safe by construction. A connection that went away is reconnected and the query is tried once more, which is what keeps a long-lived worker alive.

databasepdosql
prop

%DB -> PDO:\PDO

line 12
This function triggers an error if no PDO connector is defined for database operations.
error('No PDO connector defined')
prop

%DB -> fieldQuotes:string

line 13
Retrieves the field quotes for a specified database field, allowing for the correct formatting of SQL queries.
bt
prop

%DB -> savepoint:int

line 14
Creates a savepoint in the current database transaction, allowing for partial rollbacks.
prop

%DB -> insertIgnore:string

line 15
Inserts a new record into the database while ignoring any duplicate entries that would cause a conflict.
' IGNORE'
prop

%DB -> insertOnConflict:string

line 16
Inserts a new record into the database, and in case of a conflict, it updates the existing record instead.
void
method

%DB -> load (string $table, string $columns = '*', string $where = void, string $joins = void, string $group = void, string $limit = void, string $order = void, ...$args)

line 18
Loads data from the specified table in the database, allowing for optional selection of columns, filtering with conditions, joining with other tables, grouping, ordering, and limiting the results.
$table = strpos($table, space) || strpos($table, dot) ? $table : "$this->fieldQuotes$table$this->fieldQuotes"
$args && $where = ($where ? "$where AND " : void).loop(array_keys($args), fn($column) => $table.dot.$this->quoteId($column).'=?', ' AND ')
$joins && $joins = " $joins"
$where && $where = " WHERE $where"
$group && $group = " GROUP BY $group"
$order && $order = " ORDER BY $order"
$limit && $limit = " LIMIT $limit"
$query = "SELECT $columns FROM $table$joins$where$group$order$limit"
return $this->query($query, ...array_values($args))
method

%DB -> query ($query, ...$args)

line 30
Executes a database query with the specified SQL statement and arguments, returning the result set.
$this->queryRun($query, $args, true)
method

%DB -> queryRun ($query, $args, $retry):\PDOStatement

line 37
Runs a statement, retrying once when the connection was lost.
try {
	if (!$args) $stmt = $this->PDO->query($query)
	else {
		$stmt = $this->PDO->prepare($query)
		$stmt->execute($args)
	}
	if (debug){
		$match = regex('/\b(UPDATE|INSERT INTO|DELETE FROM|FROM)\b\s+([`"\[]?\w+[`"\]]?)/i', strtr($query, [$this->fieldQuotes => void]))
		$where = strtr(regex('/\bWHERE (\b.+)/is', $query)[1] ?? void, [' ORDER BY' => void])
		$match && debug("Q: $match[1] $match[2]".strtr(rtrim(" $where "), [dq => void])." (".$stmt->rowCount().")")
	}
	return $stmt
}
catch (\PDOException $e){
	// Retry only idempotent read statements outside a transaction. WITH is excluded: a `WITH ... UPDATE`
	// or `WITH ... DELETE` CTE is a mutation that may already have run before the connection dropped
	// (error 2013), and a reconnect would start a fresh, transaction-less session.
	if (!$retry || !$this->goneAway($e) || $this->PDO->inTransaction() || !preg_match('/^\s*\(*\s*(SELECT|SHOW|EXPLAIN|DESCRIBE|DESC)\b/i', $query)) error('Database error'.colon.lf.$query.lf.lf.$e->getMessage())
	unset($this->PDO)
	return $this->queryRun($query, $args, false)
}
method

%DB -> goneAway ($e):bool

line 61
Checks if a database connection error indicates that the connection has been lost or the server is unavailable.
in_array((int)($e->errorInfo[1] ?? 0), [2006, 2013], true) || stripos($e->getMessage(), 'gone away') !== false || stripos($e->getMessage(), 'lost connection') !== false
method

%DB -> column (...$args):array

line 63
Fetches a single column from the result set of a database query, using the provided arguments to load the data.
$this->load(...$args)->fetchAll(\PDO::FETCH_COLUMN)
method

%DB -> item (...$args)

line 64
Loads an item from the database using the provided arguments and returns it as a single column value, or null if not found.
($v = $this->load(...$args)->fetch(\PDO::FETCH_COLUMN)) === false ? null : $v
method

%DB -> pair (...$args):array

line 65
Fetches all rows from the database as a key-value pair array using the provided arguments.
$this->load(...$args)->fetchAll(\PDO::FETCH_KEY_PAIR)
method

%DB -> group (...$args):array

line 66
Fetches all records from the database grouped by a specified column or columns, returning the results as an array of objects of the specified class.
$this->load(...$args)->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_CLASS, obj::class)
method

%DB -> records (...$args):array

line 67
Fetches all records from the database as objects of the specified class, using the provided arguments for the query.
$this->load(...$args)->fetchAll(\PDO::FETCH_CLASS|\PDO::FETCH_UNIQUE, obj::class)
method

%DB -> rows (...$args):array

line 68
Fetches all rows from the database as instances of the specified class using the provided arguments.
$this->load(...$args)->fetchAll(\PDO::FETCH_CLASS, obj::class)
method

%DB -> record (...$args):?obj

line 69
Fetches a single record from the database as an object of the specified class, returning null if no record is found.
$this->load(...$args)->fetchObject(obj::class) ?: null
method

%DB -> quoteList (array $ids):string

line 70
Quotes each element in the provided array of IDs for safe use in SQL queries, returning a comma-separated string.
loop($ids, fn($id) => $this->PDO->quote((string)$id), comma)
method

%DB -> quoteId ($id):string

line 75
Safely quotes a SQL identifier such as a column or table name.
$this->fieldQuotes.str_replace($this->fieldQuotes, $this->fieldQuotes.$this->fieldQuotes, (string)$id).$this->fieldQuotes
method

%DB -> create (string $table, ...$data)

line 77
Inserts a new record into the specified table with the provided data, optionally ignoring conflicts based on the 'ignore' flag.
if ($ignore = $data['ignore'] ?? false) unset($data['ignore'])
$columns = implode(comma, loop(array_keys($data), fn($k) => $this->quoteId($k)))
$values = implode(comma, array_fill(0, count($data), qm))
$query = "INSERT".($ignore ? $this->insertIgnore : void)." INTO $table ($columns) VALUES ($values)".($ignore ? $this->insertOnConflict : void)
$this->query($query, ...array_values(loop($data, fn($value) => is_a($value, obj::class) ? $value->id : $value)))
return $this->lastId() ?: ($data['id'] ?? null)
method

%DB -> lastId

line 86
Returns the ID of the last inserted row in the database.
$this->PDO->lastInsertId()
method

%DB -> change (string $table, string $where, ...$data):int

line 88
Updates records in the specified table based on the given conditions and data.
$whereCount = substr_count($where, qm)
$updates = isset($data['updates']) ? $data['updates'] : void
unset($data['updates'])
$updates .= (($wheres = array_slice(array_keys($data), $whereCount)) && $updates ? comma : void).loop($wheres, fn($key) => $this->quoteId($key).'=?', comma)
$query = "UPDATE $table SET $updates WHERE $where"
$args = array_values([...array_slice($data, $whereCount), ...array_slice($data, 0, $whereCount)])
return $this->query($query, ...$args)->rowCount()
method

%DB -> delete (string $table, string $where, ...$args):int

line 98
Deletes rows from the specified table based on the given condition. It returns the number of affected rows.
$this->query("DELETE FROM $table WHERE $where", ...$args)->rowCount()
method

%DB -> begin:?bool

line 99
Starts a new database transaction using PDO.
$this->PDO->beginTransaction()
method

%DB -> commit:?bool

line 100
Commits the current transaction in the database, making all changes made during the transaction permanent.
$this->PDO->commit()
method

%DB -> rollback:?bool

line 101
Rolls back the current transaction if one is active, reverting any changes made during the transaction.
$this->PDO->inTransaction() && $this->PDO->rollBack()
method

%DB -> transaction ($callback)

line 103
Executes a database transaction, allowing for a callback function to be run within the transaction context. If an error occurs, it rolls back to the last savepoint or the beginning of the transaction, ensuring data integrity.
if (!$this->PDO->inTransaction()){
	$this->begin
	try {
		$result = $callback()
		$this->commit
		return $result
	} catch (\Throwable $e){
		$this->rollback
		throw $e
	}
}
// Nested: a savepoint gives the inner unit its own rollback point, so its failure
// (e.g. an audit insert) is undone even when the outer transaction commits.
$sp = 'phlo_sp_'.(++$this->savepoint)
$this->PDO->exec('SAVEPOINT '.$sp)
try {
	$result = $callback()
	$this->PDO->exec('RELEASE SAVEPOINT '.$sp)
	$this->savepoint--
	return $result
} catch (\Throwable $e){
	$this->PDO->exec('ROLLBACK TO SAVEPOINT '.$sp)
	$this->PDO->exec('RELEASE SAVEPOINT '.$sp)
	$this->savepoint--
	throw $e
}
object

%JSONDB

/phlo/resources/DB/JSONDB.phlo

JSON file database driver. One JSONDB instance = one JSON file = one model table. No joins, no transactions, no schema introspection.

A model in a JSON file, for a set of records that stays small and readable: no joins, no transactions, and every write rewrites the whole file. It understands only equality and IN in a where, and raw SQL is refused outright, so keep the model plain. Move to SQLite the moment the file grows or two processes start writing.

jsondatabasefilestorage
static

JSONDB :: __handle

line 12
JSONDB::$__handle is a reference to the internal file handle used for accessing the JSON database specified by the $file path.
"JSONDB/$file"
method

%JSONDB -> __construct (private string $file)

line 13
The constructor initializes a JSONDB instance and ensures that the directory for the specified file exists, creating it if necessary.
$dir = dirname($this->file)
is_dir($dir) || mkdir($dir, 0755, true) || error("JSONDB: cannot create dir $dir")
prop

%JSONDB -> PDO:\PDO

line 17
This function triggers an error indicating that the JSONDB driver is not compatible with PDO.
error('JSONDB driver does not use PDO')
prop

%JSONDB -> fieldQuotes:string

line 18
Retrieves the quotes for a specified field in a JSONDB resource.
void
prop

%JSONDB -> lastInsertedId

line 19
Returns the ID of the last inserted record in the JSONDB.
null
method

%JSONDB -> quoteList (array $ids):string

line 26
Builds the quoted id list an IN clause expects.
dq.implode(dq.comma.dq, $ids).dq
method

%JSONDB -> objRead:array

line 28
Reads a JSON file and returns its contents as an associative array. If the file does not exist or is empty, it returns an empty array.
file_exists($this->file) ? json_decode(file_get_contents($this->file), true) ?: [] : []
method

%JSONDB -> objWrite (array $data):int|false

line 29
file_put_contents($this->file, json_encode(array_values($data), jsonPretty), LOCK_EX)
method

%JSONDB -> objNextId (array $data):int

line 30
Returns the next available ID for a new object in the given array of data by finding the maximum current ID and adding one, or returns 1 if the array is empty.
$data ? (int)max(array_column($data, 'id')) + 1 : 1
method

%JSONDB -> objFilter (array $data, string $where = void, ...$args):array

line 32
Filters an array of data based on specified conditions in the 'where' clause, returning only the matching rows.
if (!$where) return $data
$filtered = []
foreach ($data AS $row){
	$match = true
	$parts = preg_split('/\s+AND\s+/i', $where)
	$argIndex = 0
	foreach ($parts AS $part){
		if (preg_match('/^[`"]?(\w+)[`"]?\s*=\s*\?$/', trim($part), $m)){
			$column = $m[1]
			$value = $args[$argIndex++] ?? null
			if (($row[$column] ?? null) != $value) $match = false
		}
		elseif (preg_match('/^[`"]?(\w+)[`"]?\s+IN\s*\((.+)\)$/i', trim($part), $m)){
			$column = $m[1]
			$ids = array_map(fn($v) => trim($v, "\"' "), explode(comma, $m[2]))
			if (!in_array($row[$column] ?? null, $ids)) $match = false
		}
	}
	$match && $filtered[] = $row
}
return $filtered
method

%JSONDB -> objSelect (string $where = void, string $limit = void, string $order = void, ...$args):array

line 56
Selects objects from the JSON database based on specified conditions, with optional ordering and limiting of results.
$data = $this->objFilter($this->objRead(), $where, ...array_values($args))
if ($order){
	$desc = str_contains($order, 'DESC')
	$col = trim(preg_replace('/\s+(ASC|DESC)/i', void, $order), '` ')
	usort($data, fn($a, $b) => $desc ? ($b[$col] ?? 0) <=> ($a[$col] ?? 0) : ($a[$col] ?? 0) <=> ($b[$col] ?? 0))
}
$limit && $data = array_slice($data, 0, (int)$limit)
return $data
method

%JSONDB -> create (string $table, ...$data)

line 70
ignore compares ids loosely, so the string "7" and the number 7 count as one row.
if ($ignore = $data['ignore'] ?? false) unset($data['ignore'])
$all = $this->objRead()
$data['id'] ??= $this->objNextId($all)
foreach ($data AS $key => $value) is_a($value, 'obj') && $data[$key] = $value->id
if ($ignore){
	foreach ($all AS $row) if (($row['id'] ?? null) == $data['id']) return $data['id']
}
$all[] = $data
$this->objWrite($all)
$this->lastInsertedId = $data['id']
return $data['id']
method

%JSONDB -> change (string $table, string $where, ...$data):int

line 84
Updates rows in the specified table that match the given condition with new data, returning the number of rows changed.
$all = $this->objRead()
$whereCount = substr_count($where, qm)
$whereArgs = array_slice(array_values($data), 0, $whereCount)
$updates = array_slice($data, $whereCount, null, true)
$changed = 0
foreach ($all AS &$row){
	if ($this->objFilter([$row], $where, ...$whereArgs)){
		foreach ($updates AS $key => $value) $row[$key] = $value
		$changed++
	}
}
unset($row)
$this->objWrite($all)
return $changed
method

%JSONDB -> delete (string $table, string $where, ...$args):int

line 101
Deletes records from the specified table in the JSON database that match the given condition.
$all = $this->objRead()
$matching = $this->objFilter($all, $where, ...$args)
$matchIds = array_column($matching, 'id')
$remaining = array_values(array_filter($all, fn($row) => !in_array($row['id'] ?? null, $matchIds)))
$this->objWrite($remaining)
return count($matching)
method

%JSONDB -> load (string $table, string $columns = '*', string $where = void, string $joins = void, string $group = void, string $limit = void, string $order = void, ...$args):JSON_result

line 110
Loads data from a specified table in JSONDB, allowing for optional selection of columns, filtering with conditions, joining with other tables, grouping, limiting results, and ordering.
!$where && $args && $where = loop(array_keys($args), fn($column) => "$column=?", ' AND ')
$data = $this->objSelect($where, $limit, $order, ...array_values($args))
return %JSON_result($data)
method

%JSONDB -> query ($query, ...$args)

line 115
Executes a query against the JSONDB, but does not support raw SQL queries.
error('JSONDB driver does not support raw SQL queries')
method

%JSONDB -> begin:?bool

line 117
Begins a transaction in the JSONDB, allowing for multiple operations to be executed atomically.
null
method

%JSONDB -> commit:?bool

line 118
Commits the current transaction to the JSONDB, saving all changes made during the transaction.
null
method

%JSONDB -> rollback:?bool

line 119
Reverts the last transaction in the JSONDB, restoring the database to its previous state.
null
object

%JSON_result

/phlo/resources/DB/JSON.result.phlo

Minimal PDOStatement-like wrapper for JSONDB result arrays

Exists so JSONDB can hand back something that behaves like a PDO statement, which is what lets the ORM stay unaware of which driver answered. You do not build one yourself; you meet it as the return value of a JSONDB query.

jsondatabaseresult
static

JSON_result :: __handle

line 10
This property is used to access the internal handle of a JSON_result object.
null
prop

%JSON_result -> data:array

line 11
Accesses the `$data` property of the `JSON_result` object, which contains the parsed data from a JSON response.
[]
method

%JSON_result -> __construct (array $data)

line 12
Initializes a JSON_result object with the provided data array.
$this->data = $data
method

%JSON_result -> fetchAll ($mode = 2, $class = 'obj'):array

line 14
if ($mode === \PDO::FETCH_COLUMN) return loop($this->data, fn($row) => reset((array)$row))
if ($mode === \PDO::FETCH_KEY_PAIR){
	$out = []
	foreach ($this->data AS $row){
		$vals = array_values((array)$row)
		$out[$vals[0] ?? null] = $vals[1] ?? null
	}
	return $out
}
if (($mode & (\PDO::FETCH_CLASS | \PDO::FETCH_UNIQUE)) === (\PDO::FETCH_CLASS | \PDO::FETCH_UNIQUE)){
	$out = []
	foreach ($this->data AS $row){
		$o = new $class
		foreach ((array)$row AS $k => $v) $o->$k = $v
		$out[$row['id'] ?? count($out)] = $o
	}
	return $out
}
if (($mode & \PDO::FETCH_CLASS) === \PDO::FETCH_CLASS){
	$out = []
	foreach ($this->data AS $row){
		$o = new $class
		foreach ((array)$row AS $k => $v) $o->$k = $v
		$out[] = $o
	}
	return $out
}
return $this->data
method

%JSON_result -> fetchObject ($class = 'obj'):?obj

line 45
Fetches a single object of the specified class from the JSON result data, mapping the properties of the object to the values in the first row of the data.
if (!$this->data) return null
$row = reset($this->data)
$o = new $class
foreach ((array)$row AS $k => $v) $o->$k = $v
return $o
method

%JSON_result -> fetch ($mode = 2)

line 53
Fetches a result row from the JSON data, returning either the entire row or a specific column based on the provided mode.
if (!$this->data) return null
$row = reset($this->data)
if ($mode === \PDO::FETCH_COLUMN) return reset((array)$row)
return $row
method

%JSON_result -> fetchColumn ($col = 0)

line 60
Fetches a single column from the first row of the JSON result set, returning the value at the specified column index or false if not found.
if (!$this->data) return false
$row = reset($this->data)
$vals = array_values((array)$row)
return $vals[$col] ?? false
method

%JSON_result -> rowCount:int

line 67
Returns the number of rows in the JSON result set.
count($this->data)
object

%model

/phlo/resources/DB/model.phlo

Phlo ORM class with unified columns and schema

A model is a class with fields, and everything else follows from that: columns, form, validation and relations. Named arguments in a read are equality, so records(active: 1) is a where and nothing more; reach for query() when you need a comparison, a LIKE or an IN. A parent field gives back the record itself rather than an id, and children and many-relations are fetched once for a whole result set instead of per row. Set objValidate to have field rules enforced on save, objAudit to log every change, and objCache to keep reads in APCu.

ormmodeldatabaserecordsschema
static

model :: DB

line 12
This property accesses the database engine configuration for the model, throwing an error if none is configured.
error('No database engine configured for '.static::class)
static

model :: objCache

line 13
The model::$objCache property holds a cache for model objects, allowing for improved performance by reducing the need to repeatedly fetch the same data.
false
static

model :: objRecordLimit

line 14
Sets the maximum number of records that can be retrieved by the model.
10000
static

model :: objAudit

line 15
The `model::$objAudit` property is used to access the audit object associated with the model, allowing for tracking changes and modifications.
false
static

model :: objValidate

line 16
The model::$objValidate property indicates whether the object validation is enabled or disabled, returning false if validation is not active.
false
static

model :: idColumn

line 17
The model::$idColumn property specifies the name of the identifier column used in the model, defaulting to 'id'.
'id'
static

model :: idType

line 18
Defines the data type for the model's identifier as an integer.
'int'
static

model :: canView

line 20
This property indicates whether the current user has permission to view the model.
true
static

model :: canCreate

line 21
The model::$canCreate property indicates whether a new instance of the model can be created.
true
static

model :: canChange

line 22
This property indicates whether the model can be changed, returning true if changes are allowed.
true
static

model :: canDelete

line 23
The model::$canDelete property indicates whether the model instance can be deleted.
true
static

model :: state:obj

line 25
The `model::$state` property holds the current state of the model, including metadata, records, and errors.
%req->model ??= obj(meta: [], records: [], errors: [])
static

model :: columns:string

line 26
Retrieves the columns defined in the model, either from the static::$columns property or by calling the schema method if it exists.
if (isset(static::$columns)) return static::$columns
if (!method_exists(static::class, 'schema')) return static::$table.'.*'
$state = static::state()
$key = spl_object_id(static::DB()).':'.static::DB()->fieldQuotes.':'.static::$table
return $state->meta[static::class]['columns'][$key] ??= static::_columns()
static

model :: _columns:string

line 33
Retrieves the column names of the model's associated database table, formatted with field quotes.
$fq = static::DB()->fieldQuotes
$list = array_merge(...array_values(array_filter(loop(static::fields(), fn($field) => loop($field->objColumns, fn($col) => static::$table."$fq.$fq".$col)))))
return $fq.implode("$fq,$fq", $list).$fq
static

model :: fields:array

line 38
Returns the fields defined in the model's schema, or an empty array if no schema exists. If the schema is available, it retrieves the fields from the model's state metadata.
if (!method_exists(static::class, 'schema')) return static::$fields ?? []
$state = static::state()
return $state->meta[static::class]['fields'] ??= static::_fields()
static

model :: _fields:array

line 43
This property retrieves the fields defined in the model's schema, ensuring that none of the field names conflict with reserved keywords.
$reserved = ['table','order','fields','columns','create','change','delete','records','record','column','item','pair','DB','objCache','objState','objSave','objGet','objAudit','objValidate','objErrors','idColumn','idType']
$fields = loop(static::schema(), fn($field, $column) => last($field->name ??= $column, $field->type === 'parent' && $field->obj ??= $column, $field))
foreach ($reserved AS $word) isset($fields[$word]) && error("Reserved column name '$word' in ".static::class)
return $fields
static

model :: field ($name)

line 49
Accesses a specific field defined in the model's static fields array using the provided name.
static::fields()[$name]
static

model :: create (...$args):?static

line 51
Creates a new instance of the model, validating the input arguments and executing any defined lifecycle methods before saving the record to the database.
$class = static::class
if (static::objValidate() && !static::objRunValidation($args)) return null
$record = new $class(...$args)
method_exists(static::class, 'beforeSave') && $record->beforeSave()
method_exists(static::class, 'beforeCreate') && $record->beforeCreate()
$pk = static::idColumn()
return static::objAudit() ? static::transaction(fn() => static::objCreateCommit($record, $pk)) : static::objCreateCommit($record, $pk)
static

model :: objCreateCommit ($record, $pk):?static

line 61
Creates a new record in the model and triggers afterCreate and afterSave methods if they exist, while also logging the creation event for auditing purposes.
$id = static::createRecord(...$record)
$record = static::record(...[$pk => $record->$pk ?? $id])
method_exists(static::class, 'afterCreate') && $record->afterCreate()
method_exists(static::class, 'afterSave') && $record->afterSave()
static::objAudit() && audit::log($record, 'create', [], $record->objData)
return $record
static

model :: objRunValidation ($data):bool

line 70
Validates the provided data against the defined fields of the model, collecting any errors encountered during the validation process.
$errors = []
$fields = static::fields()
foreach ($data AS $column => $value){
	if (!($field = $fields[$column] ?? null) || !method_exists($field, 'objValidate')) continue
	if ($error = $field->objValidate($value)) $errors[$column] = $error
}
static::state()->errors[static::class] = $errors
return empty($errors)
static

model :: objErrors:array

line 81
Retrieves the error messages associated with the current model instance, returning an empty array if none are found.
static::state()->errors[static::class] ?? []
static

model :: createRecord (...$args)

line 82
Creates a new record in the specified database table using the provided arguments.
static::DB()->create(static::$table, ...$args)
static

model :: change ($where, ...$args):int

line 83
This method handles the change operation for a model, performing an audit if necessary and logging updates to the database.
if (!static::objAudit()) return static::DB()->change(static::$table, $where, ...$args)
$pk = static::idColumn()
$bindings = array_values(array_filter($args, 'is_int', ARRAY_FILTER_USE_KEY))
return static::transaction(function() use ($where, $args, $bindings, $pk){
	$old = static::DB()->query('SELECT '.static::$table.'.* FROM '.static::$table.' WHERE '.$where, ...$bindings)->fetchAll(\PDO::FETCH_CLASS, static::class)
	$result = static::DB()->change(static::$table, $where, ...$args)
	foreach ($old AS $record){
		$fresh = static::record(...[$pk => $record->$pk])
		$fresh && audit::log($fresh, 'update', $record->objData, $fresh->objData)
	}
	return $result
})
static

model :: delete ($where, ...$args):int

line 98
Deletes records from the database table associated with the model, invoking 'beforeDelete' and 'afterDelete' methods if they exist, and handling auditing and transactions as necessary.
if (method_exists(static::class, 'beforeDelete') || method_exists(static::class, 'afterDelete') || static::objAudit()){
	$records = static::DB()->query('SELECT '.static::$table.'.* FROM '.static::$table.' WHERE '.$where, ...$args)->fetchAll(\PDO::FETCH_CLASS, static::class)
	foreach ($records AS $record) method_exists(static::class, 'beforeDelete') && $record->beforeDelete()
	return static::objAudit() ? static::transaction(fn() => static::objDeleteCommit($where, $args, $records)) : static::objDeleteCommit($where, $args, $records)
}
return static::DB()->delete(static::$table, $where, ...$args)
static

model :: objDeleteCommit ($where, $args, $records):int

line 107
Deletes a record from the database based on the specified conditions and executes an optional after-delete method for each record, logging the deletion if auditing is enabled.
$result = static::DB()->delete(static::$table, $where, ...$args)
foreach ($records AS $record){
	method_exists(static::class, 'afterDelete') && $record->afterDelete()
	static::objAudit() && audit::log($record, 'delete', $record->objData, [])
}
return $result
static

model :: objLogChange ($where, ...$args):int

line 116
Logs changes to the specified model object, capturing the state before and after modifications.
static::change($where, ...$args)
method

%model -> objSave:?static

line 118
$pk = static::idColumn()
$pkValue = $this->$pk ?? $this->id ?? null
$pkValue || error('Can\'t save '.static::class.' record without '.$pk)
$old = static::record(...[$pk => $pkValue])
$isNew = !$old
method_exists(static::class, 'beforeSave') && $this->beforeSave($old)
if ($isNew){
	method_exists(static::class, 'beforeCreate') && $this->beforeCreate()
	$saved = static::objAudit() ? static::transaction(fn() => $this->objSaveCreate($pk, $pkValue)) : $this->objSaveCreate($pk, $pkValue)
}
else {
	method_exists(static::class, 'beforeChange') && $this->beforeChange($old)
	static::change($pk.'=?', $pkValue, ...$this)
	$saved = static::record(...[$pk => $pkValue])
	method_exists(static::class, 'afterChange') && $saved->afterChange($old)
}
method_exists(static::class, 'afterSave') && $saved->afterSave($old)
return $saved
method

%model -> objSaveCreate ($pk, $pkValue):?static

line 139
Creates a new record in the model and performs post-creation actions if defined, such as logging and auditing.
static::createRecord(...$this)
$saved = static::record(...[$pk => $pkValue])
method_exists(static::class, 'afterCreate') && $saved->afterCreate()
static::objAudit() && audit::log($saved, 'create', [], $saved->objData)
return $saved
static

model :: transaction ($callback)

line 147
Executes a database transaction using the provided callback function, ensuring that all operations within the transaction are completed successfully before committing.
static::DB()->transaction($callback)
static

model :: query:query

line 148
Executes a query on the model's database, returning the results based on the specified conditions.
phlo('query', class: static::class)
static

model :: column (...$args):array

line 150
Accesses a specific column from the records loaded by the model, using the fetchAll method with PDO's FETCH_COLUMN option.
static::recordsLoad($args, 'fetchAll', [\PDO::FETCH_COLUMN])
static

model :: item (...$args)

line 151
Loads records from the database using the specified arguments and fetch mode, returning a single column of data.
static::recordsLoad($args, 'fetch', [\PDO::FETCH_COLUMN])
static

model :: pair (...$args):array

line 152
Loads records from the database as a key-value pair array using the specified fetch mode.
static::recordsLoad($args, 'fetchAll', [\PDO::FETCH_KEY_PAIR])
static

model :: records (...$args):array

line 153
Retrieves all records from the model, loading them as instances of the model class.
static::recordsLoad($args, 'fetchAll', [\PDO::FETCH_CLASS|\PDO::FETCH_UNIQUE, static::class], true)
static

model :: recordCount (...$args)

line 154
Returns the total number of records in the model's database table.
static::item(...$args, columns: 'COUNT('.static::idColumn().')')
static

model :: record (...$args):?static

line 155
Retrieves a single record from the model's records based on the provided arguments, returning an error if multiple records are found.
count($records = static::records(...$args)) > 1 ? error('Multiple records for '.static::class) : (current($records) ?: null)
static

model :: recordsLoad ($args, $fetch, $fetchMode, $saveRelations = false)

line 162
Loads records and keeps a full record read on the request-local state for relation reuse.
$pk = static::idColumn()
$args['table'] ??= static::$table
$saveRelations && $args['columns'] ??= static::$table.'.'.$pk.' as _,'.static::columns()
isset(static::$joins) && debug && error('DEPRECATED: static $joins in '.static::class.'. Use getParent/getChildren/getMany instead.')
isset(static::$joins) && $args['joins'] = static::$joins.(isset($args['joins']) ? " $args[joins]" : void)
method_exists(static::class, 'where') && $args['where'] = static::where().(isset($args['where']) ? " AND $args[where]" : void)
isset(static::$group) && $args['group'] ??= static::$group
isset(static::$order) && $args['order'] ??= static::$order
if ($cacheKey = $args['cacheKey'] ?? null) unset($args['cacheKey'])
if ($duration = $args['cache'] ?? static::objCache()){
	unset($args['cache'])
	$cacheArgs = $args
	ksort($cacheArgs)
	$records = apcu($cacheKey ?? static::class.slash.md5(json_encode($cacheArgs)), fn() => static::DB()->load(...$args)->$fetch(...$fetchMode), $duration === true ? 86400 : $duration)
}
else $records = static::DB()->load(...$args)->$fetch(...$fetchMode)
if ($saveRelations && $records){
	$state = static::state()
	$state->records[static::class] = array_replace($state->records[static::class] ?? [], array_column($records, null, $pk))
	count($state->records[static::class]) > static::objRecordLimit() && $state->records[static::class] = array_slice($state->records[static::class], -static::objRecordLimit(), preserve_keys: true)
}
return $records
static

model :: objRel ($key):array

line 187
Accesses the relationship object defined in the model, returning metadata or invoking a method if it exists.
$state = static::state()
return $state->meta[static::class][$key] ??= method_exists(static::class, $key) ? static::$key() : static::$$key ?? []
prop

%model -> objState:array

line 192
The `$objState` property of the model holds the state of the object, including its parents, children, and many relationships, initialized as empty arrays.
['parents' => [], 'children' => [], 'many' => []]
method

%model -> objGet ($key)

line 193
Retrieves an object associated with the specified key from the parent, children, or many relationships in the model.
$this->getParent($key) ?? $this->getChildren($key) ?? $this->getMany($key)
method

%model -> objIn ($ids, $db = null):string

line 194
Returns a quoted list of IDs from the database or 'NULL' if no IDs are provided.
$ids ? ($db ?? static::DB())->quoteList($ids) : 'NULL'
method

%model -> objMirror (string $bucket, $key)

line 200
Mirrors a freshly loaded relation onto a stale reference to the same record.
$pk = $this->objData[static::idColumn()] ?? null
if ($pk === null) return
$canonical = static::state()->records[static::class][$pk] ?? null
if (!$canonical || $canonical === $this) return
if (array_key_exists($key, $this->objState[$bucket] ?? [])) return
if (array_key_exists($key, $canonical->objState[$bucket] ?? [])) $this->objState[$bucket][$key] = $canonical->objState[$bucket][$key]
method

%model -> getParent ($key)

line 209
Retrieves the parent object associated with a given key from the model's state, loading it if necessary.
if (array_key_exists($key, $this->objState['parents'])) return $this->objState['parents'][$key]
$state = static::state()
$parents = self::objRel('objParents')
if (!$relation = $parents[$key] ?? null) return
$isArray = is_array($relation)
$class = $isArray ? $relation['obj'] : $relation
$column = $isArray ? $relation['key'] ?? $key : $key
if (!$parentId = $this->objData[$column] ?? null) return $this->objState['parents'][$key] = null
if (!isset($state->records[$class][$parentId])){
	$idsToLoad = [$parentId => true]
	$allObjData = array_map(fn($record) => $record->objData, $state->records[static::class] ?? [])
	foreach ($parents as $pKey => $pRelation){
		$pIsArray = is_array($pRelation)
		$pClass = $pIsArray ? $pRelation['obj'] : $pRelation
		if ($pClass === $class) foreach (array_column($allObjData, $pIsArray ? $pRelation['key'] ?? $pKey : $pKey) as $pId) $pId && !isset($state->records[$class][$pId]) && $idsToLoad[$pId] = true
	}
	if ($idsToLoad = array_keys($idsToLoad)) $class::records(where: $class::idColumn().' IN ('.$this->objIn($idsToLoad, $class::DB()).')')
}
$parentObject = $state->records[$class][$parentId] ?? null
return $this->objState['parents'][$key] = $parentObject
method

%model -> getChildren ($key)

line 232
Retrieves the child objects associated with a given key from the model's state, loading them if they are not already present.
if (array_key_exists($key, $this->objState['children'])) return $this->objState['children'][$key]
$state = static::state()
if (!$relation = self::objRel('objChildren')[$key] ?? null) return
$isArray = is_array($relation)
$class = $isArray ? $relation['obj'] : $relation
$column = $isArray ? $relation['key'] : static::objShortName()
$toLoad = array_filter($state->records[static::class] ?? [], fn($p) => !array_key_exists($key, $p->objState['children']))
if ($toLoad){
	$fq = $class::DB()->fieldQuotes
	$children = $class::records(where: $fq.$column.$fq.' IN ('.$this->objIn(array_keys($toLoad), $class::DB()).')')
	foreach ($toLoad AS $parentRecord) $parentRecord->objState['children'][$key] = []
	foreach ($children AS $childId => $child) !is_null($pId = $child->objData[$column] ?? null) && isset($state->records[static::class][$pId]) && $state->records[static::class][$pId]->objState['children'][$key][$childId] = $child
}
$this->objMirror('children', $key)
return $this->objState['children'][$key] ?? []
method

%model -> getMany ($key)

line 250
Retrieves multiple related records based on a specified key from the model's state, loading them if they are not already present.
if (array_key_exists($key, $this->objState['many'])) return $this->objState['many'][$key]
$state = static::state()
if (!$relation = self::objRel('objMany')[$key] ?? null) return
$class = $relation['obj']
$toLoad = array_filter($state->records[static::class] ?? [], fn($p) => !array_key_exists($key, $p->objState['many']))
if ($toLoad){
	$fq = static::DB()->fieldQuotes
	$lk = $relation['localKey']
	$fk = $relation['foreignKey']
	$pivotRows = static::DB()->rows(table: $relation['table'], columns: $fq.$lk.$fq.comma.$fq.$fk.$fq, where: $fq.$lk.$fq.' IN ('.$this->objIn(array_keys($toLoad)).')')
	$targetIds = array_unique(array_map(fn($row) => $row->{$relation['foreignKey']}, $pivotRows ?: []))
	$targetRecords = $targetIds ? $class::records(where: $class::idColumn().' IN ('.$this->objIn($targetIds, $class::DB()).')') : []
	foreach ($toLoad AS $parentRecord) $parentRecord->objState['many'][$key] = []
	foreach ($pivotRows ?: [] AS $row){
		$parentId = $row->$lk
		$foreignId = $row->$fk
		if (isset($state->records[static::class][$parentId]) && isset($targetRecords[$foreignId])) $state->records[static::class][$parentId]->objState['many'][$key][$foreignId] = $targetRecords[$foreignId]
	}
}
$this->objMirror('many', $key)
return $this->objState['many'][$key] ?? []
method

%model -> getCount ($key):int

line 274
Retrieves the count of related records for a given key, loading data if necessary from the database and caching the result in the object's state.
if (array_key_exists($key, $this->objState['counts'] ?? [])) return $this->objState['counts'][$key]
$state = static::state()
if ($relation = self::objRel('objChildren')[$key] ?? null){
	$toLoad = array_filter($state->records[static::class] ?? [], fn($p) => !array_key_exists($key, $p->objState['counts'] ?? []))
	if ($toLoad){
		$isArray = is_array($relation)
		$class = $isArray ? $relation['obj'] : $relation
		$column = $isArray ? $relation['key'] : static::objShortName()
		$fq = $class::DB()->fieldQuotes
		$counts = $class::pair(columns: $fq.$column.$fq.', COUNT(*)', where: $fq.$column.$fq.' IN ('.$this->objIn(array_keys($toLoad), $class::DB()).')', group: $fq.$column.$fq)
		foreach ($toLoad as $id => $record) $record->objState['counts'][$key] = (int)($counts[$id] ?? 0)
	}
	$this->objMirror('counts', $key)
	return $this->objState['counts'][$key] ?? 0
}
if ($relation = self::objRel('objMany')[$key] ?? null){
	$toLoad = array_filter($state->records[static::class] ?? [], fn($p) => !array_key_exists($key, $p->objState['counts'] ?? []))
	if ($toLoad){
		$fq = static::DB()->fieldQuotes
		$localKey = $relation['localKey']
		$counts = static::DB()->load(table: $relation['table'], columns: $fq.$localKey.$fq.',COUNT(*)', where: $fq.$localKey.$fq.' IN ('.$this->objIn(array_keys($toLoad)).')', group: $fq.$localKey.$fq)->fetchAll(\PDO::FETCH_KEY_PAIR)
		foreach ($toLoad as $id => $record) $record->objState['counts'][$key] = (int)($counts[$id] ?? 0)
	}
	$this->objMirror('counts', $key)
	return $this->objState['counts'][$key] ?? 0
}
return 0
method

%model -> getLast ($key)

line 304
Retrieves the last child object associated with the specified key from the model's state, loading it if necessary.
if (array_key_exists($key, $this->objState['last_child'] ?? [])) return $this->objState['last_child'][$key]
$state = static::state()
if ($relation = self::objRel('objChildren')[$key] ?? null){
	$toLoad = array_filter($state->records[static::class] ?? [], fn($p) => !array_key_exists($key, $p->objState['last_child'] ?? []))
	if ($toLoad){
		$isArray = is_array($relation)
		$class = $isArray ? $relation['obj'] : $relation
		$column = $isArray ? $relation['key'] : static::objShortName()
		$childTable = $class::$table
		$fq = $class::DB()->fieldQuotes
		$qt = $fq.$childTable.$fq
		$qc = $fq.$column.$fq
		$ids = $this->objIn(array_keys($toLoad), $class::DB())
		$childPk = $class::idColumn()
		$joins = ' INNER JOIN (SELECT MAX('.$fq.$childPk.$fq.') AS last_id, '.$qc.' AS parent_id FROM '.$qt.' WHERE '.$qc.' IN ('.$ids.') GROUP BY '.$qc.') AS lcmax ON '.$qt.'.'.$fq.$childPk.$fq.' = lcmax.last_id'
		$lastChildren = $class::records(joins: $joins)
		foreach ($toLoad as $record) $record->objState['last_child'][$key] = null
		foreach ($lastChildren as $child) if (isset($state->records[static::class][$parentId = $child->objData[$column]])) $state->records[static::class][$parentId]->objState['last_child'][$key] = $child
	}
	$this->objMirror('last_child', $key)
	return $this->objState['last_child'][$key] ?? null
}
return null
static

model :: objResolveClass ($name):string

line 330
Resolves the class name for the specified model object, allowing dynamic class handling in Phlo.
$name
static

model :: objShortName ($class = null):string

line 331
This expression retrieves the class name of the current model or the specified class if it exists, using the static context.
$class ?? static::class
static

model :: objParents:array

line 333
Returns the parent objects associated with the current model, utilizing the defined schema and filtering fields of type 'parent'.
if (property_exists(static::class, 'objParents')) return static::$objParents
if (!method_exists(static::class, 'schema')) return []
return loop(array_filter(static::fields(), fn($f) => $f->type === 'parent'), fn($f, $c) => $f->key ? arr(obj: static::objResolveClass($f->obj), key: $f->key) : (static::objResolveClass($f->obj ?? $c)))
static

model :: objChildren:array

line 339
Returns the children objects associated with the current model, leveraging the defined schema and filtering fields of type 'child'.
if (property_exists(static::class, 'objChildren')) return static::$objChildren
if (!method_exists(static::class, 'schema')) return []
return loop(array_filter(static::fields(), fn($f) => $f->type === 'child'), fn($f, $c) => $f->key ? arr(obj: static::objResolveClass($f->obj), key: $f->key) : (static::objResolveClass($f->obj ?? $c)))
static

model :: objMany:array

line 345
Returns the 'objMany' property if it exists; otherwise, it retrieves an array of related objects based on the schema definition.
if (property_exists(static::class, 'objMany')) return static::$objMany
if (!method_exists(static::class, 'schema')) return []
return loop(array_filter(static::fields(), fn($f) => $f->type === 'many'), fn($f) => arr(obj: static::objResolveClass($f->obj), table: $f->table, localKey: $f->localKey ?? static::objShortName(), foreignKey: $f->foreignKey ?? $f->obj))
object

%MySQL

/phlo/resources/DB/MySQL.phlo

MySQL handler via DB class

Reads host, database, user and password from the mysql section of %creds. It is the default assumption of the ORM, so a model that names no engine of its own ends up here.

mysqlpdodatabasesql
prop

%MySQL -> PDO:\PDO

line 12
Creates a new PDO instance for connecting to a MySQL database using the provided credentials.
new \PDO('mysql:host='.%creds->mysql->host.';dbname='.%creds->mysql->database, %creds->mysql->user, %creds->mysql->password)
object

%PostgreSQL

/phlo/resources/DB/PostgreSQL.phlo

PostgreSQL resource

Quotes identifiers with double quotes rather than backticks and has no INSERT IGNORE, so a duplicate is skipped with ON CONFLICT DO NOTHING. Postgres folds an unquoted name to lower case, so a column called createdAt in a schema is not the same one you get back unquoted.

postgresqlpdodatabasesql
prop

%PostgreSQL -> PDO:\PDO

line 12
Creates a new PDO instance for connecting to a PostgreSQL database using the provided credentials.
new PDO('pgsql:host='.%creds->postgresql->host.';dbname='.%creds->postgresql->database, %creds->postgresql->user, %creds->postgresql->password)
prop

%PostgreSQL -> fieldQuotes:string

line 13
Returns the field name wrapped in double quotes for PostgreSQL compatibility.
dq
prop

%PostgreSQL -> insertIgnore:string

line 14
Inserts a new record into a PostgreSQL database, ignoring the operation if a record with the same unique key already exists.
void
prop

%PostgreSQL -> insertOnConflict:string

line 15
Inserts a new record into a PostgreSQL database, and if a conflict occurs (e.g., a duplicate key), it does nothing instead of throwing an error.
' ON CONFLICT DO NOTHING'
method

%PostgreSQL -> lastId

line 21
Asks for the last id inside a savepoint, so a failed lastval() cannot abort the transaction.
$savepoint = $this->PDO->inTransaction()
$savepoint && $this->PDO->exec('SAVEPOINT phlo_lastid')
try {
	$id = $this->PDO->lastInsertId()
} catch (\Throwable $e){
	$id = false
}
if ($savepoint) $this->PDO->exec($id === false ? 'ROLLBACK TO SAVEPOINT phlo_lastid' : 'RELEASE SAVEPOINT phlo_lastid')
return $id
object

%Qdrant

/phlo/resources/DB/Qdrant.phlo

Embeddings resource with Qdrant

Embeddings are cached in APCu for four weeks per input, so repeating a search costs nothing at the AI end. create() opens a collection at 1536 dimensions, the length of an OpenAI vector, so state the size yourself when another engine fills it. search() without input sends a zero vector, which lists a collection rather than searching it.

qdrantembeddingsvectorsearchai
method

%Qdrant -> get (string $input, ?string $model = null):array

line 11
Retrieves an embedding for the given input string using the specified model, caching the result for 28 days.
apcu('embedding/'.token(input: $input), fn($input) => %AI->embedding(input: $input, model: $model), 86400 * 28)
method

%Qdrant -> collections:array

line 13
Retrieves an array of collection names from the Qdrant API response.
array_column($this->request('collections')->result->collections, 'name')
method

%Qdrant -> create ($collection, $size = 1536, $distance = 'Cosine'):bool

line 14
Creates a new collection in Qdrant with specified vector size and distance metric.
$this->request("collections/$collection", PUT: arr(vectors: arr(size: $size, distance: $distance)))->status === 'ok'
method

%Qdrant -> upsert ($collection, $id, $input, ...$payload)

line 15
This function updates or inserts a point in a specified Qdrant collection using the provided ID and input vector, along with optional payload data.
$this->request("collections/$collection/points", PUT: arr(points: [arr(id: $id, vector: $this->get($input), payload: $payload ?: null)]))->result->operation_id
method

%Qdrant -> delete ($collection, ...$ids)

line 16
Deletes specified points from a Qdrant collection based on their IDs.
$this->request("collections/$collection/points/delete", POST: arr(points: $ids))->result
method

%Qdrant -> search ($collection, $input = null, $top = 100):array

line 17
Searches for points in the specified Qdrant collection based on the input vector and returns the top results.
create($this->request("collections/$collection/points/search", POST: arr(vector: is_null($input) ? array_fill(0, 1536, 0) : $this->get($input), top: $top, with_payload: true))->result, fn($record) => $record->id, fn($record) => last($record = array_merge(get_object_vars($record), get_object_vars($record->payload)), obj(...array_filter($record, fn($key) => $key !== 'payload', ARRAY_FILTER_USE_KEY))))
method

%Qdrant -> drop ($collection)

line 18
Drops the specified collection from Qdrant by sending a DELETE request to the appropriate endpoint.
$this->request("collections/$collection", DELETE: true)->result
method

%Qdrant -> request ($uri, ...$data)

line 20
Sends an HTTP request to the specified Qdrant server URI with optional data and returns the decoded JSON response.
json_decode(HTTP(%creds->qdrant->server.$uri, %creds->qdrant->key ? ['api-key: '.%creds->qdrant->key] : [], true, ...$data))
object

%query

/phlo/resources/DB/query.phlo

Fluent query builder for Phlo ORM

For everything named arguments cannot say: eq, gt, like, in, isNull, order, limit and offset, chained and closed with records, record, column, item or count. Column names are quoted for the driver you are on, so the same chain works on MySQL and Postgres. Values go in as bindings, so a search box can be passed straight through.

querybuilderormdatabasesql
prop

%query -> class

line 11
Retrieves the class type of the current query object.
prop

%query -> conditions

line 12
Defines conditions for filtering results in a query.
[]
prop

%query -> bindings

line 13
The `query->$bindings` retrieves the bindings associated with a query in Phlo, allowing access to the parameters used in the query execution.
[]
prop

%query -> orderBy

line 14
Specifies the field by which the results of a query should be ordered.
prop

%query -> limitVal

line 15
Sets the maximum number of results to return from a query in Phlo.
prop

%query -> offsetVal

line 16
Retrieves the current offset value for pagination in a query.
method

%query -> fq:string

line 17
This expression retrieves the field quotes from the database class associated with the current instance, or defaults to 'bt' if the class is not set.
($class = $this->class) ? $class::DB()->fieldQuotes : bt
method

%query -> q ($column):string

line 18
Validates the specified column name for the query builder and formats it by surrounding each part with the fully qualified name.
preg_match('/^[A-Za-z_][A-Za-z0-9_.]*$/', $column) || error("Invalid column for query builder: $column")
$fq = $this->fq
return implode(dot, array_map(fn($part) => $fq.$part.$fq, explode(dot, $column)))
method

%query -> eq ($column, $value):static

line 24
Filters the query results to include only those where the specified column equals the given value.
$this->where($this->q($column)." = ?", $value)
method

%query -> neq ($column, $value):static

line 25
Filters results where the specified column is not equal to the given value.
$this->where($this->q($column)." != ?", $value)
method

%query -> gt ($column, $value):static

line 26
Adds a condition to the query that filters results where the specified column is greater than the given value.
$this->where($this->q($column)." > ?", $value)
method

%query -> gte ($column, $value):static

line 27
Generates a query condition that checks if the value of the specified column is greater than or equal to a given value.
$this->where($this->q($column)." >= ?", $value)
method

%query -> lt ($column, $value):static

line 28
Adds a condition to the query to filter results where the specified column is less than the given value.
$this->where($this->q($column)." < ?", $value)
method

%query -> lte ($column, $value):static

line 29
Adds a condition to the query that filters results where the specified column is less than or equal to the given value.
$this->where($this->q($column)." <= ?", $value)
method

%query -> like ($column, $value):static

line 30
Adds a condition to the query that checks if the specified column contains a value similar to the provided value using the SQL LIKE operator.
$this->where($this->q($column)." LIKE ?", $value)
method

%query -> in ($column, array $values):static

line 31
Adds a condition to the query to filter results where the specified column's value is in the provided array of values.
$this->where($this->q($column)." IN (".implode(comma, array_fill(0, count($values), qm)).")", ...$values)
method

%query -> isNull ($column):static

line 32
Checks if the specified column in the query is null.
$this->where($this->q($column)." IS NULL")
method

%query -> notNull ($column):static

line 33
Adds a condition to the query to ensure that the specified column is not null.
$this->where($this->q($column)." IS NOT NULL")
method

%query -> between ($column, $min, $max):static

line 34
Filters results to include only those where the specified column's value is between the given minimum and maximum values.
$this->where($this->q($column)." BETWEEN ? AND ?", $min, $max)
method

%query -> raw ($sql, ...$bindings):static

line 35
Executes a raw SQL query with the provided bindings, allowing for dynamic query construction.
$this->where($sql, ...$bindings)
method

%query -> where ($condition, ...$values):static

line 36
Adds a condition to the query with the specified values for binding. It allows for dynamic filtering of results based on the provided condition.
$this->conditions[] = $condition
foreach ($values AS $v) $this->bindings[] = $v
return $this
method

%query -> order ($order):static

line 42
Sets the order for the query using the specified $order value and returns the current instance.
$this->orderBy = $order
return $this
method

%query -> limit ($limit):static

line 47
Sets the maximum number of results to return from a query, defined by the $limit parameter.
$this->limitVal = $limit
return $this
method

%query -> offset ($offset):static

line 52
Sets the offset value for the query, allowing for pagination of results.
$this->offsetVal = $offset
return $this
method

%query -> build:array

line 56
Builds a query argument array based on specified conditions, order, limit, and bindings for use in database operations.
$where = $this->conditions ? implode(' AND ', $this->conditions) : void
$limit = $this->limitVal ? ($this->offsetVal ? "$this->offsetVal,$this->limitVal" : "$this->limitVal") : void
$args = ['where' => $where ?: void, 'order' => $this->orderBy ?: void, 'limit' => $limit ?: void]
foreach ($this->bindings AS $b) $args[] = $b
return $args
prop

%query -> records:array

line 64
Retrieves a collection of records from the specified class using the provided build parameters.
($class = $this->class) && $class::records(...$this->build)
prop

%query -> record:?model

line 65
Retrieves a record from the specified class using the provided build parameters.
($class = $this->class) && $class::record(...$this->build)
prop

%query -> column:array

line 66
Accesses a specific column from a query result set using the defined class and build parameters.
($class = $this->class) && $class::column(...$this->build)
prop

%query -> item

line 67
Retrieves an item from a class using the provided build parameters.
($class = $this->class) && $class::item(...$this->build)
prop

%query -> count

line 68
Returns the total number of records in the specified class using the recordCount method.
($class = $this->class) && $class::recordCount(...$this->build)
method

%query -> delete:int

line 69
Deletes records from the database based on specified conditions. If no conditions are provided, an error is raised.
$class = $this->class
$where = $this->conditions ? implode(' AND ', $this->conditions) : error('Cannot delete without conditions')
return $class::delete($where, ...$this->bindings)
object

%SQLite

/phlo/resources/DB/SQLite.phlo

SQLite resource

One file, one database, given as a path: %SQLite('/path/db.sqlite'). It writes with a lock over the whole file, so it fits a single site or a worker but not a set of processes writing at once. Perfect where you want the ORM without a server.

sqlitepdodatabasesql
static

SQLite :: __handle

line 12
This property holds the handle to the SQLite database connection, allowing interaction with the database.
"SQLite/$file"
method

%SQLite -> __construct (private string $file)

line 13
Initializes a new SQLite instance with the specified database file.
prop

%SQLite -> PDO:\PDO

line 14
Creates a new PDO instance for SQLite using the specified file.
new PDO('sqlite:'.$this->file)
prop

%SQLite -> insertIgnore:string

line 15
Inserts a new record into the SQLite database, ignoring the operation if a record with the same primary key already exists.
' OR IGNORE'

Last updated on 23 August 2026

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.