Using JSONB with Doctrine in Symfony 7
I recently needed to store some flexible data in a PostgreSQL database for a Symfony project. Instead of bloating my schema with nullable columns or spinning off extra tables, I went with jsonb. It’s native to PostgreSQL, performs well, and gives you a lot of flexibility.
But here’s the catch: Doctrine doesn’t support jsonb out of the box. So you’ll need to do a bit of setup to get everything working smoothly.
1. Define the field in your entity #
Let’s say you’re building a Product entity and want to store attributes like color, size, etc., without hardcoding them into your schema. Here’s how you define it:
#[ORM\Entity]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'jsonb')]
private ?array $attributes = null;
// Getters and setters...
}
Notice the jsonb type — we’ll teach Doctrine what that means in a second.
2. Create a custom type for Doctrine #
Since Doctrine doesn’t know what jsonb is, we need to register a custom type. Create a file like this: src/Doctrine/Type/JsonbType.php.
<?php
declare(strict_types=1);
namespace App\Doctrine\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class JsonbType extends Type
{
public const JSONB = 'jsonb';
public function convertToPHPValue($value, AbstractPlatform $platform): ?array
{
if (null === $value) {
return null;
}
return json_decode($value, true);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
if (null === $value) {
return null;
}
return json_encode($value);
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return 'jsonb';
}
public function getName(): string
{
return self::JSONB;
}
}
Nothing fancy here — just basic serialization and deserialization.
3. Register the type in Doctrine #
Tell Symfony where to find your new jsonb type. Open up your doctrine.yaml config file and add this:
doctrine:
dbal:
url: "%env(resolve:DATABASE_URL)%"
types:
jsonb: App\Doctrine\Type\JsonbType
Now when you declare a column with type: jsonb, Doctrine knows exactly what to do with it.
4. Create the migration #
Time to generate and run your database migration. Use the usual Symfony commands:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
After generating the migration, double-check the SQL — make sure it’s actually using JSONB:
$this->addSql('CREATE TABLE product (id SERIAL PRIMARY KEY, attributes JSONB DEFAULT NULL)');
If it says JSON or some weird fallback, you probably forgot to register the type or used the wrong one in your entity.
That’s it for the setup. In the next post, I’ll cover inserting data and querying JSONB fields using PostgreSQL’s powerful operators — no raw SQL mess, promise.