vrok / symfony-addons
Symfony helper classes
Package info
github.com/j-schumann/symfony-addons
Type:symfony-bundle
pkg:composer/vrok/symfony-addons
Requires
- php: ^8.4
- symfony/framework-bundle: ^7.4.10|^8.0.0
- symfony/yaml: ^7.4.10|^8.0.0
Requires (Dev)
- api-platform/core: ^5.0.0
- doctrine/dbal: ^4.5.0
- doctrine/doctrine-bundle: ^3.2.0
- doctrine/doctrine-fixtures-bundle: ^4.1.0
- doctrine/orm: ^3.5.0
- doctrine/persistence: ^4.1.1
- friendsofphp/php-cs-fixer: ^3.95.5
- justinrainbow/json-schema: ^6.5.2
- monolog/monolog: ^3.8.0
- phpstan/phpstan: ^2.3.1
- phpstan/phpstan-deprecation-rules: ^2.0.5
- phpunit/phpunit: ^13.3.5
- rector/rector: ^2.6
- roave/security-advisories: dev-latest
- symfony/browser-kit: ^7.4.10|^8.0.0
- symfony/clock: ^7.4.10|^8.0.0
- symfony/console: ^7.4.10|^8.0.0
- symfony/doctrine-bridge: ^7.4.10|^8.0.0
- symfony/doctrine-messenger: ^7.4.10|^8.0.0
- symfony/error-handler: ^7.4.17|^8.1.5
- symfony/http-client: ^7.4.10|^8.0.0
- symfony/mailer: ^7.4.10|^8.0.0
- symfony/monolog-bundle: ^3.11.0|^4.0.0
- symfony/polyfill-php85: ^1.33.0
- symfony/string: ^7.4.10|^8.0.0
- symfony/twig-bundle: ^7.4.10|^8.0.0
- symfony/validator: ^7.4.10|^8.0.0
- symfony/workflow: ^7.4.10|^8.0.0
- vrok/doctrine-addons: ^3.1.0
- zalas/phpunit-globals: ^4.2.0
Suggests
- api-platform/test: Required by the ApiPlatformTestCase when API Platform is installed as split packages instead of api-platform/core
- justinrainbow/json-schema: Required for the schemaClass of ApiPlatformTestCase::testOperation()
- lexik/jwt-authentication-bundle: Creates the token for the email of ApiPlatformTestCase::testOperation(), unless getJWT() is overridden
- vrok/doctrine-addons: ^3.1.0 provides the DQL functions the ContainsFilter, the JsonExistsFilter and the SimpleSearchFilter on JSON columns in Postgres require
Provides
None
Conflicts
- api-platform/core: <5.0
- api-platform/metadata: <5.0
- doctrine/dbal: <4.5
- friendsofphp/php-cs-fixer: <3.95.5
- twig/twig: <3.21
Replaces
None
- dev-main
- 4.0.0
- 3.8.0
- 3.7.0
- 3.6.0
- 3.5.0
- 3.4.0
- 3.3.1
- 3.3.0
- 3.2.1
- 3.2.0
- 3.1.0
- 3.0.0
- 2.16.0
- 2.15.0
- 2.14.0
- 2.13.1
- 2.13.0
- 2.12.0
- 2.11.0
- 2.10.0
- 2.9.0
- 2.8.1
- 2.8.0
- 2.7.0
- 2.6.0
- 2.5.0
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.1
- 2.0.0
- v1.11.0
- v1.9.10
- v1.9.1
- v1.9.0
- v1.8.0
- v1.6.1
- v1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.0
- dev-develop
- dev-review-fixes
- dev-conventions
- dev-test-coverage
- dev-release-3.x
This package is auto-updated.
Last update: 2026-10-13 22:46:30 UTC
README
This is a library with additional classes for usage in combination with the Symfony framework.
Validators
AtLeastOneOf
Works like Symfony's own AtLeastOneOf constraint, but instead of returning a message like This value should satisfy at least ... it returns the message of the last failed validation. Can be used
for optional form fields where only simple messages should be displayed when AtLeastOneOf is
used with Blank as first constraint. See AtLeastOneOfValidatorTest for examples.
NoHtml
This validator tries to detect if a string contains HTML, to allow only plain text. See
NoHtmlValidatorTest for examples of allowed / forbidden values.
NoLineBreaks
This validator raises a violation if it detects one or more linebreak characters in the validated
string. Detects unicode linebreaks, see NoLineBreaksValidatorTest for details.
NoSurroundingWhitespace
This validator raises a violation if it detects trailing or leading whitespace or newline characters
in the validated string. Linebreaks and spaces are valid within the string. Uses a regex looking for
\s and \R, see NoSurroundingWhitespaceValidatorTest for details on detected characters.
PasswordStrength
This validator evaluates the strength of a given password string by determining its entropy instead
of requiring something like "must contain at least one uppercase & one digit & one special char".
Allows to set a minStrength to vary the requirements. See
Vrok\SymfonyAddons\Helper\PasswordStrength for details on the calculation.
PHPUnit helpers
Using the ApiPlatformTestCase
This class is used to test ApiPlatform endpoints by specifying input data and verifying the response data. It combines the traits documented below to refresh the database before each test, optionally create authenticated requests and check for created logs / sent emails / dispatched messages. It allows to easily check for expected response content, allowed or forbidden keys in the data or to verify against a given schema.
Requires api-platform/core, symfony/browser-kit, and symfony/http-client to be installed.
schemaClass additionally requires justinrainbow/json-schema, email requires
lexik/jwt-authentication-bundle unless getJWT() is overridden.
<?php use Vrok\SymfonyAddons\PHPUnit\ApiPlatformTestCase; class AuthApiTest extends ApiPlatformTestCase { public function testAuthRequiresPassword(): void { $this->testOperation( uri: '/authentication_token', method: 'POST', requestOptions: ['json' => ['username' => 'fakeuser']], responseCode: 400, contentType: 'application/json', json: [ 'type' => 'https://tools.ietf.org/html/rfc2616#section-10', 'title' => 'An error occurred', 'detail' => 'The key "password" must be provided.', ], ); } }
| Option | Usage | Example |
|---|---|---|
| uri | the URI / endpoint to call |
|
| iri |
an array of |
|
| if given, tries to find a User with that email and sends the token of `getJWT()` for this user as Bearer, see "Authentication" below |
|
|
| postFormAuth |
if given (and 'email' is set) the token of `getJWT()` is sent as 'application/x-www-form-urlencoded'
request in a form field. This is used for download endpoints where the browser should present the user with the file to download instead of loading it into memory via Javascript. (As we don't want to supply the token via GET to prevent security issues and as we cannot set a cookie.) |
|
| method |
HTTP method for the request, defaults to GET. If PATCH is used, the content-type header is
automatically set to |
|
| requestOptions | options for the HTTP client, e.g. query parameters or basic auth |
requestOptions: [ 'json' => [ 'username' => 'Peter', 'email' => 'peter@example.com', ], // or: 'query' => [ 'order' => ['createdAt' => 'asc'], ], // or: 'headers' => ['content-type' => 'application/json'], ] |
| cookies | cookies to send with the request, name => value |
|
| files |
An array of one or more files to upload. The files will be copied to a temp file, and wrapped in an
|
files: [ 'picture' => [ 'path' => '/path/to/file.png', 'originalName' => 'mypicture.png', 'mimeType' => 'image/png', ] ] |
| responseCode | asserts that the received status code matches |
|
| contentType | asserts that the received content type header matches exactly |
|
| json | asserts that the returned content is JSON and contains the given array as subset |
json: [ 'username' => 'Peter', 'email' => 'peter@example.com', ] |
| requiredKeys | asserts the dataset contains the list of keys. Used for elements where the value is not known in advance, e.g. ID, slug, timestamps. Can be nested. |
requiredKeys: ['member' => [['id', '@id']]] |
| forbiddenKeys | like requiredKeys, but the dataset may not contain those |
forbiddenKeys: ['member' => [['password', 'salt']]] |
| schemaClass | Asserts that the received response matches the JSON schema for the given class. If the `iri` parameter is used or the request method is *not* GET, the item schema is used. Else the collection schema is used. |
schemaClass: User::class, |
| createdLogs | array of entries, asserts the messages to be present (with the correct log level) in the monolog handlers after the operation ran |
createdLogs: [
['Failed to validate the provider', Level::Error],
],
|
| emailCount | asserts this number of emails to be sent via the mailer after the operation was executed |
emailCount: 2,
|
| messageCount | asserts this number of messages to be dispatched to the message bus |
messageCount: 2,
|
| dispatchedMessages | Array of message classes, asserts that at least one instance of each given class has been dispatched to the message bus. An Element can also be an array of [FQCN, callable], in that case the callback is called for each matching message with that message as first parameter and the JSON response as second parameter (an empty array for a response without content), to trigger additional assertions for the message. |
dispatchedMessages: [ TenantCreatedMessage::class, [TenantCreatedMessage::class, function (object $message, array $data): void { self::assertSame($data['id'], $message->tenantId); }] ], |
| dispatchedEvents | Array of event names (may be class names), asserts that at least one instance of each given event has been dispatched to Symfony's EventDispatcher. |
dispatchedEvents: [
'kernel.response',
ProjectPreCreateEvent::class,
],
|
Authentication
With the email argument, testOperation() sends the token returned by getJWT(), which the
lexik/jwt-authentication-bundle creates by default. This tests the real authentication, including
the user checker and the claims of the token. Other ways:
- Different token issuer, e.g. OIDC: override
getJWT()in your test case and return a token your authenticator accepts, e.g. signed with a test key that thewhen@testconfig trusts. - Token as cookie: pass it with
cookies, e.g.cookies: ['BEARER' => $this->getJWT([...])]. - No token, e.g. to only test access rules: use the client directly:
$client = static::createClient(); $client->loginUser($user);and then$client->request(...). This skips the authenticator, the user checker and the claims, and only lasts for the first request of the client.
Using the RefreshDatabaseTrait
Brings the DB schema up to date, removes existing data before each test and fills the tables with
predefined fixtures. Install doctrine/doctrine-fixtures-bundle and create fixtures, the trait uses
the test group per default.
Just include the trait in your testcase and call bootKernel() or createClient(), e.g. in the
setUp method:
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Vrok\SymfonyAddons\PHPUnit\RefreshDatabaseTrait; class DatabaseTest extends KernelTestCase { use RefreshDatabaseTrait; /** * @var \Doctrine\ORM\EntityManager */ private $entityManager; protected function setUp(): void { $kernel = self::bootKernel(); $this->entityManager = $kernel->getContainer() ->get('doctrine') ->getManager(); } }
Optionally define which fixtures to use for this test class:
protected static array $fixtureGroups = ['test', 'other'];
Redeclare the property in the test class instead of assigning it in setUpBeforeClass(): the
property is static, an assignment would change the groups for every other test class sharing it.
The trait caches the loaded fixtures in a static property too: if you use the trait in your own
base class instead of the ApiPlatformTestCase, call static::fixtureCleanup() in its
tearDownAfterClass(), so the next test class loads its own fixture groups.
Supports setting the cleanup method after tests via DB_CLEANUP_METHOD. Allowed values are purge,
dropSchema and dropDatabase, for more details see the docblock of the RefreshDatabaseTrait.
Suggested method is purge for all database platforms, see benchmark below. Results may vary
depending on your DB schema and/or server setup, so check if different settings work better for you.
On MySQL/MariaDB you can switch the purge method, by setting the ENV DB_PURGE_MODE to delete
(the default) or truncate. For details see the trait class. This setting has no effect on the
other platforms.
Benchmark
The numbers below come from the Refresh Benchmark CI workflow (see bin/benchmark.sh) and are
median milliseconds per bootKernel() (200 iterations), on a GitHub-hosted Ubuntu runner
(4 CPU, 16 GB RAM), against this package's own test schema.
| platform | on tmpfs | on disk | ||||||
|---|---|---|---|---|---|---|---|---|
| purge | dropSchema | dropDatabase | purge | dropSchema | dropDatabase | |||
| delete | truncate | delete | truncate | |||||
| SQLite | 7.4 | 19.7 | 12.0 | 53.3 | 153.9 | 64.9 | ||
| MariaDB 12 | 12.1 | 13.1 | 37.5 | 25.9 | 107.4 | 54.8 | 412.2 | 376.5 |
| MySQL 9 | 23.4 | 25.1 | 77.3 | 61.9 | 121.0 | 309.4 | 753.2 | 521.6 |
| PostgreSQL 18 | 23.4 | 68.3 | 78.5 | 35.5 | 162.9 | 186.1 | ||
| SQL Server 2022 | 16.1 | 120.2 | dnf * | 40.0 | 312.2 | dnf * | ||
The bold cell of each row is the fastest method on tmpfs, which is the setup worth having.
dnf: did not finish in the benchmark's limit of 600s per run.
DB_PURGE_MODE only has an effect on MySQL and MariaDB. On the other three platforms both purge
modes run the same code.
- DB_CLEANUP_METHOD=purge is usually the cheapest method everywhere, the DB_PURGE_MODE then varies
- Putting the database on tmpfs is worth far more than the choice of cleanup method.
- Using further optimizations like
--innodb-doublewrite=OFF --innodb-flush-log-at-trx-commit=2 --skip-log-binfor MySQL/MariaDB,-c fsync=off -c synchronous_commit=off -c full_page_writes=offfor PostgreSQL orALTER DATABASE model SET DELAYED_DURABILITY = FORCEDfor SQL Server produces no better results or performs even worse, so check before using them.
Running the databases on tmpfs
Test databases are throwaway by definition, so there is no reason to write them to disk. This is the single largest speedup available and costs nothing but a few lines.
With docker compose:
services: mysql: image: mysql:9 tmpfs: - /var/lib/mysql:rw,size=2g mariadb: image: mariadb:12 tmpfs: - /var/lib/mysql:rw,size=2g postgres: image: postgres:18 tmpfs: - /var/lib/postgresql/18/docker:rw,size=2g mssql: image: kcollins/mssql:latest tmpfs: - /var/opt/mssql/data:rw,size=2g
In GitHub Actions, a service container takes no command, and options are passed to docker create, so --tmpfs belongs there:
services: mysql: image: mysql:9 env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: db_test options: >- --tmpfs /var/lib/mysql:rw,size=2g --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 ports: - 3306:3306
For SQLite, point the DSN at a tmpfs path instead, e.g. sqlite:////dev/shm/test.db — four slashes,
three would make the path relative.
Using the MonologAssertsTrait
For use with a Symfony project using the monolog-bundle. Requires monolog/monolog of v3.0 or
higher.
Include the trait in your testcase and call prepareLogger before triggering the action that should
create logs and use assertLoggerHasMessage afterwards to check if a log record was created with
the given message & severity:
use Monolog\Level; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Vrok\SymfonyAddons\PHPUnit\MonologAssertsTrait; class LoggerTest extends KernelTestCase { use MonologAssertsTrait; public function testLog(): void { self::prepareLogger(); $logger = static::getContainer()->get(LoggerInterface::class); $logger->error('Failed to do something'); self::assertLoggerHasMessage('Failed to do something', Level::Error); } }
Workflow helpers
Require symfony/workflow.
PropertyMarkingStore
Can be used instead of the default MethodMarkingStore, for entities & properties without
Setter/Getter.
workflow.yaml:
framework: workflows: application_state: type: state_machine marking_store: # We need to use a service as there is no option to register a new "type" service: workflow.application.marking_store
services.yaml:
# When using the "service" option, all other settings like "property: state" # are ignored in the workflow.yaml -> That's why we need a service definition # with the correct arguments. workflow.application.marking_store: class: Vrok\SymfonyAddons\Workflow\PropertyMarkingStore arguments: [true, 'state']
WorkflowHelper
Allows to get an array of available transitions and their blockers, can be used to show the user what transitions are possible from the current state and/or why a transition is currently blocked.
public function __invoke( Entity $data, WorkflowInterface $entityStateMachine, ): array { $result = $data->toArray(); $result['transitions'] = WorkflowHelper::getTransitionList($data, $entityStateMachine); return $result; }
'publish' => [
'blockers' => [
TransitionBlocker::UNKNOWN => 'Title is empty!',
],
],
Cron events
Adding this bundle to the bundles.php registers three new CLI commands:
Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],
bin/console cron:hourly bin/console cron:daily bin/console cron:monthly
When these are called, they trigger an event (CronHourlyEvent, CronDailyEvent,
CronMonthlyEvent) that can be used by one or more event listeners/subscribers to do maintenance,
push messages to the messenger etc. It is your responsibility to execute these commands via crontab
correctly!
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Vrok\SymfonyAddons\Event\CronDailyEvent; class MyEventSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ CronDailyEvent::class => [ ['onCronDaily', 100], ], ]; } public function onCronDaily(CronDailyEvent $event): void { // e.g. remove outdated records } }
ApiPlatform Filters
The filters are used with the #[QueryParameter] attribute, the legacy #[ApiFilter] attribute
is not supported. The parameter names the property to filter, nested properties, e.g.
children.content, are supported. Without property, API Platform uses the key of the parameter
if it matches a property of the resource.
SimpleSearchFilter
Selects entities where the search term is found (case insensitive) in at least one of the specified
properties. The properties can also be of relations, e.g. child.name. All specified properties
must be string types (varchar, text etc.) or JSON fields (Postgres only), in that case the JSON is
cast to string first.
#[GetCollection(parameters: [
'pattern' => new QueryParameter(
filter: new SimpleSearchFilter(),
description: 'Search in the name, slug, description, parent and children',
properties: ['description', 'name', 'slug', 'parent.title', 'children.content'],
),
])]
The filters set no OpenAPI description, use the description of the #[QueryParameter].
Searching JSON columns on PostgreSQL requires CAST as defined Doctrine function, e.g. by
vrok/doctrine-addons:
doctrine: orm: dql: string_functions: CAST: Vrok\DoctrineAddons\ORM\Query\AST\CastFunction
ContainsFilter
Postgres-only: Filters entities by their jsonb fields, if they contain the search parameter, using
the @> operator. For example for filtering for numbers or strings in an array. Values that are
valid JSON are used as they are (?numbers=5 finds the number 5, ?roles="5" the string "5"),
other values are searched as strings (?roles=ROLE_ADMIN).
#[GetCollection(parameters: [
'numbers' => new QueryParameter(filter: new ContainsFilter()),
])]
Requires CONTAINS as defined Doctrine function, provided by vrok/doctrine-addons:
doctrine: orm: dql: string_functions: CONTAINS: Vrok\DoctrineAddons\ORM\Query\AST\ContainsFunction
JsonExistsFilter
Postgres-only: Filters entities by their jsonb fields, if they contain the search values. For
example for filtering Users by their role, to prevent accidental matching with overlapping role
names (e.g. ROLE_ADMIN and ROLE_ADMIN_BLOG) when searching as text with
WHERE roles LIKE '%ROLE_ADMIN%'.
Multiple values (e.g. ?roles[]=ROLE_ADMIN&roles[]=ROLE_EDITOR) are combined with AND by default
(?& operator), a record must contain all values. Set combination to JsonExistsFilter::OR to
match records that contain at least one of the values (?| operator). For a single value both
behave like the ? operator.
#[GetCollection(parameters: [
// records with at least one of the given roles
'anyRole' => new QueryParameter(
filter: new JsonExistsFilter(combination: JsonExistsFilter::OR),
property: 'roles',
),
// records with all the given roles
'role' => new QueryParameter(filter: new JsonExistsFilter(), property: 'roles'),
])]
Requires a DQL function of vrok/doctrine-addons >= 3.1, depending on the combination of the
filter. Register the functions for the combination(s) you use (this applies to single and multiple
values):
doctrine: orm: dql: string_functions: # for JsonExistsFilter::AND, the default JSON_CONTAINS_ALL_TEXT: Vrok\DoctrineAddons\ORM\Query\AST\JsonContainsAllTextFunction # for JsonExistsFilter::OR JSON_CONTAINS_ANY_TEXT: Vrok\DoctrineAddons\ORM\Query\AST\JsonContainsAnyTextFunction
MultipartDecoder
Adding this bundle to the bundles.php registers the MultipartDecoder to allow handling of file
uploads with additional data (e.g. in ApiPlatform):
Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],
The decoder is automatically called for multipart requests and simply returns all POST parameters
and uploaded files together. To enable this add the multipart format to your
config/packages/api_platform.yaml. The formats replace API Platform's default list, so keep the
formats you already use, the first one is the default:
api_platform: formats: jsonld: ['application/ld+json'] multipart: ['multipart/form-data']
FormDecoder
Adding this bundle to the bundles.php registers the FormDecoder to allow handling HTML form data
in ApiPlatform:
Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],
The decoder is automatically called for form requests and simply returns all POST parameters. To
enable this add the form format to your config/packages/api_platform.yaml. The formats
replace API Platform's default list, so keep the formats you already use, the first one is the
default:
api_platform: formats: jsonld: ['application/ld+json'] form: ['application/x-www-form-urlencoded']
Twig Extensions
Adding this bundle to the bundles.php together with the symfony/twig-bundle registers the new
extension:
Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],
FormatBytes
Converts bytes to human-readable notation (supports up to TiB). This extension is auto-registered. In your Twig template:
{{ attachment.filesize|formatBytes }}
Outputs: 9.34 MiB
Experimental / Additional Features
NamedArgumentsFromArrayRector
This Rector allows migrating function calls that previously used an array of options (like
ApiPlatformTestCase#testOperation) to use named arguments instead.
This can be configured to target functions, static class methods or instance methods. Example
for the rector.php:
use Vrok\SymfonyAddons\Rector\NamedArgumentsFromArrayRector; return RectorConfig::configure() ->withConfiguredRule(NamedArgumentsFromArrayRector::class, [ 'targets' => [ [ApiPlatformTestCase::class, 'testOperation'], ], ]) ;
This converts
$this->testOperation([ 'uri' => '/test', 'requiredKeys' => [ 'success', 'message', ], 'dispatchedEvents' => ['failedEvent'], ]);
to
$this->testOperation(uri: '/test', requiredKeys: [ 'success', 'message', ], dispatchedEvents: ['failedEvent']);
WrapNamedMethodArgumentsFixer
This Fixer for php-cs-fixer allows wrapping long lines of function calls with named arguments to
contain one argument per line, respecting multiline argument values like arrays. This can be used to
improve readability, e.g. after using the NamedArgumentsFromArrayRector which puts multiple
arguments on the same line.
It allows configuring the maximum number of arguments to keep on a single line, each call with more named arguments will be wrapped.
Register the Fixer in your .php-cs-fixer.dist.php and add a rule:
return $config ->registerCustomFixers([ new Vrok\SymfonyAddons\PhpCsFixer\WrapNamedMethodArgumentsFixer(), ]) ->setRules([ "VrokSymfonyAddons/wrap_named_method_arguments" => [ 'max_arguments' => 2, ], // your custom formatting rules: '@Symfony' => true, [...] ]) ;
This converts
$this->testOperation(uri: '/test', requiredKeys: [ 'success', 'message', ], dispatchedEvents: ['failedEvent']);
to
$this->testOperation( uri: '/test', requiredKeys: [ 'success', 'message', ], dispatchedEvents: ['failedEvent'] );
Attention: Formatting (indentation) is only fixed after the arguments were wrapped, by your
specification of method_argument_space and array_indentation (or rulesets containing those, like
@Symfony).
Developer Doc
composer.json require
- symfony/yaml is required for loading the bundle & test config
composer.json dev
- doctrine/doctrine-fixtures-bundle is required for tests of the ApiPlatformTestCase
- friendsofphp/php-cs-fixer checks the code style (
composer cs-check, also in CI) and is required for the tests of the WrapNamedMethodArgumentsFixer - justinrainbow/json-schema is required for tests of the
schemaClassof the ApiPlatformTestCase - rector/rector is required for the refactoring rules and the tests of the NamedArgumentsFromArrayRector
- zalas/phpunit-globals sets ENV variables per test via attributes
- symfony/browser-kit is required for tests of the MultipartDecoder
- symfony/mailer is required for tests of the
emailCountof the ApiPlatformTestCase - symfony/doctrine-messenger is required for tests of the
messageCountof the ApiPlatformTestCase - symfony/monolog-bundle is required for tests of the MonologAssertsTrait
- phpstan/phpstan and phpstan/phpstan-deprecation-rules run the static analysis
(
composer phpstan), also in CI - symfony/clock provides
DatePointfor fixed dates in the tests - symfony/error-handler >= 7.4.17 / 8.1.5 restores the exception handler it registers, older versions make the kernel tests risky
- symfony/polyfill-php85 >= 1.33 is required by rector/rector on PHP 8.4 (
array_last()), which does not require it itself - symfony/string is required for API Platform's inflector
- symfony/twig-bundle is required for tests of the FormatBytesExtension
- symfony/workflow is required for tests of the WorkflowHelper and PropertyMarkingStore
- monolog/monolog must be at least v3 for
Monolog\Level - api-platform/core and vrok/doctrine-addons are required for testing the ApiPlatform filters
Open ToDos
- ContainsFilter: option to match records that contain at least one of the values (OR), like
the
combinationof the JsonExistsFilter - remove the
jsonbmapping_typesfrom the test config when support for doctrine/dbal 4 is dropped, DBAL 5 maps jsonb columns to the jsonb type itself testOperation(): a parameter for finding the user by another field thanemail, e.g.user: ['username' => 'alice'], withemaildeprecated