fle/postgresql-type-bundle
A Symfony bundle that integrates PostgreSQL-specific Doctrine types, making native Postgres data types easier to map and use in entities. Includes bundle configuration and service wiring for quick setup in Symfony applications.
This is a small Symfony bundle that registers Doctrine DBAL custom types for PostgreSQL-specific column types (jsonb, hstore, arrays, inet/cidr, uuid, interval, etc.) so they can be used as normal entity property types in Doctrine. Before doing anything, note the headline fact: the repository is archived and has 4 stars / 0 dependents. Treat it as reference material or a source to copy from, not as a dependency you build a product on.
Minimal steps:
Doctrine\DBAL\Types\Type subclasses it ships. In most cases 1–3 of them map to a type that modern Doctrine DBAL already supports natively (json, jsonb, uuid, guid), so you may only need the array or hstore bits — or none at all.config/bundles.php entry plus the class from composer.json's autoload). If you are on Symfony Flex, you will have to wire this manually since there is no recipe.doctrine.dbal.types in config/packages/doctrine.yaml; in older setups it is Types::addType() inside a compiler pass or bundle boot(). Also set doctrine.dbal.mapping_types so the schema tool maps the reverse direction (e.g. jsonb: jsonb, _int4: array).jsonb or text[] column on an entity.#[ORM\Column(name: 'tags', type: 'text_array')]
private array $tags = [];
#[ORM\Column(name: 'attrs', type: 'jsonb')]
private array $attrs = [];
Look first at: the bundle's DependencyInjection/ extension (what it auto-registers and under what type names), the type classes themselves (convertToDatabaseValue / convertToPHPValue is where all the real behavior lives), and the Resources/config service definitions. The README of a 4-star archived bundle is usually thinner than the code; read the code.
Type-name indirection. Never hardcode the bundle's type string across entities. Define your own constants or a small enum (App\Doctrine\Type::JSONB) so that when you replace the bundle with native Doctrine types or martin-georgiev/postgresql-for-doctrine, you change one file instead of grepping 40 entities.
Explicit mapping_types is not optional. Without it, doctrine:schema:validate and doctrine:migrations:diff will see jsonb in the database, not recognize it, and either drop/recreate the column or emit a perpetual diff. Pair every custom types entry with a mapping_types entry.
doctrine:
dbal:
types:
jsonb: App\Doctrine\Type\JsonbType
text_array: App\Doctrine\Type\TextArrayType
mapping_types:
jsonb: jsonb
_text: text_array
_int4: int_array
Keep conversion logic in the type, not the entity. The value of a DBAL type is that convertToPHPValue decodes and convertToDatabaseValue encodes, so repositories, fixtures, and serializers all get consistent behavior for free. If you find yourself calling json_decode() in a service, the type layer is leaking.
Arrays: pick one representation and hold it. Postgres arrays can be PHP lists (int[], text[]) or associative (hstore-ish). Decide per type and document it. For nullable arrays, decide whether NULL and '{}' are distinct — they are in Postgres, and a type that coerces '{}' to null will silently lose data on round-trip.
DQL and query building. Custom types only affect hydration and parameter binding; they do not teach DQL about Postgres operators. @>, ?, &&, and jsonb_path_query need raw SQL or a custom DQL function. The practical pattern is a repository method using Connection::executeQuery() with ArrayParameterType for the array operand, returning scalars or Result::HYDRATE_ARRAY.
Migrations. Because these are native Postgres types, prefer writing the migration SQL by hand (ALTER TABLE ... ADD COLUMN attrs jsonb NOT NULL DEFAULT '{}') and then running diff to confirm it produces no further changes. That "empty diff" is your integration test that the type + mapping_types wiring is correct.
Fixtures and tests. Use a real Postgres in CI (Docker service or testcontainers) rather than SQLite. Half the bugs in this class of bundle live in the array/hstore string serialization, which SQLite cannot exercise.
Doctrine\DBAL\Types\Type::addType() is deprecated/removed across DBAL 3→4, and Type::getName() is gone in DBAL 4. If you are on a modern stack, this bundle may not even boot. Pin your DBAL minor and expect to fork.json, jsonb, uuid, guid, array, simple_array itself. If the bundle (or your config) re-registers the same name you get "Unknown column type" or silently different conversion behavior depending on registration order. Check Types::getTypeRegistry()->has() before adding.AbstractPlatform::getName(); others need to know the server version to decide jsonb-vs-json behavior. When the platform returns postgresql vs postgresql94 vs postgresql16, a hardcoded string comparison in the type will misbehave. Verify against the actual platform class your DBAL version instantiates.convertToDatabaseValue swallowing errors. Many small bundles do json_encode() without checking the return; an invalid UTF-8 byte or a resource in the array becomes false and lands in the DB as false/''. Add strict JSON_THROW_ON_ERROR in your fork and a try/catch that rethrows with the entity/column name in the message — you will save hours of debugging.convertToPHPValue receiving already-decoded data. It can be called with null, with a resource (stream), and sometimes with a value already converted on the same instance. Guard: if ($value === null || is_array($value)) return $value;.jsonb — it is the modern answer and avoids this whole class of problem.Doctrine\DBAL\Types\JsonType and registering your subclass gets you most of the way for jsonb without the bundle; a compiler pass that registers types from a config array is about 40 lines. Before adopting, estimate that fork cost — for 4-star archived code it is usually lower than the dependency risk.martin-georgiev/postgresql-for-doctrine (actively maintained, array/hstore/jsonb/range types) or native DBAL types plus a hand-written platform override. Reach for this bundle only as a source of copy-paste implementations for the one or two types you cannot get elsewhere.How can I help you explore Laravel packages today?