Flowforge Logo
Essentials

Database Schema

Set up your database for drag-and-drop Kanban functionality.

Required Fields

Your model needs these essential fields for Kanban functionality:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string('title');                         // Card title
    $table->string('status');                        // Column identifier
    $table->flowforgePositionColumn();               // Drag-and-drop ordering
    $table->timestamps();
});

Position Column

The flowforgePositionColumn() method is crucial for drag-and-drop functionality. It handles database-specific collations automatically:

// Default column name 'position'
$table->flowforgePositionColumn();

// Custom column name
$table->flowforgePositionColumn('sort_order');

Database-Specific Collations

Flowforge automatically applies the correct binary collation for consistent fractional ranking across all database systems:

DatabaseCollationPurpose
MySQL/MariaDButf8mb4_binBinary comparison by character code values
PostgreSQLCBinary byte comparison (POSIX locale)
SQL ServerLatin1_General_BIN2Unicode code-point comparison
SQLiteNoneUses BINARY collation by default

Example Migration

Here's a complete example for adding Flowforge support to an existing table:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('tasks', function (Blueprint $table) {
            $table->flowforgePositionColumn('position');
        });
    }

    public function down(): void
    {
        Schema::table('tasks', function (Blueprint $table) {
            $table->dropColumn('position');
        });
    }
};

Factory Integration

When using factories, ensure position values are generated correctly:

use Relaticle\Flowforge\Services\Rank;

class TaskFactory extends Factory
{
    private static array $statusCounters = ['todo' => 0, 'in_progress' => 0, 'done' => 0];
    private static array $lastRanks = [];

    public function definition(): array
    {
        $status = $this->faker->randomElement(['todo', 'in_progress', 'done']);
        
        return [
            'title' => $this->faker->sentence(3),
            'status' => $status,
            'position' => $this->generatePositionForStatus($status),
        ];
    }

    private function generatePositionForStatus(string $status): string
    {
        if (self::$statusCounters[$status] === 0) {
            $rank = Rank::forEmptySequence();
        } else {
            $rank = isset(self::$lastRanks[$status]) 
                ? Rank::after(self::$lastRanks[$status])
                : Rank::forEmptySequence();
        }

        self::$statusCounters[$status]++;
        self::$lastRanks[$status] = $rank;
        
        return $rank->get();
    }
}

Repair Command

If position data becomes corrupted, use the repair command:

php artisan flowforge:repair-positions

This command will:

  • Identify records with missing or corrupted position values
  • Regenerate proper fractional ranking positions
  • Maintain existing order where possible
  • Work across all supported database systems