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.dart';

class DatabaseConfig {
  static Map<String, dynamic> get config => {
    'driver': Khadem.env.getOrDefault('DB_CONNECTION', 'mysql'),
    'host': Khadem.env.getOrDefault('DB_HOST', 'localhost'),
    'port': Khadem.env.getInt('DB_PORT'),
    'database': Khadem.env.get('DB_DATABASE'),
    'username': Khadem.env.get('DB_USERNAME'),
    'password': Khadem.env.get('DB_PASSWORD'),
    'run_migrations': true,
    'run_seeders': false,
  };
}

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

⚡ Tip: Use environment variables for sensitive connection details

SQLite

File-based, no server

sqlite

MySQL

Production-ready RDBMS

mysql

PostgreSQL

Advanced features, JSON

pgsql

Migrations

Creating Migrations

dart
// database/migrations/0_create_users_table.dart
import 'package:khadem/khadem_dart.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.rememberToken();
      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 primary key
  table.id('custom_id'); // Custom primary key name

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

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

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

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

  // JSON & Arrays
  table.json('metadata'); // JSON
  table.array('tags'); // ARRAY

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

  // Foreign Keys
  table.foreignId('user_id'); // Foreign key column
  table.morphs('commentable'); // Polymorphic relation columns
});
dart
// Using the Migrator class
import 'package:khadem/khadem_dart.dart';

final migrator = Migrator(Khadem.db);

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

// Rollback last migration
await migrator.downAll();

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

// Refresh database (rollback + migrate)
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.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
  List<String> get fillable => [
    'name',
    'email',
    'password',
    'created_at',
    'updated_at'
  ];

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

  @override
  List<String> get appends => [];

  @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) {
    return switch (key) {
      'id' => id = value,
      'name' => name = value,
      'email' => email = value,
      'password' => password = value,
      'created_at' => createdAt = value,
      'updated_at' => updatedAt = value,
      _ => null
    };
  }

  @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');
await user.save();

// Find user by ID
final user = await User().findById(1);

// Find users by condition
final users = await User().findWhere('email', '=', 'john@example.com');

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

// Delete user
await user.delete();

// Query with relationships
final userWithPosts = await User().load(['posts']);

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

// Append computed attributes
user.append(['full_name']);
user.appendAttribute('display_name');

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

// Get all except specific attributes
final userData = user.except(['password']);
dart
// One-to-One relationship
class User extends KhademModel<User> {
  @override
  Map<String, RelationDefinition> get relations => {
    'profile': RelationDefinition<Profile>(
      type: RelationType.hasOne,
      relatedTable: 'profiles',
      localKey: 'id',
      foreignKey: 'user_id',
      factory: () => Profile(),
    ),
  };
}

class Profile extends KhademModel<Profile> {
  @override
  Map<String, RelationDefinition> get relations => {
    'user': RelationDefinition<User>(
      type: RelationType.belongsTo,
      relatedTable: 'users',
      localKey: 'user_id',
      foreignKey: 'id',
      factory: () => User(),
    ),
  };
}

// One-to-Many relationship
class User extends KhademModel<User> {
  @override
  Map<String, RelationDefinition> get relations => {
    'posts': RelationDefinition<Post>(
      type: RelationType.hasMany,
      relatedTable: 'posts',
      localKey: 'id',
      foreignKey: 'user_id',
      factory: () => Post(),
    ),
  };
}

class Post extends KhademModel<Post> {
  @override
  Map<String, RelationDefinition> get relations => {
    'user': RelationDefinition<User>(
      type: RelationType.belongsTo,
      relatedTable: 'users',
      localKey: 'user_id',
      foreignKey: 'id',
      factory: () => User(),
    ),
  };
}

// Many-to-Many relationship (simplified)
class User extends KhademModel<User> {
  @override
  Map<String, RelationDefinition> get relations => {
    'roles': RelationDefinition<Role>(
      type: RelationType.belongsToMany,
      relatedTable: 'roles',
      localKey: 'id',
      foreignKey: 'user_id',
      factory: () => Role(),
    ),
  };
}

Query Builder

Fluent Query Interface

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

final db = Khadem.db;

// Get all records
final users = await db.table('users').get();

// Get first record
final user = await db.table('users').first();

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

// Where clauses
final activeUsers = await db.table('users')
    .where('is_active', '=', true)
    .get();

// Count records
final count = await db.table('users').count();

// Check existence
final exists = await db.table('users')
    .where('email', '=', 'john@example.com')
    .exists();

// Insert record
final userId = await db.table('users').insert({
  'name': 'John Doe',
  'email': 'john@example.com',
  'created_at': DateTime.now(),
  'updated_at': DateTime.now(),
});

// Update records
await db.table('users')
    .where('id', '=', 1)
    .update({
      'name': 'John Smith',
      'updated_at': DateTime.now(),
    });

// Delete records
await db.table('users')
    .where('id', '=', 1)
    .delete();

🔧 Interface: QueryBuilderInterface

📊 Supports: Complex queries, joins, aggregations

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

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

// Joins
final users = await db.table('users')
    .join('posts', 'users.id', '=', 'posts.user_id')
    .select(['users.name', 'posts.title'])
    .get();

// Grouping and aggregation
final stats = await db.table('posts')
    .select(['user_id'])
    .groupBy('user_id')
    .having('COUNT(*)', '>', 5)
    .get();

// Raw queries
final users = await db.table('users')
    .whereRaw('created_at > ?', [DateTime.now().subtract(Duration(days: 30))])
    .get();
dart
// Raw SQL queries using connection
final connection = db.connection;

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

// Raw insert
final result = await connection.execute(
  'INSERT INTO users (name, email) VALUES (?, ?)',
  ['John Doe', 'john@example.com']
);

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

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

Seeders

Database Seeding

dart
// database/seeders/database_seeder.dart
import 'package:khadem/khadem_dart.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
    await Khadem.db.table('users').insert([
      {
        'name': 'John Doe',
        'email': 'john@example.com',
        'password': 'hashed_password',
        'created_at': DateTime.now(),
        'updated_at': DateTime.now(),
      },
      {
        'name': 'Jane Smith',
        'email': 'jane@example.com',
        'password': 'hashed_password',
        'created_at': DateTime.now(),
        'updated_at': DateTime.now(),
      },
    ]);
  }
}

📁 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 extends Seeder {
  @override
  void run() {
    // Create 10 users using factory
    for (var i = 0; i < 10; i++) {
      db.table('users').insert(UserFactory.makeWithOverrides({
        'name': 'User $i',
        'email': 'user$i@example.com',
      }));
    }
  }
}
dart
// Using SeederManager
import 'package:khadem/khadem_dart.dart';

final seederManager = SeederManager();

// Register seeders
seederManager.register(DatabaseSeeder());
seederManager.register(UserSeeder());

// Run all seeders
await seederManager.runAll();

// Run specific seeder
await seederManager.run('user_seeder');

Database Transactions

Transaction Management

dart
// Using DatabaseManager transactions
await Khadem.db.connection.transaction((trx) async {
  final userId = await trx.table('users').insert({
    'name': 'John Doe',
    'email': 'john@example.com',
  });

  await trx.table('profiles').insert({
    'user_id': userId,
    'bio': 'Software developer',
  });
});

// Manual transaction using connection
final connection = Khadem.db.connection;
await connection.transaction((trx) async {
  final userId = await trx.table('users').insert({
    'name': 'John Doe',
    'email': 'john@example.com',
  });

  await trx.table('profiles').insert({
    'user_id': userId,
    'bio': 'Software developer',
  });
});

🔒 Ensures: Data consistency and integrity

⚡ Performance: Batch operations efficiently

On this page