martin-georgiev/postgresql-for-doctrine
Adds PostgreSQL-specific power to Doctrine DBAL/ORM: rich native types (jsonb, arrays, ranges, network, geometric, etc.) plus DQL functions/operators for JSON and array querying. Supports PostgreSQL 9.4+ and PHP 8.2+.
PostgreSQL native enum types are user-defined types with a fixed set of ordered string values. Unlike every other type in this library, there is no pre-registered constant - each PostgreSQL enum maps to its own subclass of Enum.
📖 See also: Available Types
Enum is an abstract base class. You create one concrete subclass per PostgreSQL enum type. The subclass declares TYPE_NAME (matching the PostgreSQL type name exactly) and implements getEnumClass() returning the fully-qualified name of a PHP BackedEnum.
Only string-backed PHP enums are supported. PostgreSQL enum values are text labels, so int-backed enums cannot be mapped correctly.
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped', 'cancelled');
Values must match the PostgreSQL enum cases exactly (case-sensitive).
enum OrderStatus: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case SHIPPED = 'shipped';
case CANCELLED = 'cancelled';
}
use MartinGeorgiev\Doctrine\DBAL\Types\Enum;
final class OrderStatusType extends Enum
{
protected const TYPE_NAME = 'order_status';
protected function getEnumClass(): string
{
return OrderStatus::class;
}
}
The TYPE_NAME constant must match the PostgreSQL type name exactly - it is used as both the DBAL type name and the SQL declaration.
use Doctrine\DBAL\Types\Type;
Type::addType('order_status', OrderStatusType::class);
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Order
{
#[ORM\Column(type: 'order_status')]
private OrderStatus $status;
}
Each PostgreSQL enum requires its own subclass and addType call.
// Two PostgreSQL enums → two subclasses
final class OrderStatusType extends Enum
{
protected const TYPE_NAME = 'order_status';
protected function getEnumClass(): string { return OrderStatus::class; }
}
final class PaymentMethodType extends Enum
{
protected const TYPE_NAME = 'payment_method';
protected function getEnumClass(): string { return PaymentMethod::class; }
}
Type::addType('order_status', OrderStatusType::class);
Type::addType('payment_method', PaymentMethodType::class);
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped', 'cancelled');
ALTER TABLE orders ADD COLUMN status order_status NOT NULL DEFAULT 'pending';
ALTER TYPE ... ADD VALUE cannot run inside a transaction. In Symfony Migrations, use $this->addSql() directly. Doctrine Migrations wraps each migration in a transaction by default, so you must opt out:
public function preUp(Schema $schema): void
{
$this->connection->executeStatement('ALTER TYPE order_status ADD VALUE \'returned\'');
}
public function up(Schema $schema): void
{
// other schema changes that can run in a transaction
}
Alternatively, run it outside a transaction block in your migration tool.
PostgreSQL does not support removing or renaming enum cases. The workaround is to create a new type and migrate the column:
CREATE TYPE order_status_new AS ENUM ('pending', 'processing', 'shipped', 'cancelled', 'returned');
ALTER TABLE orders ALTER COLUMN status TYPE order_status_new USING status::text::order_status_new;
DROP TYPE order_status;
ALTER TYPE order_status_new RENAME TO order_status;
How can I help you explore Laravel packages today?