Queries
A query starts with Model::query(), or with Query::table() when you have no model. Builder methods return the query, and a terminal method runs it. A terminal method takes the database, as &db, or a transaction, as &mut *tx:
use orbit_orm::{Database, Direction, Model, Operator, Query};
let db = Database::connect("mysql://user:password@127.0.0.1/app").await?;
let popular = Post::query()
.where_op("views", Operator::Ge, 100)
.order_by("views", Direction::Desc)
.limit(10)
.get(&db)
.await?;
Filters
Each filter has an or_ form that joins it with OR. A filter without the prefix joins with AND.
where_eq(column, value)andor_where_eqcompare for equality.where_op(column, operator, value)andor_where_optake anOperator:Lt,Le,Gt,Ge,NeorLike.where_in(column, values)andor_where_inmatch any of a list. An empty list matches no rows.where_null(column),where_not_null(column)and theiror_forms check forNULL.
A None value follows Laravel. where_eq(column, None) is IS NULL, and where_op with Operator::Ne and None is IS NOT NULL. Any other operator with None matches no rows.
Grouped conditions
where_group and or_where_group put their conditions in parentheses. The closure receives an empty query and adds its conditions to it:
let posts = Post::query()
.where_op("views", Operator::Ge, 100)
.where_group(|q| q.where_eq("title", "Hello").or_where_eq("title", "Hi"))
.get(&db)
.await?;
Only the conditions are kept from the group. order_by, limit and the other settings inside it are ignored. An empty group adds nothing.
Order, columns and paging
order_by(column, Direction::Asc)orDirection::Descsorts the rows. Later calls sort within the earlier ones.select(&[columns])chooses the columns thatget,firstand the methods built on them return. Without it, every column is selected.limit(n)returns at mostnrows, andoffset(n)skips the firstn. An offset without a limit is allowed.
Reading rows
get(db)returns every matching row, as aVec.first(db)returns the first row, orNone. It has no default order, so callorder_bywhen the row matters.first_or_fail(db)isfirst, and returnsError::NotFoundwhen no row matches.find(db, id)returns the row whose primary key isid, orNone.find_or_fail(db, id)isfind, and returnsError::NotFoundwhen no row matches.count(db)returns the number of matching rows, as ani64.exists(db)returns whether any row matches.
let post = Post::query().find_or_fail(&db, 1).await?;
let newest = Post::query().order_by("id", Direction::Desc).first(&db).await?;
Writing rows
The writes take the columns and their values as pairs. Each column must be in the model's FILLABLE list, or the write returns Error::NotFillable.
insert(db, values)inserts one row and returns its auto-increment id, as au64.replace(db, values)runs MySQL'sREPLACE INTO. It deletes the row that has the same unique or primary key, then inserts the new one, and returns the rows affected. Because the old row is deleted, its id changes, and columns that are not invaluestake their defaults. This is not Laravel'supsert.update(db, values)sets the columns on every matching row, and returns the number of rows changed. An empty list of values changes nothing and returns0. Order and limit are ignored.delete(db)deletes every matching row, and returns the number deleted. Order and limit are ignored.
A query with no conditions applies update and delete to every row of the table.
let id = Query::<Post>::table()
.insert(&db, &[("title", "Hello".into())])
.await?;
let affected = Query::<Post>::table()
.replace(&db, &[("title", "Hello".into())])
.await?;
Values and names
Every value is sent as a bound parameter, never as SQL text. Table and column names are quoted with backticks. A name that is empty or contains a backtick is an error, Error::InvalidIdentifier.
Row locks
for_update() adds FOR UPDATE to get, first, find, count and exists. Inside a transaction, the lock lasts until the transaction ends. Outside one, the statement runs in autocommit and the lock lasts only for that statement. See Transactions.
let post = Post::query()
.where_eq("id", 1)
.for_update()
.first_or_fail(&mut *tx)
.await?;
Orbit