Database & ORM

Powerful database abstraction with migrations, query builder, and ORM capabilities for Khadem Dart.

DatabaseManagerKhademModelQueryBuilderMigrationsSeeders

Database Configuration

Database Manager Setup

dart
// config/database.dart
import 'package:khadem/khadem.dart';

class DatabaseConfig {
  static Map<String, dynamic> get config => {
    'driver': 'mysql', // Currently only MySQL is supported
    'host': Khadem.env.getOrDefault('DB_HOST', 'localhost'),
    'port': Khadem.env.getInt('DB_PORT', 3306),
    'database': Khadem.env.get('DB_DATABASE'),
    'username': Khadem.env.get('DB_USERNAME'),
    'password': Khadem.env.get('DB_PASSWORD'),
    'charset': 'utf8mb4',
    'collation': 'utf8mb4_unicode_ci',
    'run_migrations': true,
    'run_seeders': false,
  };
}

// In your .env file:
// DB_HOST=localhost
// DB_PORT=3306
// DB_DATABASE=khadem_app
// DB_USERNAME=root
// DB_PASSWORD=secret

💡 Note: Configuration is loaded from config/database.dart

⚡ Tip: Use environment variables for sensitive connection details

MySQL

Fully Supported

Production-ready relational database with full ORM support, migrations, and query builder.

mysql✨ Recommended

SQLite & PostgreSQL

Coming Soon

SQLite and PostgreSQL drivers are planned for future releases.

sqlitepgsql

Migrations

Creating Migrations

dart
// database/migrations/0_create_users_table.dart
import 'package:khadem/khadem.dart';

class CreateUsersTable implements MigrationFile {
  @override
  String get name => 'create_users_table';

  @override
  Future<void> up(SchemaBuilder schema) async {
    schema.create('users', (Blueprint table) {
      table.id();
      table.string('name');
      table.string('email').unique();
      table.timestamp('email_verified_at').nullable();
      table.string('password');
      table.timestamps();
    });
  }

  @override
  Future<void> down(SchemaBuilder schema) async {
    schema.dropIfExists('users');
  }
}

📁 Location: database/migrations/

🔄 Naming: timestamp_description.dart

dart
// Available column types in Blueprint
schema.create('products', (Blueprint table) {
  // Primary Keys
  table.id(); // Auto-incrementing BIGINT primary key
  table.id('custom_id'); // Custom primary key name

  // Strings
  table.string('name', length: 100); // VARCHAR(100)
  table.text('description'); // TEXT

  // Numbers
  table.integer('quantity'); // INT
  table.bigInteger('views'); // BIGINT
  table.float('price'); // FLOAT

  // Dates & Times
  table.date('birth_date'); // DATE
  table.timestamp('created_at'); // TIMESTAMP

  // Boolean & Enum
  table.boolean('is_active'); // BOOLEAN/TINYINT
  table.enumColumn('status', ['pending', 'approved', 'rejected']);

  // JSON
  table.json('metadata'); // JSON column

  // Special helper methods
  table.timestamps(); // Adds created_at and updated_at
  table.softDeletes(); // Adds deleted_at for soft deletes

  // Foreign Keys & Relations
  table.foreignId('user_id'); // BIGINT UNSIGNED for foreign keys
  table.morphs('commentable'); // Polymorphic relation columns
  // Creates: commentable_type (VARCHAR) and commentable_id (BIGINT)
});
dart
// Using the Migrator class
import 'package:khadem/khadem.dart';

final migrator = Migrator(Khadem.db);

// Run all pending migrations
await migrator.upAll();

// Rollback all migrations
await migrator.downAll();

// Reset and re-run all migrations
await migrator.reset();

// Refresh database (downAll + upAll)
await migrator.refresh();

// Show migration status
await migrator.status();

// Run specific migration
await migrator.up('create_users_table');

// Rollback specific migration
await migrator.down('create_users_table');

Khadem Model (ORM)

Model Definition

dart
// app/models/user.dart
import 'package:khadem/khadem.dart';

class User extends KhademModel<User> with Timestamps, HasRelationships {
  User({
    this.name,
    this.email,
    this.password,
    int? id,
  }) {
    this.id = id;
  }

  String? name;
  String? email;
  String? password;

  @override
  String get tableName => 'users';

  @override
  List<String> get fillable => ['name', 'email', 'password'];

  @override
  List<String> get hidden => ['password'];

  @override
  List<String> get protected => ['password']; // Never in JSON

  @override
  Map<String, dynamic> get casts => {
    'created_at': DateTime,
    'updated_at': DateTime,
  };

  @override
  Map<String, RelationDefinition> get relations => {
    'posts': hasMany<Post>(
      foreignKey: 'user_id',
      relatedTable: 'posts',
      factory: () => Post(),
    ),
  };

  @override
  dynamic getField(String key) {
    return switch (key) {
      'id' => id,
      'name' => name,
      'email' => email,
      'password' => password,
      'created_at' => createdAt,
      'updated_at' => updatedAt,
      _ => null
    };
  }

  @override
  void setField(String key, dynamic value) {
    switch (key) {
      case 'id': id = value; break;
      case 'name': name = value; break;
      case 'email': email = value; break;
      case 'password': password = value; break;
      case 'created_at': createdAt = value; break;
      case 'updated_at': updatedAt = value; break;
    }
  }

  @override
  User newFactory(Map<String, dynamic> data) {
    return User()..fromJson(data);
  }
}

📁 Location: app/models/

🔗 Extends: KhademModel

dart
// Create a new user
final user = User(name: 'John Doe', email: 'john@example.com', password: 'secret');
await user.save();
print('User created with ID: ${user.id}');

// Find user by ID using query builder
final foundUser = await User().query.where('id', '=', 1).first();
if (foundUser != null) {
  print('Found: ${foundUser.name}');
}

// Find users by condition
final users = await User().query.where('email', '=', 'john@example.com').get();
print('Found ${users.length} users');

// Update user
user.name = 'John Smith';
await user.save();

// Delete user
await user.delete();

// Mass assignment with fillable protection
final newUser = User()..fromJson({
  'name': 'Jane Doe',
  'email': 'jane@example.com',
  'password': 'secret',
  'role': 'admin', // Ignored if not in fillable
});
await newUser.save();
dart
// Load relationships
final user = await User().query.where('id', '=', 1).first();
await user?.load('posts');

// Eager load with query
final users = await User().query.withRelations(['posts']).get();

// Check if relation is loaded
if (user?.isRelationLoaded('posts') ?? false) {
  final posts = user!.getRelation('posts');
}

// Load missing relations only
await user?.loadMissing(['posts', 'profile']);

// Get only specific attributes
final userData = user?.only(['name', 'email']);

// Get all except specific attributes
final userData = user?.except(['password']);

Query Builder

Fluent Query Interface

dart
// Using query builder through models
import 'package:khadem/khadem.dart';

// Get all records
final users = await User().query.get();

// Get first record
final user = await User().query.first();

// Get specific columns
final users = await User().query.select(['name', 'email']).get();

// Where clauses
final activeUsers = await User().query
    .where('is_active', '=', true)
    .get();

// Multiple where conditions
final filteredUsers = await User().query
    .where('is_active', '=', true)
    .where('role', '=', 'admin')
    .get();

// Ordering
final latestUsers = await User().query
    .orderBy('created_at', direction: 'DESC')
    .limit(10)
    .get();

// Pagination
final result = await User().query.paginate(perPage: 15, page: 1);
print('Total: ${result.total}');
print('Current page: ${result.currentPage}');
print('Data: ${result.data.length} items');

// Count records
final count = await User().query.count();

// Check existence
final exists = await User().query
    .where('email', '=', 'john@example.com')
    .exists();

// Insert record using model
final user = User(
  name: 'John Doe',
  email: 'john@example.com',
);
await user.save();

// Update records using model
final user = await User().query.where('id', '=', 1).first();
if (user != null) {
  user.name = 'John Smith';
  await user.save();
}

// Delete records using model
final user = await User().query.where('id', '=', 1).first();
await user?.delete();

🔧 Interface: QueryBuilderInterface

📊 Supports: Complex queries, joins, aggregations

dart
// Complex where conditions
final users = await User().query
    .where('age', '>', 18)
    .where('city', '=', 'New York')
    .orWhere('city', '=', 'Los Angeles')
    .get();

// Ordering and pagination
final users = await User().query
    .orderBy('created_at', direction: 'DESC')
    .limit(10)
    .offset(20)
    .get();

// whereHas - Query based on relationships
final usersWithPosts = await User().query
    .whereHas('posts', (query) {
      query.where('status', '=', 'published');
    })
    .get();

// Grouping with having
final query = User().query
    .select(['role', 'COUNT(*) as count'])
    .groupBy('role')
    .having('count', '>', 5);

// Raw queries
final users = await User().query
    .whereRaw('created_at > ?', [DateTime.now().subtract(Duration(days: 30))])
    .get();

// JSON queries
final users = await User().query
    .whereJsonContains('settings->notifications', 'email')
    .get();
dart
// Raw SQL queries using DatabaseManager
import 'package:khadem/khadem.dart';

final db = Khadem.db;
final connection = db.connection;

// Raw select
final results = await connection.query(
  'SELECT * FROM users WHERE id = ?',
  [1]
);

// Raw insert
await connection.execute(
  'INSERT INTO users (name, email, created_at, updated_at) VALUES (?, ?, ?, ?)',
  ['John Doe', 'john@example.com', DateTime.now(), DateTime.now()]
);

// Raw update
await connection.execute(
  'UPDATE users SET name = ?, updated_at = ? WHERE id = ?',
  ['John Smith', DateTime.now(), 1]
);

// Raw delete
await connection.execute('DELETE FROM users WHERE id = ?', [1]);

// Use whereRaw in query builder for safer raw queries
final users = await User().query
    .whereRaw('YEAR(created_at) = ?', [2024])
    .get();

Seeders

Database Seeding

dart
// database/seeders/database_seeder.dart
import 'package:khadem/khadem.dart';

class DatabaseSeeder implements Seeder {
  @override
  String get name => 'database_seeder';

  @override
  Future<void> run() async {
    // Call other seeders
    await UserSeeder().run();
    await PostSeeder().run();
    await CategorySeeder().run();
  }
}

// database/seeders/user_seeder.dart
class UserSeeder implements Seeder {
  @override
  String get name => 'user_seeder';

  @override
  Future<void> run() async {
    // Create test users using models
    final users = [
      User(name: 'John Doe', email: 'john@example.com', password: 'hashed_password'),
      User(name: 'Jane Smith', email: 'jane@example.com', password: 'hashed_password'),
    ];
    
    for (final user in users) {
      await user.save();
    }
  }
}

📁 Location: database/seeders/

🎯 Purpose: Populate database with test data

dart
// Using factories for seeding
class UserFactory {
  static Map<String, dynamic> make() {
    return {
      'name': 'Test User',
      'email': 'test@example.com',
      'password': 'hashed_password',
      'created_at': DateTime.now(),
      'updated_at': DateTime.now(),
    };
  }

  static Map<String, dynamic> makeWithOverrides(Map<String, dynamic> overrides) {
    return {...make(), ...overrides};
  }
}

// In seeder
class UserSeeder implements Seeder {
  @override
  String get name => 'user_seeder';

  @override
  Future<void> run() async {
    // Create 10 users using factory
    for (var i = 0; i < 10; i++) {
      final userData = UserFactory.makeWithOverrides({
        'name': 'User $i',
        'email': 'user$i@example.com',
      });
      
      final user = User()..fromJson(userData);
      await user.save();
    }
  }
}
dart
// Using Seeder class
import 'package:khadem/khadem.dart';

// Create and run database seeder
final databaseSeeder = DatabaseSeeder();
await databaseSeeder.run();

// Run specific seeder
final userSeeder = UserSeeder();
await userSeeder.run();

// Or manually seed data
await User(name: 'Admin', email: 'admin@example.com', password: 'hashed').save();
await User(name: 'User', email: 'user@example.com', password: 'hashed').save();

Database Transactions

Transaction Management

dart
// Using DatabaseManager transactions
import 'package:khadem/khadem.dart';

final connection = Khadem.db.connection;

await connection.transaction((trx) async {
  // Create user
  final user = User(name: 'John Doe', email: 'john@example.com');
  await user.save();
  
  // Create profile for the user
  final profile = Profile(userId: user.id, bio: 'Software developer');
  await profile.save();
  
  // If any operation fails, the entire transaction is rolled back
});

// Transaction with error handling
try {
  await connection.transaction((trx) async {
    final user = User(name: 'Jane Doe', email: 'jane@example.com');
    await user.save();
    
    // This might fail
    final order = Order(userId: user.id, total: 100.0);
    await order.save();
  });
  print('Transaction completed successfully');
} catch (e) {
  print('Transaction failed: $e');
  // All changes are automatically rolled back
}

🔒 Ensures: Data consistency and integrity

⚡ Performance: Batch operations efficiently

On this page