ruskit 0.1.5

A modern web framework for Rust inspired by Laravel
Documentation
# Models and Entities

Ruskit uses a clear separation between entities (data structures) and models (business logic). Entities represent your database tables and their fields, while models contain the business logic, relationships, and database operations.

## Generating Models

You can quickly generate a new model using the `kit make:model` command:

```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
```

This will:
1. Create a new entity file in `src/app/entities/`
2. Create a new model file in `src/app/models/`
3. Add the entity to `src/app/entities/mod.rs`
4. Add the model to `src/app/models/mod.rs`
5. Generate:
   - Entity struct with validation fields
   - Model implementation with business logic
   - Migration setup
   - Basic query methods

## Entity Structure

Entities define your data structure and validation rules:

```rust
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use rustavel_derive::GenerateValidationFields;
use crate::framework::database::model::{Field, ModelValidation};
use validator::ValidationError;

#[derive(Debug, Serialize, Deserialize, FromRow, GenerateValidationFields)]
pub struct Post {
    #[sqlx(default)]
    pub id: i64,
    pub title: String,
    pub content: String,
    pub user_id: i64,
    pub created_at: i64,
    pub updated_at: i64,
}
```

## Model Implementation

Models contain your business logic, relationships, and database operations:

```rust
use validator::ValidationError;
use crate::framework::database::{
    model::{Model, Rules, Validate, ValidationRules},
    query_builder::QueryBuilder,
    DatabaseError,
    migration::Migration,
};
use crate::app::entities::Post;
use async_trait::async_trait;

impl Post {
    /// 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
    }
}

impl ValidationRules for Post {
    fn validate_rules(&self) -> Result<(), ValidationError> {
        self.title.validate(Rules::new().required().min(3).max(255))?;
        self.content.validate(Rules::new().required())?;
        Ok(())
    }
}

#[async_trait]
impl Model for Post {
    fn table_name() -> &'static str {
        "posts"
    }

    fn id(&self) -> i64 {
        self.id
    }

    fn migrations() -> Vec<Migration> {
        vec![
            Migration::new(
                "create_posts_table",
                "CREATE TABLE posts (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    title TEXT NOT NULL,
                    content TEXT NOT NULL,
                    user_id INTEGER NOT NULL,
                    created_at INTEGER NOT NULL,
                    updated_at INTEGER NOT NULL,
                    FOREIGN KEY (user_id) REFERENCES users(id)
                )",
                "DROP TABLE posts"
            )
        ]
    }
}
```

## Model Validation

Ruskit provides a powerful validation system using the `GenerateValidationFields` derive macro and the `ValidationRules` trait:

```rust
// In your entity file:
#[derive(Debug, Serialize, Deserialize, FromRow, GenerateValidationFields)]
pub struct User {
    #[sqlx(default)]
    pub id: i64,
    pub name: String,
    pub email: String,
    pub created_at: i64,
    pub updated_at: i64,
}

// In your model file:
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(())
    }
}
```

The validation will be automatically applied when creating or updating records.

## Relationships

Ruskit provides three types of relationships: `HasOne`, `HasMany`, and `BelongsTo`. Here's how to use them:

### HasMany Relationship

Used when a model has multiple related records:

```rust
use crate::framework::database::model::HasMany;
use crate::app::entities::Post;

impl User {
    /// Get all posts by this user
    pub fn posts(&self) -> HasMany<Post> {
        HasMany::new::<Self>()
    }
}

// Usage:
let user = User::find(1).await?;
let posts = user.posts().get().await?;  // Get all posts
let new_post = user.posts().create(post).await?;  // Create a new post
```

### BelongsTo Relationship

Used when a model belongs to another model:

```rust
use crate::framework::database::model::BelongsTo;
use crate::app::entities::User;

impl Post {
    /// Get the user who created this post
    pub fn user(&self) -> BelongsTo<User> {
        BelongsTo::new::<Self>()
    }
}

// Usage:
let post = Post::find(1).await?;
let user = post.user().get().await?;  // Get the related user
```

### HasOne Relationship

Used when a model has exactly one related record:

```rust
use crate::framework::database::model::HasOne;
use crate::app::entities::{User, Profile};

impl User {
    /// Get user's profile
    pub fn profile(&self) -> HasOne<Self, Profile> {
        HasOne::new("user_id")
    }
}

// Usage:
let user = User::find(1).await?;
let profile = user.profile().get().await?;  // Get the related profile
```

### Custom Foreign Keys

You can specify custom foreign keys for any relationship:

```rust
// Custom foreign key for HasMany
HasMany::with_key("author_id")

// Custom foreign key for BelongsTo
BelongsTo::with_key("author_id")

// Custom foreign key for HasOne
HasOne::new("author_id")
```

## Basic Operations

Every model automatically gets these basic operations:

```rust
// Find by ID
let post = Post::find(1).await?;

// Get all records
let all_posts = Post::all().await?;

// Create a new record
let post = Post {
    id: 0,
    title: "My First Post".to_string(),
    content: "Hello World!".to_string(),
    user_id: 1,
    created_at: chrono::Utc::now().timestamp(),
    updated_at: chrono::Utc::now().timestamp(),
};
let created_post = Post::create(post).await?;

// Create with validation
let validated_post = Post::create_validated(post).await?;
```

## Query Builder

The `QueryBuilder` provides a fluent interface for building SQL queries:

```rust
// Complex query example
let posts = QueryBuilder::table("posts")
    .select("posts.*, users.name as author_name")
    .where_clause("published", "=", true)
    .where_clause("created_at", ">", yesterday_timestamp)
    .order_by("created_at", "DESC")
    .limit(10)
    .offset(20)
    .get::<Post>()
    .await?;
```

## Best Practices

1. **Separation of Concerns**:
   - Keep data structure in entities
   - Keep business logic in models
   - Use relationships for model associations

2. **Validation**:
   - Use `GenerateValidationFields` for automatic validation field generation
   - Implement `ValidationRules` for custom validation logic
   - Validate data before saving to database

3. **Naming Conventions**:
   - Use singular names for entity and model structs (`Post`, not `Posts`)
   - Use snake_case for table names (`posts`, not `Posts`)
   - Use descriptive names for relationships and methods

4. **Field Types**:
   - Use appropriate types for your fields (e.g., `i64` for IDs and timestamps)
   - Consider using `Option<T>` for nullable fields
   - Use `bool` for boolean fields (SQLite stores them as INTEGER)

5. **Timestamps**:
   - Always include `created_at` and `updated_at` fields
   - Use UNIX timestamps (seconds since epoch) for consistency

6. **Security**:
   - Never expose sensitive fields directly
   - Implement proper access control in your models
   - Validate input data before creating/updating records

7. **Performance**:
   - Add indexes for frequently queried fields
   - Use appropriate field types for better performance
   - Consider adding caching for frequently accessed data