symfony/ai-doctrine-message-store
Doctrine DBAL message store integration for Symfony AI Chat. Persist and retrieve chat messages in a relational database using Doctrine DBAL, enabling durable conversation history and easy storage configuration within Symfony applications.
Install Dependencies
composer require symfony/ai symfony/ai-doctrine-message-store doctrine/dbal
doctrine/dbal (v3.x+) for DBAL support.Configure DBAL Connection
Ensure your config/database.php includes a DBAL-compatible connection (e.g., pgsql, mysql). Example:
'connections' => [
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'schema' => 'public',
],
],
Set Up Schema Run the package’s schema migration (adapt for Laravel):
# Symfony CLI (if using Symfony)
php bin/console doctrine:schema:update --force
# Laravel Alternative: Create a migration
php artisan make:migration create_ai_messages_table
Schema (simplified for Laravel):
Schema::create('ai_messages', function (Blueprint $table) {
$table->id();
$table->string('conversation_id');
$table->text('content');
$table->json('metadata')->nullable();
$table->timestamps();
$table->index(['conversation_id', 'created_at']);
});
First Use Case: Store a Chat Message
use Symfony\Component\AI\MessageStoreInterface;
use Symfony\AI\DoctrineMessageStore\DoctrineDBALMessageStore;
// Laravel Service Provider (e.g., AppServiceProvider)
public function register()
{
$this->app->singleton(MessageStoreInterface::class, function ($app) {
return new DoctrineDBALMessageStore(
$app['db']->connection('pgsql')->getDoctrineConnection()
);
});
}
// Usage in a Controller/Service
$messageStore = app(MessageStoreInterface::class);
$messageStore->save(
new \Symfony\Component\AI\Message(
'user-123',
'What is Laravel?',
'Laravel is a PHP framework...'
)
);
Message CRUD Operations
$message = new \Symfony\Component\AI\Message(
'conv-abc',
'user', 'Hello!',
'ai', 'Hi there!'
);
$messageStore->save($message);
conversation_id or timestamp.
$messages = $messageStore->findByConversation('conv-abc');
$messageStore->deleteByConversation('conv-abc');
Conversation History
findByConversation() to fetch multi-turn dialogues.scopeConversation() to Eloquent if hybrid storage is needed.Metadata Handling
user_id, ai_model) in the metadata JSON field.
$message->setMetadata([
'user_id' => 1,
'model' => 'gpt-4',
'tokens' => 1200,
]);
Symfony AI Chat Compatibility
Chat component:
use Symfony\Component\AI\Chat\Chat;
use Symfony\Component\AI\Chat\Message;
$chat = new Chat($messageStore);
$chat->addUserMessage('Hello!');
$response = $chat->ask('ai-model');
$chat->addAIMessage($response);
Laravel Event Listeners
// app/Providers/EventServiceProvider
public function boot()
{
\Symfony\Component\AI\Message::saved(function ($message) {
\Log::info("AI Message stored: {$message->getContent()}");
});
}
Batch Operations
executeStatement() for bulk inserts (e.g., importing historical chats):
$conn = $messageStore->getConnection();
$stmt = $conn->prepare("INSERT INTO ai_messages (...) VALUES (...)");
$stmt->executeStatement([/* batch data */]);
Hybrid Storage
// Cache recent messages in Redis, fall back to DBAL
$redis = Redis::connection();
$cacheKey = "ai:conv:{$conversationId}";
if (!$redis->exists($cacheKey)) {
$messages = $messageStore->findByConversation($conversationId);
$redis->set($cacheKey, json_encode($messages), 'EX', 3600);
}
Schema Conflicts
Schema::table() for updates or generate a custom migration:
Schema::table('ai_messages', function (Blueprint $table) {
$table->json('metadata')->nullable()->after('content');
});
PSR-15 Overhead
$conn = $messageStore->getConnection();
$conn->insert('ai_messages', [
'conversation_id' => 'conv-abc',
'content' => 'Hello!',
'created_at' => now(),
]);
Performance Bottlenecks
conversation_id and created_at.$messages = Cache::remember("ai:conv:{$id}", now()->addHours(1), function () use ($id) {
return $messageStore->findByConversation($id);
});
Symfony Dependency Bloat
symfony/ai for a single store may add unnecessary components.// Mock Symfony AI's Message class
class LaravelAIMessage implements \Symfony\Component\AI\MessageInterface {
// Implement required methods
}
Transaction Handling
DB::transaction(function () use ($messageStore) {
$messageStore->save($message);
// Other DB operations
});
Query Logging Enable DBAL logging to inspect slow queries:
$conn->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Schema Validation Verify the table structure matches the package’s expectations:
php artisan schema:dump
Compare with the Symfony AI schema.
Connection Issues
$connection = $app['db']->connection('pgsql')->getDoctrineConnection();
Custom Metadata
Extend the metadata field to store app-specific data:
$message->setMetadata([
'user_id' => auth()->id(),
'session_id' => session()->getId(),
]);
Soft Deletes
Add a deleted_at column and implement soft deletes:
Schema::table('ai_messages', function (Blueprint $table) {
$table->softDeletes();
});
Override deleteByConversation() to use soft deletes.
Search Functionality Add full-text search with PostgreSQL/MySQL:
Schema::table('ai_messages', function (Blueprint $table) {
$table->fullText('content');
});
Query with:
$conn->executeQuery("
SELECT * FROM ai_messages
WHERE to_tsvector('english', content) @@ to_tsquery('english', ?)
", ['laravel']);
Event Dispatching Trigger Laravel events on message operations:
// In DoctrineDBALMessageStore
event(new \App\Events\AIMessageStored($message));
Rate Limiting Use Laravel’s rate limiting middleware for API endpoints that store messages:
How can I help you explore Laravel packages today?