{% extends "docs_base.html" %}
{% block title %}Models - Ruskit Documentation{% endblock %}
{% block content %}
<div class="prose prose-slate max-w-none">
<h1>Models</h1>
<p>Models in Ruskit represent the business logic and database operations of your application. They work in conjunction with entities to provide a clean separation between data structure and business logic.</p>
<h2 id="generating-models">Generating Models</h2>
<p>You can quickly generate a new model using the <code>kit make:model</code> command:</p>
<pre><code class="language-bash"># Generate a model
cargo kit make:model Post
# Generate a model with migration
cargo kit make:model Post --migration
# After generating a model with migration, run:
cargo kit migrate</code></pre>
<p>This will:</p>
<ol>
<li>Create a new entity file in <code>src/app/entities/</code></li>
<li>Create a new model file in <code>src/app/models/</code></li>
<li>Add both to their respective <code>mod.rs</code> files</li>
<li>Generate:
<ul>
<li>Entity struct with validation fields</li>
<li>Model implementation with business logic</li>
<li>Migration setup</li>
<li>Basic query methods</li>
</ul>
</li>
</ol>
<h2 id="model-implementation">Model Implementation</h2>
<p>Here's an example of a complete model implementation:</p>
<pre><code class="language-rust">use validator::ValidationError;
use crate::framework::database::{
model::{Model, HasMany, Rules, Validate, ValidationRules},
query_builder::QueryBuilder,
DatabaseError,
migration::Migration,
};
use crate::app::entities::{User, Post};
use async_trait::async_trait;
impl User {
/// Get recent records
pub async fn recent(limit: i64) -> Result<Vec<Self>, DatabaseError> {
QueryBuilder::table(Self::table_name())
.order_by("created_at", "DESC")
.limit(limit)
.get::<Self>()
.await
}
/// Get all posts by this user
pub fn posts(&self) -> HasMany<Post> {
HasMany::new::<Self>()
}
}
impl ValidationRules for User {
fn validate_rules(&self) -> Result<(), ValidationError> {
self.name.validate(Rules::new().required().min(3).max(255))?;
self.email.validate(Rules::new().required().email())?;
Ok(())
}
}
#[async_trait]
impl Model for User {
fn table_name() -> &'static str {
"users"
}
fn id(&self) -> i64 {
self.id
}
fn migrations() -> Vec<Migration> {
vec![
Migration::new(
"1739887638_create_users_table",
"CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)",
"DROP TABLE users"
),
]
}
}
</code></pre>
<h2 id="relationships">Relationships</h2>
<p>Ruskit supports various types of relationships. Here are examples:</p>
<h3>HasMany Relationship</h3>
<pre><code class="language-rust">impl User {
pub fn posts(&self) -> HasMany<Post> {
HasMany::new::<Self>()
}
}</code></pre>
<h3>BelongsTo Relationship</h3>
<pre><code class="language-rust">impl Post {
pub fn user(&self) -> BelongsTo<User> {
BelongsTo::new::<Self>()
}
}</code></pre>
<h2 id="validation-rules">Validation Rules</h2>
<p>Models implement validation rules through the <code>ValidationRules</code> trait:</p>
<pre><code class="language-rust">impl ValidationRules for Post {
fn validate_rules(&self) -> Result<(), ValidationError> {
self.title.validate(Rules::new().required().max(255))?;
self.content.validate(Rules::new().required())?;
Ok(())
}
}</code></pre>
<h2 id="query-methods">Query Methods</h2>
<p>You can add custom query methods to your models:</p>
<pre><code class="language-rust">impl Post {
pub async fn recent(limit: i64) -> Result<Vec<Self>, DatabaseError> {
QueryBuilder::table(Self::table_name())
.order_by("created_at", "DESC")
.limit(limit)
.get::<Self>()
.await
}
}</code></pre>
<h2 id="best-practices">Best Practices</h2>
<ol>
<li>
<strong>Separation of Concerns</strong>:
<ul>
<li>Keep data structure in entities</li>
<li>Keep business logic in models</li>
<li>Use relationships for model associations</li>
</ul>
</li>
<li>
<strong>Validation</strong>:
<ul>
<li>Implement <code>ValidationRules</code> for all models</li>
<li>Keep validation rules close to business logic</li>
<li>Use descriptive error messages</li>
</ul>
</li>
<li>
<strong>Relationships</strong>:
<ul>
<li>Use appropriate relationship types</li>
<li>Keep relationship methods descriptive</li>
<li>Consider eager loading for performance</li>
</ul>
</li>
<li>
<strong>Query Methods</strong>:
<ul>
<li>Keep query methods focused and reusable</li>
<li>Use the query builder for complex queries</li>
<li>Consider pagination for large datasets</li>
</ul>
</li>
</ol>
</div>
<div class="mt-8 border-t border-gray-200 pt-6">
<div class="flex justify-between">
<a href="/docs/entities" class="text-primary-600 hover:text-primary-900">← Entities</a>
<a href="/docs/validation" class="text-primary-600 hover:text-primary-900">Validation →</a>
</div>
</div>
{% endblock %}
{% block toc %}
<div class="hidden xl:block">
<div class="sticky top-16 space-y-4">
<div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
<h4 class="text-sm font-medium text-gray-900 mb-4">On this page</h4>
<nav class="space-y-2">
<a href="#generating-models" class="block text-gray-500 hover:text-gray-900 text-sm">Generating Models</a>
<a href="#model-implementation" class="block text-gray-500 hover:text-gray-900 text-sm">Model Implementation</a>
<a href="#relationships" class="block text-gray-500 hover:text-gray-900 text-sm">Relationships</a>
<a href="#validation-rules" class="block text-gray-500 hover:text-gray-900 text-sm">Validation Rules</a>
<a href="#query-methods" class="block text-gray-500 hover:text-gray-900 text-sm">Query Methods</a>
<a href="#best-practices" class="block text-gray-500 hover:text-gray-900 text-sm">Best Practices</a>
</nav>
</div>
</div>
</div>
{% endblock %}