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-doctrine-message-store doctrine/dbal symfony/ai
doctrine/dbal (v3.x) for DBAL compatibility.Register DBAL Connection
Configure a DBAL connection in config/database.php (Laravel) or config/packages/doctrine.yaml (Symfony):
// Laravel: config/database.php
'connections' => [
'ai_dbal' => [
'driver' => 'pdo_mysql',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'host' => env('DB_HOST'),
'port' => env('DB_PORT', '3306'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
],
First Use Case: Store a Chat Message
// Laravel: Create a service to wrap the Symfony store
use Symfony\AI\DoctrineMessageStore\DoctrineDBALMessageStore;
use Symfony\Component\Messenger\Message\SentMessage;
use Symfony\Component\Messenger\Envelope;
class AiMessageStoreService
{
public function __construct()
{
$connection = \DB::connection('ai_dbal')->getDoctrineConnection();
$this->store = new DoctrineDBALMessageStore($connection);
}
public function saveMessage(array $message)
{
$envelope = new Envelope(new SentMessage($message));
$this->store->store($envelope);
}
}
Usage:
$store = new AiMessageStoreService();
$store->saveMessage([
'id' => 'chat_123',
'content' => 'Hello, AI!',
'metadata' => ['user_id' => 1, 'timestamp' => now()],
]);
Retrieve Messages
public function getMessages(string $conversationId)
{
return $this->store->find($conversationId);
}
Symfony AI Chat Integration
$conversationId = 'user_42_chat_session';
$messages = [
['role' => 'user', 'content' => 'What is Laravel?'],
['role' => 'assistant', 'content' => 'A PHP framework...'],
];
foreach ($messages as $message) {
$this->store->saveMessage([
'conversation_id' => $conversationId,
'content' => $message['content'],
'role' => $message['role'],
]);
}
Laravel-Specific Patterns
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton('ai.message_store', function ($app) {
$connection = $app['db']->connection('ai_dbal')->getDoctrineConnection();
return new DoctrineDBALMessageStore($connection);
});
}
use Illuminate\Support\Facades\Event;
Event::listen(\App\Events\AIResponseGenerated::class, function ($event) {
$store = app('ai.message_store');
$store->saveMessage([
'conversation_id' => $event->conversationId,
'content' => $event->response,
'role' => 'assistant',
]);
});
Schema Management
php artisan make:migration create_ai_messages_table
public function up()
{
\DB::connection('ai_dbal')->getSchemaBuilder()->createTable('ai_messages', function (Blueprint $table) {
$table->id();
$table->string('conversation_id');
$table->text('content');
$table->string('role')->comment('user|assistant|system');
$table->json('metadata')->nullable();
$table->timestamps();
$table->index(['conversation_id', 'created_at']);
});
}
doctrine/doctrine-migrations-bundle for schema updates.Bulk Operations
// Export all messages for a conversation
$messages = $this->store->find('conversation_123');
$serialized = json_encode($messages);
// Import (custom logic needed; not natively supported)
$decoded = json_decode($serialized, true);
foreach ($decoded as $message) {
$this->store->saveMessage($message);
}
Hybrid Storage Combine with Redis for performance:
use Symfony\Component\Cache\Adapter\RedisAdapter;
$cache = new RedisAdapter();
$cache->save('ai:conversation_123', $messages, 3600); // Cache for 1 hour
Avoid Symfony Bloat
symfony/ai-doctrine-message-store and doctrine/dbal. Skip symfony/ai if using Laravel’s AI packages (e.g., laravel-ai).Leverage Laravel’s Query Builder For advanced queries, use DBAL’s query builder:
$query = $connection->createQueryBuilder()
->select('*')
->from('ai_messages')
->where('conversation_id = :id')
->setParameter('id', 'user_42_chat')
->orderBy('created_at', 'ASC')
->execute();
Transaction Management Wrap AI operations in transactions:
\DB::connection('ai_dbal')->transaction(function () {
$store->saveMessage($message);
// Other DB operations...
});
Testing Use Laravel’s testing tools with a SQLite in-memory DB:
public function testMessageStorage()
{
$connection = \DB::connection('sqlite_test')->getDoctrineConnection();
$store = new DoctrineDBALMessageStore($connection);
$store->saveMessage(['conversation_id' => 'test', 'content' => 'Hello']);
$messages = $store->find('test');
$this->assertCount(1, $messages);
}
Schema Mismatch
-- Required columns
id (PK), conversation_id, content, role, created_at, updated_at
$connection->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
PSR-15 Incompatibility
class LaravelMessageStore
{
public function __construct(private DoctrineDBALMessageStore $store) {}
public function store(array $message)
{
$envelope = new Envelope(new SentMessage($message));
$this->store->store($envelope);
}
public function find(string $conversationId)
{
return $this->store->find($conversationId);
}
}
Connection Configuration
config/database.php:
'ai_dbal' => [
'driver' => 'pdo_mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset
How can I help you explore Laravel packages today?