# Eloquent: Getting Started
- [Introduction](#introduction)
- [Generating Models](#generating-models)
- [Defining Models](#defining-models)
- [Table Names](#table-names)
- [Primary Keys](#primary-keys)
- [Fillable Attributes](#fillable-attributes)
- [Attribute Casting](#attribute-casting)
- [Retrieving Models](#retrieving-models)
- [Inserting & Updating](#inserting--updating)
- [Deleting Models](#deleting-models)
- [Hybrid Persistence](#hybrid-persistence)
## Introduction
Magic's Eloquent ORM provides a beautiful, simple Active Record implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data, insert new records, and update existing ones.
Unlike traditional ORMs, Magic's Eloquent supports **Hybrid Persistence**. Your models can persist to a local SQLite database, a remote REST API, or both—giving you offline-first capabilities out of the box.
## Generating Models
To generate a new model, use the `make:model` Magic CLI command:
```bash
magic make:model Post
```
### Available Options
| Option | Shortcut | Description |
|--------|----------|-------------|
| `--migration` | `-m` | Create a database migration |
| `--seeder` | `-s` | Create a database seeder |
| `--factory` | `-f` | Create a model factory |
| `--controller` | `-c` | Create a controller |
| `--policy` | `-p` | Create a policy |
| `--all` | `-a` | Create all related files |
### The `--all` Flag
The `-a` flag is the most convenient way to scaffold a complete feature:
```bash
magic make:model Product --all
```
This single command creates:
- `lib/app/models/product.dart`
- `lib/database/migrations/m__create_products_table.dart`
- `lib/database/seeders/product_seeder.dart`
- `lib/database/factories/product_factory.dart`
- `lib/app/policies/product_policy.dart`
- `lib/app/controllers/product_controller.dart`
- `lib/resources/views/product/` (index, show, create, edit)
## Defining Models
Models typically live in the `lib/app/models` directory:
```dart
import 'package:fluttersdk_magic/fluttersdk_magic.dart';
class User extends Model with HasTimestamps, InteractsWithPersistence {
@override
String get table => 'users';
@override
String get resource => 'users';
@override
List get fillable => ['name', 'email', 'avatar'];
@override
Map get casts => {
'email_verified_at': 'datetime',
'is_active': 'bool',
'settings': 'json',
};
// Typed accessors
String? get name => getAttribute('name') as String?;
set name(String? value) => setAttribute('name', value);
String? get email => getAttribute('email') as String?;
Carbon? get emailVerifiedAt => getAttribute('email_verified_at') as Carbon?;
bool get isActive => getAttribute('is_active') as bool? ?? false;
// Static helpers (required for type-safe queries)
static Future find(dynamic id) =>
InteractsWithPersistence.findById(id, User.new);
static Future> all() =>
InteractsWithPersistence.allModels(User.new);
}
```
### Table Names
The `table` property specifies which database table corresponds to your model:
```dart
@override
String get table => 'users';
```
### Primary Keys
By default, Eloquent assumes your primary key is `id`. You may override this:
```dart
@override
String get primaryKey => 'user_id';
@override
bool get incrementing => false; // For UUID keys
```
### Fillable Attributes
Define which attributes can be mass-assigned:
```dart
@override
List get fillable => ['name', 'email', 'avatar'];
```
Only these attributes will be set when using `fill()`:
```dart
user.fill({
'name': 'John',
'email': 'john@example.com',
'is_admin': true, // Ignored - not in fillable
});
```
## Attribute Casting
The `casts` property converts attributes to common data types:
```dart
@override
Map get casts => {
'created_at': 'datetime', // Returns Carbon
'updated_at': 'datetime',
'is_active': 'bool', // Returns bool
'settings': 'json', // Returns Map
'age': 'int', // Returns int
'price': 'double', // Returns double
};
```
### Available Cast Types
| Cast | Returns |
|------|---------|
| `datetime` | `Carbon` (Magic's date/time wrapper) |
| `bool` | `bool` |
| `int` | `int` |
| `double` | `double` |
| `json` | `Map` or `List` |
### Accessing Cast Attributes
```dart
final user = await User.find(1);
Carbon? createdAt = user.createdAt; // Carbon instance
bool isActive = user.isActive; // bool
Map settings = user.settings; // Map
```
## Retrieving Models
### Retrieving All Models
```dart
final users = await User.all();
for (final user in users) {
print(user.name);
}
```
### Retrieving A Single Model
```dart
final user = await User.find(1);
if (user != null) {
print(user.name);
}
```
### Refreshing Models
Reload a model's attributes from the database:
```dart
await user.refresh();
```
## Inserting & Updating
### Inserting Models
To create a new record, instantiate a model, set attributes, and call `save()`:
```dart
final user = User()
..fill({
'name': 'John Doe',
'email': 'john@example.com',
});
await user.save();
print(user.id); // The new auto-generated ID
```
### Updating Models
To update an existing model, retrieve it, modify attributes, and call `save()`:
```dart
final user = await User.find(1);
if (user != null) {
user.name = 'Updated Name';
await user.save();
}
```
### Dirty Checking
Track which attributes have changed:
```dart
final user = await User.find(1);
print(user.isDirty()); // false
user.name = 'New Name';
print(user.isDirty()); // true
print(user.isDirty('name')); // true
print(user.getDirty()); // {'name': 'New Name'}
```
## Deleting Models
```dart
final user = await User.find(1);
if (user != null) {
await user.delete();
print(user.exists); // false
}
```
## Hybrid Persistence
Magic's unique feature is hybrid persistence—syncing between local SQLite and remote REST API.
### Configuration
Control where your model persists:
```dart
// Local database only (offline mode)
@override
bool get useLocal => true;
@override
bool get useRemote => false;
// Remote API only (online mode)
@override
bool get useLocal => false;
@override
bool get useRemote => true;
// Both (hybrid mode - default)
@override
bool get useLocal => true;
@override
bool get useRemote => true;
```
### API Resource Endpoint
The `resource` property maps to your REST API:
```dart
@override
String get resource => 'users';
// Maps to:
// GET /users -> all()
// GET /users/{id} -> find(id)
// POST /users -> save() (insert)
// PUT /users/{id} -> save() (update)
// DELETE /users/{id} -> delete()
```
### How Hybrid Mode Works
When using hybrid mode:
1. **Read operations** check local database first, then sync from API
2. **Write operations** persist to both local and remote
3. **Offline support** is automatic—writes queue until online
> [!TIP]
> Use hybrid mode for offline-first apps that sync to a backend API.