Allow doctrine/doctrine-bundle ^3.0 and doctrine/dbal ^4.0 (#19064)
Some checks are pending
Continuous Integration (Minimal) / Static checks (push) Waiting to run
Continuous Integration (Minimal) / Tests (MariaDB) (push) Blocked by required conditions
Continuous Integration (Minimal) / Tests (MySQL) (push) Blocked by required conditions
Continuous Integration (Minimal) / Javascript Tests (MySQL) (push) Blocked by required conditions
Continuous Integration (Minimal) / Tests (PostgreSQL) (push) Blocked by required conditions
Continuous Integration (Minimal) / Frontend (push) Blocked by required conditions
Continuous Integration (Minimal) / Packages (push) Blocked by required conditions

| Q               | A
|-----------------|-----
| Branch?         | 2.3
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations? | no<!-- don't forget to update the UPGRADE-*.md file
-->
| Related tickets | duplicates
https://github.com/Sylius/Sylius/pull/18754, partially
https://github.com/Sylius/Sylius/issues/18760
| License         | MIT

This PR is a duplication of #18754 (which targets the `symfony-8`
branch). It reuses that PR's commits to bring the broadened Doctrine
support into `2.3`.

### What it does

Widens the supported Doctrine stack, **opt-in via `||`, no minimum is
bumped**:

  - `doctrine/doctrine-bundle` `^2.13 || ^3.0`
  - `doctrine/dbal` `^3.9 || ^4.0`
  - `doctrine/persistence` `^3.3 || ^4.0`
  - `doctrine/data-fixtures` `^1.7 || ^2.2` (dev)

Plus the compatibility work the newer stack requires:

- DBAL 3.x/4.x compatibility layers (platform detection,
`getSchemaManager()` → `createSchemaManager()`,
    `SqlitePlatform`/`SQLitePlatform`, `QueryBuilder::select()`);
- a custom `ObjectType` registered as `object` (the built-in `object`
type was removed in DBAL 4),
covering the only two `type="object"` mappings:
`PaymentSecurityToken.details` and `PaymentRequest.payload`;
- config updates: removed `auto_generate_proxy_classes` (gone in
DoctrineBundle 3), PSR-6 cache pools,
and forced `SEQUENCE` identity generation on PostgreSQL to keep the
schema backward compatible;
  - CI matrix entries to test both DoctrineBundle 2 and 3.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added support for DoctrineBundle 3 and DBAL 4.
  * Added a custom DBAL “object” type to keep payment data compatible.
* **Documentation**
* Added Sylius 2.3 upgrade notes, including Doctrine cache pool
migration and PostgreSQL identity/sequence behavior.
* **Bug Fixes**
* Updated Doctrine ORM cache configuration to use Symfony cache pools
(production and test setups).
* Fixed timestamp handling to consistently store mutable `DateTime`
values where required.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Jan Góralski 2026-06-18 10:56:49 +02:00 committed by GitHub
commit f745ee5aa0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 543 additions and 249 deletions

View file

@ -44,7 +44,7 @@ jobs:
phpunit-cli-api:
needs: get-matrix
runs-on: ubuntu-latest
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -73,6 +73,14 @@ jobs:
composer require winzou/state-machine:^0.4 --no-update
composer require winzou/state-machine-bundle:^0.6 --no-update
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin
@ -145,7 +153,7 @@ jobs:
behat-ui:
needs: get-matrix
runs-on: ubuntu-latest
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}${{ matrix.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -174,6 +182,14 @@ jobs:
composer require winzou/state-machine:^0.4 --no-update
composer require winzou/state-machine-bundle:^0.6 --no-update
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin

View file

@ -44,7 +44,7 @@ jobs:
phpunit-cli-api:
needs: get-matrix
runs-on: ubuntu-latest
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}"
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -70,6 +70,14 @@ jobs:
if: matrix.twig == '^2.12'
run: composer require --no-update --no-scripts --no-interaction "twig/twig:${{ matrix.twig }}"
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin
@ -85,8 +93,8 @@ jobs:
uses: SyliusLabs/BuildTestAppAction@v4
with:
build_type: "sylius"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}"
e2e: "yes"
database: "mysql:${{ matrix.mysql }}"
php_version: ${{ matrix.php }}
@ -131,7 +139,7 @@ jobs:
behat-ui:
needs: get-matrix
runs-on: ubuntu-latest
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}"
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -157,6 +165,14 @@ jobs:
if: matrix.twig == '^2.12'
run: composer require --no-update --no-scripts --no-interaction "twig/twig:${{ matrix.twig }}"
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin
@ -172,8 +188,8 @@ jobs:
uses: SyliusLabs/BuildTestAppAction@v4
with:
build_type: "sylius"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}"
e2e: "yes"
database: "mysql:${{ matrix.mysql }}"
php_version: ${{ matrix.php }}

View file

@ -44,7 +44,7 @@ jobs:
phpunit-cli-api:
needs: get-matrix
runs-on: ubuntu-latest
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, PostgreSQL ${{ matrix.postgres }}"
name: "PHPUnit, CLI, API, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, PostgreSQL ${{ matrix.postgres }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -66,6 +66,14 @@ jobs:
if: "${{ inputs.branch == '' }}"
uses: actions/checkout@v4
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin
@ -81,8 +89,8 @@ jobs:
uses: SyliusLabs/BuildTestAppAction@v4
with:
build_type: "sylius"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}-"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}-"
e2e: "yes"
database: "postgres:${{ matrix.postgres }}"
php_version: ${{ matrix.php }}
@ -119,7 +127,7 @@ jobs:
behat-ui:
needs: get-matrix
runs-on: ubuntu-latest
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, PostgreSQL ${{ matrix.postgres }}"
name: "UI, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, PostgreSQL ${{ matrix.postgres }}${{ matrix.doctrine_bundle && format(', DoctrineBundle {0}', matrix.doctrine_bundle) || '' }}"
timeout-minutes: 20
strategy:
fail-fast: false
@ -141,6 +149,14 @@ jobs:
if: "${{ inputs.branch == '' }}"
uses: actions/checkout@v4
- name: Require Doctrine Bundle
if: ${{ matrix.doctrine_bundle != '' && matrix.doctrine_bundle != null }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/doctrine-bundle:${{ matrix.doctrine_bundle }}"
- name: Require DBAL 3.x for DoctrineBundle 2.x
if: ${{ matrix.doctrine_bundle == '^2.0' }}
run: composer require --no-update --no-scripts --no-interaction "doctrine/dbal:^3.9"
- name: Prepare manifest.json files
run: |
mkdir -p public/build/admin
@ -156,8 +172,8 @@ jobs:
uses: SyliusLabs/BuildTestAppAction@v4
with:
build_type: "sylius"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-"
cache_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}-"
cache_restore_key: "${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-doctrine-${{ matrix.doctrine_bundle || 'default' }}-"
e2e: "yes"
database: "postgres:${{ matrix.postgres }}"
php_version: ${{ matrix.php }}

View file

@ -22,14 +22,14 @@
"php": "8.4",
"symfony": "~6.4.0",
"mariadb": "10.11.13",
"dbal": "^3.0",
"doctrine_bundle": "^2.0",
"state_machine_adapter": "winzou_state_machine"
},
{
"php": "8.5",
"symfony": "~7.4.0",
"mariadb": "11.4.7",
"dbal": "^3.0",
"doctrine_bundle": "^3.0",
"state_machine_adapter": "symfony_workflow"
}
]
@ -40,13 +40,15 @@
"php": "8.4",
"symfony": "~6.4.0",
"mysql": "8.0",
"twig": "^3.3"
"twig": "^3.3",
"doctrine_bundle": "^2.0"
},
{
"php": "8.5",
"symfony": "~7.4.0",
"mysql": "8.4",
"twig": "^3.3"
"twig": "^3.3",
"doctrine_bundle": "^3.0"
}
]
},
@ -103,12 +105,14 @@
{
"php": "8.4",
"symfony": "~6.4.0",
"postgres": "15.13"
"postgres": "15.13",
"doctrine_bundle": "^2.0"
},
{
"php": "8.5",
"symfony": "~7.4.0",
"postgres": "17.5"
"postgres": "17.5",
"doctrine_bundle": "^3.0"
}
]
},
@ -155,6 +159,10 @@
"mariadb": [
"11.4.7"
],
"doctrine_bundle": [
"^2.0",
"^3.0"
],
"state_machine_adapter": [
"symfony_workflow"
],
@ -163,6 +171,7 @@
"php": "8.3",
"symfony": "~7.4.0",
"mariadb": "11.4.7",
"doctrine_bundle": "^2.0",
"state_machine_adapter": "winzou_state_machine"
}
]
@ -182,12 +191,17 @@
"twig": [
"^3.3"
],
"doctrine_bundle": [
"^2.0",
"^3.0"
],
"include": [
{
"php": "8.3",
"symfony": "~7.4.0",
"mysql": "8.0",
"twig": "^3.3"
"twig": "^3.3",
"doctrine_bundle": "^2.0"
}
]
},
@ -253,6 +267,20 @@
"15.13",
"16.9",
"17.5"
],
"include": [
{
"php": "8.4",
"symfony": "~6.4.0",
"postgres": "15.13",
"doctrine_bundle": "^2.0"
},
{
"php": "8.5",
"symfony": "~7.4.0",
"postgres": "17.5",
"doctrine_bundle": "^3.0"
}
]
},
"frontend": {

View file

@ -12,6 +12,38 @@
order_by_identifier: true
```
2. Configuration changes related to the broadened Doctrine support (see the *Dependencies* section).
These only matter once you opt into **DoctrineBundle 3** / **DBAL 4**:
- The `auto_generate_proxy_classes: "%kernel.debug%"` option was removed from Sylius' Doctrine configuration
(it no longer exists in DoctrineBundle 3, and since PHP 8.4 Doctrine uses native lazy objects). If you are still
on DoctrineBundle 2 and rely on this behavior, set it explicitly in your own application configuration.
- The ORM metadata/query/result cache configuration was switched from wrapped `Doctrine\Common\Cache` services
to **PSR-6 cache pools** (`type: pool`). This change lives in the application configuration (`config/packages/{prod,test}/doctrine.yaml`),
which is **not** updated automatically on existing installations, update those files in your project accordingly:
```diff
doctrine:
orm:
entity_managers:
default:
metadata_cache_driver:
- type: service
- id: doctrine.system_cache_provider
+ type: pool
+ pool: doctrine.system_cache_pool
```
As part of this switch, the `doctrine.result_cache_provider` and `doctrine.system_cache_provider` services
(defined in Sylius' application configuration, wrapping the PSR-6 pools via `Doctrine\Common\Cache\Psr6\DoctrineProvider`)
were **removed**. If you referenced them by id, use the cache pools directly (`doctrine.system_cache_pool`,
`doctrine.result_cache_pool`) with `type: pool` as shown above.
- On **PostgreSQL**, Sylius now forces the `SEQUENCE` identity generation strategy
(`identity_generation_preferences` for `PostgreSQLPlatform`) to keep the database schema backward compatible
with existing installations.
## Dependencies
1. The `behat/transliterator` package has been **deprecated** and will be removed in Sylius 3.0.
@ -68,6 +100,14 @@
If your application depends on these packages directly, require them explicitly in your `composer.json`.
4. The supported Doctrine version ranges have been **broadened** to allow the newer stack
(`doctrine/doctrine-bundle` `^2.13 || ^3.0`, `doctrine/dbal` `^3.9 || ^4.0`,
`doctrine/persistence` `^3.3 || ^4.0`, `doctrine/data-fixtures` `^1.7 || ^2.2`).
DBAL 4 removes the built-in `object` and `array` column types. Since Sylius maps two fields
as `type="object"` (`PaymentSecurityToken.details` and `PaymentRequest.payload`), it registers
a custom `Sylius\Bundle\PaymentBundle\Doctrine\DBAL\Type\ObjectType` to keep them working.
## Deprecations
1. Passing a `Sylius\Component\Core\Calculator\ProductVariantPricesCalculatorInterface` directly to the following catalog-facing classes is deprecated since Sylius 2.3.

View file

@ -58,6 +58,7 @@
"Behat\\Testwork\\Tester\\Setup\\Setup",
"Behat\\Testwork\\Tester\\Setup\\Teardown",
"Composer\\InstalledVersions",
"Doctrine\\DBAL\\Platforms\\MariaDb1027Platform",
"Doctrine\\DBAL\\Platforms\\MySqlPlatform",
"enshrined\\svgSanitize\\Sanitizer",
"FriendsOfBehat\\SymfonyExtension\\Mink\\Mink",

View file

@ -38,14 +38,14 @@
"behat/transliterator": "^1.5",
"doctrine/collections": "^2.2",
"doctrine/common": "^3.2",
"doctrine/dbal": "^3.9",
"doctrine/doctrine-bundle": "^2.13",
"doctrine/dbal": "^3.9 || ^4.0",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/event-manager": "^2.0",
"doctrine/inflector": "^2.0",
"doctrine/migrations": "^3.8",
"doctrine/orm": "^2.18 || ^3.5",
"doctrine/persistence": "^3.3",
"doctrine/persistence": "^3.3 || ^4.0",
"egulias/email-validator": "^4.0",
"fakerphp/faker": "^1.23",
"gedmo/doctrine-extensions": "^3.20",
@ -192,7 +192,7 @@
"dmore/behat-chrome-extension": "^1.4",
"dmore/chrome-mink-driver": "^2.9",
"doctrine/cache": "^2.2",
"doctrine/data-fixtures": "^1.7",
"doctrine/data-fixtures": "^1.7 || ^2.2",
"friends-of-behat/mink": "^1.11",
"friends-of-behat/mink-browserkit-driver": "^1.6",
"friends-of-behat/mink-debug-extension": "^2.1",
@ -201,7 +201,7 @@
"friends-of-behat/symfony-extension": "^2.6.2",
"friends-of-behat/variadic-extension": "^1.6",
"hwi/oauth-bundle": "^2.2",
"lchrusciel/api-test-case": "^5.3",
"lchrusciel/api-test-case": "^5.3.5",
"matthiasnoback/symfony-config-test": "^6.0",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"nyholm/psr7": "^1.8",

View file

@ -3,29 +3,14 @@ doctrine:
entity_managers:
default:
metadata_cache_driver:
type: service
id: doctrine.system_cache_provider
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: service
id: doctrine.system_cache_provider
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: service
id: doctrine.result_cache_provider
services:
doctrine.result_cache_provider:
class: Doctrine\Common\Cache\Psr6\DoctrineProvider
public: false
factory: ['Doctrine\Common\Cache\Psr6\DoctrineProvider', 'wrap']
arguments:
- '@doctrine.result_cache_pool'
doctrine.system_cache_provider:
class: Doctrine\Common\Cache\Psr6\DoctrineProvider
public: false
factory: [ 'Doctrine\Common\Cache\Psr6\DoctrineProvider', 'wrap' ]
arguments:
- '@doctrine.system_cache_pool'
type: pool
pool: doctrine.result_cache_pool
framework:
cache:

View file

@ -3,28 +3,14 @@ doctrine:
entity_managers:
default:
metadata_cache_driver:
type: service
id: doctrine.system_cache_provider
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: service
id: doctrine.system_cache_provider
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: service
id: doctrine.result_cache_provider
services:
doctrine.result_cache_provider:
class: Doctrine\Common\Cache\Psr6\DoctrineProvider
public: false
factory: ['Doctrine\Common\Cache\Psr6\DoctrineProvider', 'wrap']
arguments:
- '@doctrine.result_cache_pool'
doctrine.system_cache_provider:
class: Doctrine\Common\Cache\Psr6\DoctrineProvider
public: false
factory: [ 'Doctrine\Common\Cache\Psr6\DoctrineProvider', 'wrap' ]
arguments:
- '@doctrine.system_cache_pool'
type: pool
pool: doctrine.result_cache_pool
framework:
cache:

View file

@ -59,3 +59,10 @@ parameters:
identifier: arguments.count
count: 1
path: src/Sylius/Bundle/ShopBundle/Router/LocaleStrippingRouter.php
# DBAL 3.x/4.x compatibility - getSchemaManager() removed in DBAL 4.x
-
message: '/Call to an undefined method Doctrine\\DBAL\\Connection::getSchemaManager\(\)\./'
identifier: method.notFound
paths:
- src/Sylius/Bundle/CoreBundle/Migrations/

View file

@ -27,7 +27,10 @@ final class DoctrineORMContext implements Context
#[BeforeScenario]
public function purgeDatabase()
{
$this->entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
$configuration = $this->entityManager->getConnection()->getConfiguration();
if (method_exists($configuration, 'setSQLLogger')) {
$configuration->setSQLLogger(null);
}
$purger = new ORMPurger($this->entityManager);
$purger->purge();
$this->entityManager->clear();

View file

@ -816,7 +816,7 @@ final readonly class OrderContext implements Context
$customer->setFirstname('John');
$customer->setLastname('Doe' . $i);
$customer->setCreatedAt($this->clock->now());
$customer->setCreatedAt(\DateTime::createFromImmutable($this->clock->now()));
$customers[] = $customer;
@ -923,7 +923,7 @@ final readonly class OrderContext implements Context
$this->shipOrder($order);
}
$order->setCheckoutCompletedAt($this->clock->now());
$order->setCheckoutCompletedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->objectManager->persist($order);
$this->sharedStorage->set('order', $order);
@ -949,7 +949,7 @@ final readonly class OrderContext implements Context
$this->payOrder($order);
$order->setCheckoutCompletedAt($this->clock->now());
$order->setCheckoutCompletedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->objectManager->persist($order);
$this->sharedStorage->set('order', $order);
@ -985,7 +985,7 @@ final readonly class OrderContext implements Context
$this->shipOrder($order);
}
$order->setCheckoutCompletedAt($this->clock->now());
$order->setCheckoutCompletedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->objectManager->persist($order);
}
@ -1014,7 +1014,7 @@ final readonly class OrderContext implements Context
);
$order->setState($isFulfilled ? BaseOrderInterface::STATE_FULFILLED : BaseOrderInterface::STATE_NEW);
$order->setCheckoutCompletedAt($this->clock->now());
$order->setCheckoutCompletedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->objectManager->persist($order);
}

View file

@ -37,8 +37,8 @@
"phpstan/phpdoc-parser": ">= 2.0"
},
"require-dev": {
"doctrine/data-fixtures": "^1.7",
"doctrine/doctrine-bundle": "^2.13",
"doctrine/data-fixtures": "^1.7 || ^2.2",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -42,7 +42,7 @@ final class AllTaxons implements AllTaxonsInterface
$queryBuilder = $this->entityManager->getConnection()->createQueryBuilder();
$queryBuilder
->select([
->select(
'taxon.id as id',
'taxon.tree_root as tree_root',
'taxon.parent_id as parent_id',
@ -53,7 +53,7 @@ final class AllTaxons implements AllTaxonsInterface
'taxon.position as position',
'taxon.enabled as enabled',
'COALESCE(current_translation.name, fallback_translation.name) as name',
])
)
->from('sylius_taxon', 'taxon')
->leftJoin(
'taxon',

View file

@ -24,7 +24,7 @@ final class ArchivingPromotionApplicator implements ArchivingPromotionApplicator
public function archive(PromotionInterface $data): PromotionInterface
{
$data->setArchivedAt($this->calendar->now());
$data->setArchivedAt(\DateTime::createFromImmutable($this->calendar->now()));
return $data;
}

View file

@ -24,7 +24,7 @@ final readonly class ArchivingShippingMethodApplicator implements ArchivingShipp
public function archive(ShippingMethodInterface $data): ShippingMethodInterface
{
$data->setArchivedAt($this->clock->now());
$data->setArchivedAt(\DateTime::createFromImmutable($this->clock->now()));
return $data;
}

View file

@ -41,7 +41,7 @@ final readonly class RequestResetPasswordTokenHandler
}
$user->setPasswordResetToken($this->generator->generate());
$user->setPasswordRequestedAt($this->clock->now());
$user->setPasswordRequestedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->commandBus->dispatch(
new SendResetPasswordEmail(

View file

@ -44,7 +44,7 @@ final readonly class VerifyShopUserHandler
);
}
$user->setVerifiedAt($this->clock->now());
$user->setVerifiedAt(\DateTime::createFromImmutable($this->clock->now()));
$user->setEmailVerificationToken(null);
$user->enable();

View file

@ -24,7 +24,7 @@
],
"require": {
"php": "^8.2",
"doctrine/dbal": "^3.9",
"doctrine/dbal": "^3.9 || ^4.0",
"api-platform/doctrine-orm": "^4.2.1",
"api-platform/symfony": "^4.2.1",
"lexik/jwt-authentication-bundle": "^3.1",

View file

@ -41,7 +41,7 @@ final class ArchivingPromotionApplicatorTest extends TestCase
$this->clock->expects(self::once())->method('now')->willReturn($now);
$this->promotion->expects(self::once())->method('setArchivedAt')->with($now);
$this->promotion->expects(self::once())->method('setArchivedAt')->with(\DateTime::createFromImmutable($now));
self::assertSame($this->promotion, $this->archivingPromotionApplicator->archive($this->promotion));
}

View file

@ -64,12 +64,13 @@ final class RequestResetPasswordTokenHandlerTest extends TestCase
->method('findOneByEmail')
->with('test@email.com')
->willReturn($shopUser);
$this->clock->expects(self::once())->method('now')->willReturn(new \DateTimeImmutable());
$now = new \DateTimeImmutable();
$this->clock->expects(self::once())->method('now')->willReturn($now);
$this->generator->expects(self::once())->method('generate')->willReturn('TOKEN');
$shopUser->expects(self::once())->method('setPasswordResetToken')->with('TOKEN');
$shopUser->expects(self::once())
->method('setPasswordRequestedAt')
->with(self::isInstanceOf(\DateTimeImmutable::class));
->with(\DateTime::createFromImmutable($now));
$sendResetPasswordEmail = new SendResetPasswordEmail('test@email.com', 'WEB', 'en_US');
$this->messageBus->expects(self::once())
->method('dispatch')

View file

@ -55,11 +55,12 @@ final class VerifyShopUserHandlerTest extends TestCase
->method('findOneBy')
->with(['emailVerificationToken' => 'ToKeN'])
->willReturn($user);
$this->clock->expects(self::once())->method('now')->willReturn(new \DateTimeImmutable());
$now = new \DateTimeImmutable();
$this->clock->expects(self::once())->method('now')->willReturn($now);
$user->expects(self::once())->method('getEmail')->willReturn('shop@example.com');
$user->expects(self::once())
->method('setVerifiedAt')
->with($this->isInstanceOf(\DateTimeImmutable::class));
->with(\DateTime::createFromImmutable($now));
$user->expects(self::once())->method('setEmailVerificationToken')->with(null);
$user->expects(self::once())->method('enable');
$this->commandBus->expects(self::once())

View file

@ -34,7 +34,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -31,7 +31,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -44,7 +44,7 @@ final class RequestResetPasswordEmailHandler
}
$adminUser->setPasswordResetToken($this->generator->generate());
$adminUser->setPasswordRequestedAt($this->clock->now());
$adminUser->setPasswordRequestedAt(\DateTime::createFromImmutable($this->clock->now()));
$this->commandBus->dispatch(
new SendResetPasswordEmail($adminUser->getEmail(), $adminUser->getLocaleCode()),

View file

@ -13,6 +13,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\DependencyInjection;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\ORM\Mapping\ClassMetadata;
use Sylius\Bundle\CoreBundle\Attribute\AsCatalogPromotionApplicatorCriteria;
use Sylius\Bundle\CoreBundle\Attribute\AsCatalogPromotionPriceCalculator;
use Sylius\Bundle\CoreBundle\Attribute\AsEntityObserver;
@ -107,6 +109,7 @@ final class SyliusCoreExtension extends AbstractResourceExtension implements Pre
$this->prependDefaultDriver($container, $config['driver']);
$this->prependHwiOauth($container);
$this->prependDoctrineMigrations($container);
$this->prependDoctrineIdentityGenerationPreferences($container);
}
protected function getMigrationsNamespace(): string
@ -134,6 +137,21 @@ final class SyliusCoreExtension extends AbstractResourceExtension implements Pre
}
}
private function prependDoctrineIdentityGenerationPreferences(ContainerBuilder $container): void
{
if (!$container->hasExtension('doctrine')) {
return;
}
$container->prependExtensionConfig('doctrine', [
'orm' => [
'identity_generation_preferences' => [
PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
],
],
]);
}
private function prependHwiOauth(ContainerBuilder $container): void
{
if (!$container->hasExtension('hwi_oauth')) {

View file

@ -13,12 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Cast extends FunctionNode
{
@ -48,7 +48,7 @@ final class Cast extends FunctionNode
$type = $this->type;
if (is_a($platform, MySQLPlatform::class, true) && 'text' === $type) {
if (PlatformHelper::isMysql($platform) && 'text' === $type) {
$type = 'char';
}

View file

@ -13,14 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Day extends FunctionNode
{
@ -41,15 +39,15 @@ final class Day extends FunctionNode
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
if (is_a($platform, MySQLPlatform::class, true)) {
if (PlatformHelper::isMysql($platform)) {
return sprintf('DAY(%s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, PostgreSQLPlatform::class, true)) {
if (PlatformHelper::isPostgreSQL($platform)) {
return sprintf('EXTRACT(DAY FROM %s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, SqlitePlatform::class, true)) {
if (PlatformHelper::isSqlite($platform)) {
return sprintf('CAST(STRFTIME("%%d", %s) AS NUMBER)', $sqlWalker->walkArithmeticPrimary($this->date));
}

View file

@ -13,14 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Hour extends FunctionNode
{
@ -41,15 +39,15 @@ final class Hour extends FunctionNode
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
if (is_a($platform, MySQLPlatform::class, true)) {
if (PlatformHelper::isMysql($platform)) {
return sprintf('HOUR(%s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, PostgreSQLPlatform::class, true)) {
if (PlatformHelper::isPostgreSQL($platform)) {
return sprintf('EXTRACT(HOUR FROM %s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, SqlitePlatform::class, true)) {
if (PlatformHelper::isSqlite($platform)) {
return sprintf('CAST(STRFTIME("%%H", %s) AS NUMBER)', $sqlWalker->walkArithmeticPrimary($this->date));
}

View file

@ -13,14 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Month extends FunctionNode
{
@ -41,15 +39,15 @@ final class Month extends FunctionNode
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
if (is_a($platform, MySQLPlatform::class, true)) {
if (PlatformHelper::isMysql($platform)) {
return sprintf('MONTH(%s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, PostgreSQLPlatform::class, true)) {
if (PlatformHelper::isPostgreSQL($platform)) {
return sprintf('EXTRACT(MONTH FROM %s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, SqlitePlatform::class, true)) {
if (PlatformHelper::isSqlite($platform)) {
return sprintf('CAST(STRFTIME("%%m", %s) AS NUMBER)', $sqlWalker->walkArithmeticPrimary($this->date));
}

View file

@ -13,14 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Week extends FunctionNode
{
@ -41,15 +39,15 @@ final class Week extends FunctionNode
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
if (is_a($platform, MySQLPlatform::class, true)) {
if (PlatformHelper::isMysql($platform)) {
return sprintf('WEEK(%s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, PostgreSQLPlatform::class, true)) {
if (PlatformHelper::isPostgreSQL($platform)) {
return sprintf('EXTRACT(WEEK FROM %s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, SqlitePlatform::class, true)) {
if (PlatformHelper::isSqlite($platform)) {
return sprintf('CAST(STRFTIME("%%W", %s) AS NUMBER)', $sqlWalker->walkArithmeticPrimary($this->date));
}

View file

@ -13,14 +13,12 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
final class Year extends FunctionNode
{
@ -41,15 +39,15 @@ final class Year extends FunctionNode
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
if (is_a($platform, MySQLPlatform::class, true)) {
if (PlatformHelper::isMysql($platform)) {
return sprintf('YEAR(%s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, PostgreSQLPlatform::class, true)) {
if (PlatformHelper::isPostgreSQL($platform)) {
return sprintf('EXTRACT(YEAR FROM %s)', $sqlWalker->walkArithmeticPrimary($this->date));
}
if (is_a($platform, SqlitePlatform::class, true)) {
if (PlatformHelper::isSqlite($platform)) {
return sprintf('CAST(STRFTIME("%%Y", %s) AS NUMBER)', $sqlWalker->walkArithmeticPrimary($this->date));
}

View file

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Doctrine\Platform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
final class PlatformHelper
{
private function __construct()
{
}
public static function isMysql(object $platform): bool
{
return is_a($platform, MySQLPlatform::class, true);
}
public static function isPostgreSQL(object $platform): bool
{
return is_a($platform, PostgreSQLPlatform::class, true);
}
/** Compatibility layer for DBAL 3.x (SqlitePlatform) and 4.x (SQLitePlatform). */
public static function isSqlite(object $platform): bool
{
return str_contains(strtolower($platform::class), 'sqliteplatform');
}
}

View file

@ -18,6 +18,7 @@ use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\ORM\EntityManagerInterface;
use Sylius\Bundle\CoreBundle\Doctrine\Platform\PlatformHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -121,12 +122,6 @@ final class DatabaseSetupCommandsProvider implements DatabaseSetupCommandsProvid
private function isSQLite(): bool
{
$platform = $this->entityManager->getConnection()->getDatabasePlatform();
if (class_exists(\Doctrine\DBAL\Platforms\SqlitePlatform::class) && is_a($platform, \Doctrine\DBAL\Platforms\SqlitePlatform::class)) {
return true;
}
return false;
return PlatformHelper::isSqlite($this->entityManager->getConnection()->getDatabasePlatform());
}
}

View file

@ -34,7 +34,7 @@ final class PriceChangeLogger implements PriceChangeLoggerInterface
$logEntry = $this->logEntryFactory->create(
$channelPricing,
$this->clock->now(),
\DateTime::createFromImmutable($this->clock->now()),
$channelPricing->getPrice(),
$channelPricing->getOriginalPrice(),
);

View file

@ -41,7 +41,6 @@ sylius_core:
doctrine:
orm:
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
default:
auto_mapping: true

View file

@ -55,8 +55,9 @@ final class DatabasePlatformDataProvider implements DataProviderInterface
{
try {
$platform = $connection->getDatabasePlatform();
if ($platform instanceof AbstractPlatform) {
return $platform->getName();
$platformName = $this->getPlatformName($platform);
if (null !== $platformName) {
return $platformName;
}
} catch (\Throwable) {
}
@ -69,6 +70,27 @@ final class DatabasePlatformDataProvider implements DataProviderInterface
return null;
}
/** Compatibility layer for DBAL 3.x (getName()) and 4.x (class name based) */
private function getPlatformName(AbstractPlatform $platform): ?string
{
// DBAL 3.x has getName() method
if (method_exists($platform, 'getName')) {
return $platform->getName();
}
// DBAL 4.x removed getName(), use class name mapping
$className = strtolower($platform::class);
return match (true) {
str_contains($className, 'mysql') => 'mysql',
str_contains($className, 'postgresql') => 'postgresql',
str_contains($className, 'sqlite') => 'sqlite',
str_contains($className, 'sqlserver'), str_contains($className, 'sqlsrv') => 'mssql',
str_contains($className, 'oracle') => 'oracle',
default => null,
};
}
private function mapDriverToDatabase(string $driver): ?string
{
return match (true) {

View file

@ -27,7 +27,7 @@
],
"require": {
"php": "^8.2",
"doctrine/dbal": "^3.9",
"doctrine/dbal": "^3.9 || ^4.0",
"doctrine/doctrine-migrations-bundle": "^3.3",
"egulias/email-validator": "^4.0",
"fakerphp/faker": "^1.23",
@ -71,7 +71,7 @@
"phpstan/phpdoc-parser": ">= 2.0"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"hwi/oauth-bundle": "^2.2",
"matthiasnoback/symfony-config-test": "^6.0",

View file

@ -13,7 +13,6 @@ declare(strict_types=1);
namespace Tests\Sylius\Bundle\CoreBundle\Calculator;
use DateTime;
use PHPUnit\Framework\TestCase;
use Sylius\Bundle\CoreBundle\Calculator\DelayStampCalculator;
use Sylius\Bundle\CoreBundle\Calculator\DelayStampCalculatorInterface;
@ -35,8 +34,8 @@ final class DelayStampCalculatorTest extends TestCase
public function testCalculatesDelayStampFromGivenDates(): void
{
$currentTime = new DateTime('2021-11-11 20:20');
$targetTime = new DateTime('2021-11-11 20:21');
$currentTime = new \DateTime('2021-11-11 20:20');
$targetTime = new \DateTime('2021-11-11 20:21');
$expectedStamp = new DelayStamp(60000);
@ -47,8 +46,8 @@ final class DelayStampCalculatorTest extends TestCase
public function testReturns0DelayIfDatesTargetTimeIsSmallerThanCurrentTime(): void
{
$currentTime = new DateTime('2021-11-11 20:21');
$targetTime = new DateTime('2021-11-11 19:05');
$currentTime = new \DateTime('2021-11-11 20:21');
$targetTime = new \DateTime('2021-11-11 19:05');
$expectedStamp = new DelayStamp(0);

View file

@ -78,7 +78,7 @@ final class RequestResetPasswordEmailHandlerTest extends TestCase
$adminUser->expects($this->once())->method('getEmail')->willReturn('admin@example.com');
$adminUser->expects($this->once())->method('getLocaleCode')->willReturn('en_US');
$adminUser->expects($this->once())->method('setPasswordResetToken')->with('sometoken');
$adminUser->expects($this->once())->method('setPasswordRequestedAt')->with($now);
$adminUser->expects($this->once())->method('setPasswordRequestedAt')->with(\DateTime::createFromImmutable($now));
$expectedMessage = new SendResetPasswordEmail('admin@example.com', 'en_US');

View file

@ -66,7 +66,7 @@ final class AtomicOrderPromotionsUsageModifierTest extends TestCase
$this->entityManager
->expects($this->exactly(2))
->method('lock')
->willReturnCallback(function (object $entity, int $lockMode, mixed $version) use ($promotion, $coupon): void {
->willReturnCallback(function (object $entity, int|LockMode $lockMode, mixed $version) use ($promotion, $coupon): void {
$this->assertSame(LockMode::OPTIMISTIC, $lockMode);
if ($entity === $promotion) {
@ -134,7 +134,7 @@ final class AtomicOrderPromotionsUsageModifierTest extends TestCase
$this->entityManager
->expects($this->exactly(2))
->method('lock')
->willReturnCallback(function (object $entity, int $lockMode, mixed $version) use ($promotion, $coupon): void {
->willReturnCallback(function (object $entity, int|LockMode $lockMode, mixed $version) use ($promotion, $coupon): void {
$this->assertSame(LockMode::OPTIMISTIC, $lockMode);
if ($entity === $promotion) {
@ -202,7 +202,7 @@ final class AtomicOrderPromotionsUsageModifierTest extends TestCase
$this->entityManager
->expects($this->exactly(2))
->method('lock')
->willReturnCallback(function (object $entity, int $lockMode, mixed $version) use ($firstPromotion, $secondPromotion): void {
->willReturnCallback(function (object $entity, int|LockMode $lockMode, mixed $version) use ($firstPromotion, $secondPromotion): void {
$this->assertSame(LockMode::OPTIMISTIC, $lockMode);
if ($entity === $firstPromotion) {

View file

@ -76,7 +76,7 @@ final class PriceChangeLoggerTest extends TestCase
$this->logEntryFactory
->expects($this->once())
->method('create')
->with($channelPricing, $now, $price, $originalPrice)
->with($channelPricing, \DateTime::createFromImmutable($now), $price, $originalPrice)
->willReturn($logEntry)
;

View file

@ -32,7 +32,7 @@
"symfony/intl": "^6.4 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -44,7 +44,7 @@
"webmozart/assert": "^1.11"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"phpunit/phpunit": "^11.5",
"symfony/browser-kit": "^6.4 || ^7.4",
"symfony/dependency-injection": "^6.4.1 || ^7.4",

View file

@ -36,7 +36,7 @@
"phpstan/phpdoc-parser": ">= 2.0"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"phpunit/phpunit": "^11.5",
"symfony/browser-kit": "^6.4 || ^7.4",

View file

@ -33,7 +33,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -32,7 +32,7 @@
"webmozart/assert": "^1.11"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"phpunit/phpunit": "^11.5",
"sylius/currency-bundle": "^2.0",

View file

@ -36,7 +36,7 @@
"symfony/service-contracts": "^3.5"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\PaymentBundle\Doctrine\DBAL\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
/**
* Provides the "object" type for DBAL 4.x compatibility.
*
* The "object" type was removed in DBAL 4.0. This custom type restores it
* so that existing entity mappings using type="object" continue to work
* across both DBAL 3.x and 4.x.
*/
final class ObjectType extends Type
{
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
return $platform->getClobTypeDeclarationSQL($column);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform): string
{
return serialize($value);
}
public function convertToPHPValue($value, AbstractPlatform $platform): mixed
{
if ($value === null) {
return null;
}
if (is_resource($value)) {
$value = stream_get_contents($value);
}
set_error_handler(static function (int $code, string $message): never {
throw new \RuntimeException(sprintf('Could not unserialize object: %s', $message));
});
try {
return unserialize($value);
} finally {
restore_error_handler();
}
}
public function getName(): string
{
return 'object';
}
}

View file

@ -7,6 +7,11 @@ imports:
parameters:
env(SYLIUS_PAYMENT_ENCRYPTION_KEY_PATH): '%kernel.project_dir%/config/encryption/key'
doctrine:
dbal:
types:
object: Sylius\Bundle\PaymentBundle\Doctrine\DBAL\Type\ObjectType
sylius_payment:
payment_request:
states_to_be_cancelled_when_payment_method_changed:

View file

@ -35,7 +35,7 @@
"symfony/messenger": "^6.4 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -13,7 +13,6 @@ declare(strict_types=1);
namespace Sylius\Bundle\ProductBundle\Doctrine\ORM\Query\AST\Function;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\ORM\Query\SqlWalker;
@ -22,13 +21,10 @@ use Doctrine\ORM\Query\SqlWalker;
*/
abstract class AbstractPostgresqlJsonFunctionNode extends AbstractJsonFunctionNode
{
/**
* @throws Exception
*/
protected function validatePlatform(SqlWalker $sqlWalker): void
{
if (!$sqlWalker->getConnection()->getDatabasePlatform() instanceof PostgreSQLPlatform) {
throw Exception::notSupported(static::FUNCTION_NAME);
throw new \LogicException(sprintf('Function "%s" is not supported on this platform.', static::FUNCTION_NAME));
}
}

View file

@ -35,7 +35,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -40,7 +40,7 @@
"phpstan/phpdoc-parser": ">= 2.0"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -47,7 +47,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"phpunit/phpunit": "^11.5",
"symfony/browser-kit": "^6.4 || ^7.4",

View file

@ -24,6 +24,6 @@ final class ShippingDateAssigner implements ShippingDateAssignerInterface
public function assign(ShipmentInterface $shipment): void
{
$shipment->setShippedAt($this->clock->now());
$shipment->setShippedAt(\DateTime::createFromImmutable($this->clock->now()));
}
}

View file

@ -37,7 +37,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -44,7 +44,7 @@ final class ShippingDateAssignerTest extends TestCase
$shipment = $this->createMock(ShipmentInterface::class);
$this->clock->expects($this->once())->method('now')->willReturn(new DateTimeImmutable('20-05-2019 20:20:20'));
$shipment->expects($this->once())->method('setShippedAt')->with(new DateTimeImmutable('20-05-2019 20:20:20'));
$shipment->expects($this->once())->method('setShippedAt')->with(new \DateTime('20-05-2019 20:20:20'));
$this->shippingDateAssigner->assign($shipment);
}

View file

@ -36,7 +36,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -35,7 +35,7 @@
"symfony/framework-bundle": "^6.4.1 || ^7.4"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"doctrine/orm": "^2.18 || ^3.5",
"phpunit/phpunit": "^11.5",
"symfony/dependency-injection": "^6.4.1 || ^7.4"

View file

@ -48,7 +48,7 @@
"webmozart/assert": "^1.11"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-bundle": "^2.13 || ^3.0",
"hwi/oauth-bundle": "^2.2",
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
"phpunit/phpunit": "^11.5",

View file

@ -52,7 +52,8 @@ final class PromotionCouponGenerator implements PromotionCouponGeneratorInterfac
$coupon->setPromotion($promotion);
$coupon->setCode($code);
$coupon->setUsageLimit($instruction->getUsageLimit());
$coupon->setExpiresAt($instruction->getExpiresAt());
$expiresAt = $instruction->getExpiresAt();
$coupon->setExpiresAt($expiresAt instanceof \DateTimeImmutable ? \DateTime::createFromImmutable($expiresAt) : $expiresAt);
$generatedCoupons[$code] = $coupon;

View file

@ -194,12 +194,6 @@
"instaclick/php-webdriver": {
"version": "1.4.7"
},
"knplabs/gaufrette": {
"version": "v0.8.3"
},
"knplabs/knp-gaufrette-bundle": {
"version": "v0.7.1"
},
"knplabs/knp-menu": {
"version": "v3.1.2"
},

View file

@ -263,7 +263,9 @@ trait OrderPlacerTrait
): OrderInterface {
$objectManager = $this->get('doctrine.orm.entity_manager');
$order->setCheckoutCompletedAt($checkoutCompletedAt);
$order->setCheckoutCompletedAt(
$checkoutCompletedAt !== null ? \DateTime::createFromImmutable($checkoutCompletedAt) : null,
);
$objectManager->flush();

View file

@ -13,25 +13,14 @@ declare(strict_types=1);
namespace Sylius\Tests\Functional\Doctrine\Mock;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
class ConnectionMock extends Connection
{
/** @var DatabasePlatformMock */
private $_platformMock;
public function __construct(array $params = [], ?Driver $driver = null, ?Configuration $config = null, ?EventManager $eventManager = null)
public function __construct(array $params = [], ?Driver $driver = null, ?Configuration $config = null)
{
$this->_platformMock = new DatabasePlatformMock();
parent::__construct($params, $driver ?? new DriverMock(), $config, $eventManager);
}
public function getDatabasePlatform()
{
return $this->_platformMock;
parent::__construct($params, $driver ?? new DriverMock(), $config);
}
}

View file

@ -23,32 +23,39 @@ class DatabasePlatformMock extends AbstractPlatform
return true;
}
public function getBooleanTypeDeclarationSQL(array $field)
public function getBooleanTypeDeclarationSQL(array $field): string
{
return 'BOOLEAN';
}
public function getIntegerTypeDeclarationSQL(array $field)
public function getIntegerTypeDeclarationSQL(array $field): string
{
return 'INT';
}
public function getBigIntTypeDeclarationSQL(array $field)
public function getBigIntTypeDeclarationSQL(array $field): string
{
return 'BIGINT';
}
public function getSmallIntTypeDeclarationSQL(array $field)
public function getSmallIntTypeDeclarationSQL(array $field): string
{
return 'SMALLINT';
}
protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef): string
{
return 'INT';
}
public function getVarcharTypeDeclarationSQL(array $field)
public function getVarcharTypeDeclarationSQL(array $field): string
{
return 'VARCHAR';
}
public function getClobTypeDeclarationSQL(array $field)
public function getClobTypeDeclarationSQL(array $field): string
{
return 'CLOB';
}
public function getName(): string

View file

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Tests\Functional\Doctrine\Mock;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
class Dbal4DriverConnectionMock implements Connection
{
public function prepare(string $sql): Statement
{
return new Dbal4StatementMock();
}
public function query(string $sql): Result
{
return new DriverResultMock();
}
public function quote(string $value): string
{
return $value;
}
public function exec(string $sql): int|string
{
throw new \BadMethodCallException('Not implemented');
}
public function lastInsertId(): int|string
{
return 0;
}
public function beginTransaction(): void
{
}
public function commit(): void
{
}
public function rollBack(): void
{
}
public function getNativeConnection(): mixed
{
throw new \BadMethodCallException('Not implemented');
}
public function getServerVersion(): string
{
return '3.0.0';
}
}

View file

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Tests\Functional\Doctrine\Mock;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
class Dbal4StatementMock implements Statement
{
public function bindValue(int|string $param, mixed $value, ParameterType $type = ParameterType::STRING): void
{
}
public function execute(): Result
{
return new DriverResultMock();
}
}

View file

@ -20,47 +20,43 @@ use Doctrine\DBAL\ParameterType;
class DriverConnectionMock implements Connection
{
public function prepare($prepareString): Statement
public function prepare(string $sql): Statement
{
return new StatementMock();
}
public function query(?string $sql = null): Result
public function query(string $sql): Result
{
return new DriverResultMock();
}
public function quote($input, $type = ParameterType::STRING)
public function quote($value, $type = ParameterType::STRING)
{
return (string) $input;
return (string) $value;
}
public function exec($statement): int
public function exec(string $sql): int
{
throw new \BadMethodCallException('Not implemented');
}
public function lastInsertId($name = null)
public function lastInsertId($name = null): string|int|false
{
return false;
}
public function beginTransaction()
public function beginTransaction(): bool
{
return true;
}
public function commit()
public function commit(): bool
{
return true;
}
public function rollBack()
{
}
public function errorCode()
{
}
public function errorInfo()
public function rollBack(): bool
{
return true;
}
}

View file

@ -13,41 +13,18 @@ declare(strict_types=1);
namespace Sylius\Tests\Functional\Doctrine\Mock;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\ServerVersionProvider;
class DriverMock implements Driver
class DriverMock extends AbstractSQLiteDriver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
public function connect(array $params): DriverConnection
{
if (is_a(DriverConnection::class, ServerVersionProvider::class, true)) {
return new Dbal4DriverConnectionMock();
}
return new DriverConnectionMock();
}
public function getDatabasePlatform()
{
throw new \BadMethodCallException('Not implemented');
}
public function getSchemaManager(Connection $conn, ?AbstractPlatform $platform = null): AbstractSchemaManager
{
throw new \BadMethodCallException('Not implemented');
}
public function getExceptionConverter(): ExceptionConverter
{
throw new \BadMethodCallException('Not implemented');
}
public function getName()
{
throw new \BadMethodCallException('Not implemented');
}
public function getDatabase(Connection $conn)
{
throw new \BadMethodCallException('Not implemented');
}
}

View file

@ -21,26 +21,27 @@ use Doctrine\DBAL\Driver\Result;
*/
class DriverResultMock implements Result
{
/** @var list<array<string, mixed>> */
private array $resultSet;
/**
* Creates a new mock statement that will serve the provided fake result set to clients.
*
* @param array $resultSet The faked SQL result set.
* @param list<array<string, mixed>> $resultSet The faked SQL result set.
*/
public function __construct(array $resultSet = [])
{
$this->resultSet = $resultSet;
}
public function fetchNumeric()
public function fetchNumeric(): array|false
{
$row = $this->fetchAssociative();
return $row === false ? false : array_values($row);
}
public function fetchAssociative()
public function fetchAssociative(): array|false
{
$current = current($this->resultSet);
next($this->resultSet);
@ -48,7 +49,7 @@ class DriverResultMock implements Result
return $current;
}
public function fetchOne()
public function fetchOne(): mixed
{
$row = $this->fetchNumeric();

View file

@ -21,12 +21,14 @@ use Traversable;
class StatementMock implements IteratorAggregate, Statement
{
public function bindValue($param, $value, $type = null)
public function bindValue($param, $value, $type = null): bool
{
return true;
}
public function bindParam($column, &$variable, $type = null, $length = null)
public function bindParam($param, &$variable, $type = null, $length = null): bool
{
return true;
}
public function execute($params = null): Result