vrok / doctrine-addons
Doctrine ORM type & helper classes
Installs: 4 522
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 3
Forks: 0
Open Issues: 0
Requires
- php: ^8.2
- doctrine/dbal: ^4.2.0
Requires (Dev)
- ext-intl: *
- behat/transliterator: ^v1.4.0
- doctrine/orm: ^3.0.2
- friendsofphp/php-cs-fixer: ^3.64.0
- phpunit/phpunit: ^11.4.1
- rector/rector: ^1.2.7
- roave/security-advisories: dev-latest
- symfony/cache: ^7.0.0
- symfony/property-access: ^7.0.0
- symfony/serializer: ^7.0.0
Suggests
- ext-intl: For Entity\NormalizerHelper::to[Nullable]Locale
- behat/transliterator: For Util\UmlautTransliterator
- symfony/property-access: For ImportExport\Helper
- symfony/serializer: For Entity\LockableTrait
Conflicts
- doctrine/orm: <3.0
README
This is a library with additional classes for usage in combination with the Doctrine ORM.
Enable Postgres specific DQL functions
CAST
implements the corresponding function from Postgres to convert types:
$queryBuilder->expr()->like('CAST(u.varchar, \'text\'))', ':parameterName')
CONTAINS
allows to use the postgres-only "@>" operator
to search for values within jsonb fields or arrays.
$qb->andWhere("CONTAINS(u.numbers, :number) = true") ->setParameter('number', 3);
JSON_CONTAINS_TEXT
allows to use the postgres-only "?" operator
to search for strings within jsonb fields.
This for example allows to filter Symfony users correctly by role,
e.g. if you use role names that are part of others (ROLE_SUPER & ROLE_SUPER_ADMIN) where
using LIKE
would fail.
$qb->andWhere("JSON_CONTAINS_TEXT(u.roles, :searchRole) = true") ->setParameter('searchRole', 'ROLE_ADMIN');
JSON_FIELD_AS_TEXT
allows to use the postgres-only "->>" operator
to get JSON data within a jsonb fields as string.
This for example allows to search for records that embed JSON,
e.g. if you store metadata or addresses:
$qb->andWhere("JSON_FIELD_AS_TEXT('u.address, :addrField) = :addrValue") ->setParameter('addrField', 'city') ->setParameter('addrValue', 'Dresden');
Add to config/packages/doctrine.yaml:
doctrine: orm: dql: string_functions: CAST: Vrok\DoctrineAddons\ORM\Query\AST\CastFunction CONTAINS: Vrok\DoctrineAddons\ORM\Query\AST\ContainsFunction JSON_CONTAINS_TEXT: Vrok\DoctrineAddons\ORM\Query\AST\JsonContainsTextFunction JSON_FIELD_AS_TEXT: Vrok\DoctrineAddons\ORM\Query\AST\JsonFieldAsTextFunction
Enable types in Symfony
config/packages/doctrine.yaml
doctrine: dbal: types: # force all dates/times to be stored in UTC utcdatetime: name: datetime_immutable class: Vrok\DoctrineAddons\DBAL\Types\UTCDateTimeType # MariaDB does not support the JSON type, so we do not benefit from # validation/searching/path syntax etc. Also it uses a LONGTEXT # instead, which has a performance hit because it is stored outside # the row and causes possible temp tables to be written to disk smalljson: name: small_json class: Vrok\DoctrineAddons\DBAL\Types\SmallJsonType
Enable the MariadbTestDriver in Symfony
config/packages/test/doctrine.yaml
doctrine: dbal: # There is a bug in Doctrine\Common\DataFixtures\Purger\ORMPurger # which causes tables that are target of a foreign key constraint to # be deleted before the association table(s), which in turn causes # "1701 Cannot truncate a table referenced in a foreign key constraint" # So we use our custom driver to disable foreign key checks for TRUNCATE # because only with TRUNCATE instead of DELETE FROM we ensure the same # autoincrement IDs for fixtures in tests # "driver" left blank intentionally driver: driver_class: Vrok\DoctrineAddons\DAL\Driver\MariadbTestDriver
Enable the PostgreSQLTestDriver in Symfony
config/packages/test/doctrine.yaml
doctrine: dbal: # Default purge/TRUNCATE behavior of Postgres does not reset autoincrement # values, so we use our custom driver to reset identities for TRUNCATE. # "driver" left blank intentionally driver: driver_class: Vrok\DoctrineAddons\DAL\Driver\PostgreSQLTestDriver
Lockable entities
Implementing a simple LockableInterface
allows unified handling of different
entities that can be locked/unlocked by admins etc. The provided interface
makes very little assumptions about your code, except that each entity can
be locked/unlocked at any time (regardless of other states the entity might have).
The LockableTrait
provides an opinionated implementation of that interface,
by specifying a boolean property locked
that defaults to false and that has
a Symfony group attribute of default:read
.
Slugs with correct umlauts in Symfony
Add this to your services.yaml to have ae, ue, oe in your slugs instead of
a, u, o for ä, ü, ö.
This also handles some other chars, e.g. accents.
# Symfony 7.x gedmo.listener.sluggable: class: Gedmo\Sluggable\SluggableListener tags: - { name: doctrine.event_listener, event: 'prePersist' } - { name: doctrine.event_listener, event: 'onFlush' } - { name: doctrine.event_listener, event: 'loadClassMetadata' } calls: - [ setTransliterator, [ [ 'Vrok\DoctrineAddons\Util\UmlautTransliterator', 'transliterate' ] ] ] # Symfony 6.x gedmo.listener.sluggable: class: Gedmo\Sluggable\SluggableListener tags: - { name: doctrine.event_subscriber, connection: default } calls: - [ setAnnotationReader, [ "@annotation_reader" ] ] # only if you still use annotations - [ setTransliterator, [ [ 'Vrok\DoctrineAddons\Util\UmlautTransliterator', 'transliterate' ] ] ]
Import / export entities from / to arrays
Short Example, for more details see ExportEntity / ImportEntity and ExportTest / ImportTest for all features. Allows to export referenced entities (or only their identifiers) and collections.
use Vrok\DoctrineAddons\ImportExport\ExportableEntity; use Vrok\DoctrineAddons\ImportExport\ExportableProperty; use Vrok\DoctrineAddons\ImportExport\Helper; use Vrok\DoctrineAddons\ImportExport\ImportableEntity; use Vrok\DoctrineAddons\ImportExport\ImportableProperty; #[ExportableEntity] #[ImportableEntity] class Entity { #[ExportableProperty] #[ImportableProperty] public int $id = 0; #[ExportableProperty] #[ImportableProperty] public ?DateTimeImmutable $timestamp = null; } $entity = new Entity(); $entity->id = 1; $entity->timestamp = new Datetime(); $helper = new Helper(); $export = $helper->toArray($entity); // $export === [ // 'id' => 1, // 'timestamp' => '2022-03-23....', // ] $newInstance = $helper->fromArray($export, Entity::class);