Database & ORM
Larastvel uses SeaORM as its ORM, providing an Eloquent-like experience in Rust.
Configuration
Configure your database in config/database.toml:
driver = "sqlite" # sqlite, postgres, mysql
host = "127.0.0.1"
port = 3306
database = "larastvel"
username = "root"
password = ""Transactions
Use DatabaseManager::transaction() to run work inside a database transaction — it commits on success and rolls back on error:
use larastvel_core::database::DatabaseManager;
let db = DatabaseManager::new(&config);
db.transaction(|txn| {
Box::pin(async move {
let user = user::ActiveModel {
name: Set("John".to_string()),
..Default::default()
}
.insert(txn)
.await?;
wallet::ActiveModel {
user_id: Set(user.id),
balance: Set(0),
..Default::default()
}
.insert(txn)
.await?;
Ok(())
})
})
.await?;begin_transaction() returns a live transaction handle for manual commit() / rollback() control. Note that SQLite does not support nested transactions — only one transaction() can be open at a time per connection.
Models
use larastvel_core::table;
#[table("users")]
pub struct User {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub email: String,
}The #[table] macro generates the full SeaORM entity boilerplate (DeriveEntityModel, Relation, ActiveModelBehavior) plus a DbModel wrapper automatically.
DbModel Trait
The DbModel trait provides Laravel-style helpers on top of SeaORM entities:
use larastvel_core::sea_orm::{IntoActiveModel, Set};
let users = User::all().await?;
let user = User::find(1).await?.unwrap();
// Insert — DbModel::insert takes an ActiveModel
let created = User::insert(user::ActiveModel {
name: Set("John".to_string()),
email: Set("john@example.com".to_string()),
..Default::default()
})
.await?;
// Update — DbModel::update takes an ActiveModel
let mut active: user::ActiveModel = created.clone().into_active_model();
active.name = Set("Jane".to_string());
User::update(active).await?;
// Delete — DbModel::delete takes the model
User::delete(created).await?;Query Scopes
Use the #[scope] macro to define reusable query constraints on your models. The scope function receives a Select<Entity> as its first parameter (removed from the public API) and returns a modified query:
use larastvel_core::scope;
impl User {
/// Find users with at least N followers.
#[scope]
fn popular(query: Select<Entity>, min_followers: i64) -> Select<Entity> {
query.filter(Column::Followers.gte(min_followers))
}
}The generated method chains onto Self::query() automatically:
let users = User::popular(100).all().await?;Laravel's scope_ prefix convention is supported — scope_popular becomes popular():
impl User {
#[scope]
fn scope_recent(query: Select<Entity>, days: i64) -> Select<Entity> {
query.filter(Column::CreatedAt.gte(chrono::Utc::now().naive_utc() - chrono::Duration::days(days)))
}
}
// Call without the scope_ prefix:
let users = User::recent(7).all().await?;Generate a scaffolded scope with:
larastvel make scope popularModel Observers
Observers allow you to hook into model lifecycle events — created, updated, deleted, saved, and retrieved — by defining handler methods on a dedicated struct.
use larastvel_core::observer;
struct UserObserver;
#[observer(User)]
impl UserObserver {
async fn created(&self, user: Model) {
// React to new user creation
}
async fn deleted(&self, user: Model) {
// React to user deletion
}
}
// Register the observer at app boot:
UserObserver::observe();Only the hook methods you define are wired up — if you omit updated, no ModelUpdated listener is registered.
The DbModel trait automatically dispatches these events:
find()→ModelRetrievedinsert()→ModelCreated+ModelSavedupdate()→ModelUpdated+ModelSaveddelete()→ModelDeleted
Generate a scaffolded observer with:
larastvel make observer UserObserverMigrations
Generate and run migrations via CLI:
cargo run -p larastvel-cli -- make migration create_users_table
cargo run -p larastvel-cli -- migrateSeeders
The #[seeder] macro generates a Seeder trait implementation. See the full reference for details, arguments, and generated code.
use larastvel_core::sea_orm::DbConn;
#[seeder]
struct UserSeeder;
impl UserSeeder {
async fn seed(conn: &DbConn) -> Result<(), Box<dyn std::error::Error>> {
// Insert seed data
Ok(())
}
}Run seeders:
cargo run -p larastvel-cli -- db:seedModel Factories
The #[factory] macro generates a ModelFactory trait implementation. See the full reference for details.
use larastvel_core::models::factory::Faker;
use larastvel_core::sea_orm::Set;
use sea_orm::entity::prelude::*;
#[derive(Debug, Default)]
#[factory("user")]
pub struct UserFactory;
impl UserFactory {
fn define() -> crate::models::user::ActiveModel {
user::ActiveModel {
name: Set(Faker::name()),
email: Set(Faker::email()),
..Default::default()
}
}
}factory_create::<UserFactory>().await?;
factory_create_count::<UserFactory>(10).await?;API Resources
The #[api_resource] macro generates an ApiResource trait implementation. See the full reference for details, including how to use single-model and collection transforms.
use larastvel_core::api_resource;
#[api_resource(crate::models::user::Model)]
#[derive(Debug)]
struct UserResource;
impl UserResource {
fn to_array(model: &crate::models::user::Model) -> serde_json::Value {
serde_json::json!({
"id": model.id,
"name": model.name,
"email": model.email,
})
}
}let resource = UserResource::make(user);
let json = resource.to_array();
let collection = UserResource::collection(users);
let json = collection.to_array();Generate a scaffolded resource with:
larastvel make resource UserResourceFor spec-compliant JSON:API output — resource objects, sparse fieldsets, ?include= compound documents — see the JSON:API Resources reference.