ruskit 0.1.5

A modern web framework for Rust inspired by Laravel
Documentation
{% 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 %}