mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
Resolve conflicts between 2.1 and 2.2
This commit is contained in:
commit
197648f0ed
400 changed files with 4433 additions and 1453 deletions
2
.github/workflows/ci__full_1_14.yaml
vendored
2
.github/workflows/ci__full_1_14.yaml
vendored
|
|
@ -78,7 +78,7 @@ jobs:
|
||||||
name: "Notify about build status"
|
name: "Notify about build status"
|
||||||
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, frontend, packages]
|
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, frontend, packages]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Process data"
|
- name: "Process data"
|
||||||
|
|
|
||||||
2
.github/workflows/ci__full_2_1.yaml
vendored
2
.github/workflows/ci__full_2_1.yaml
vendored
|
|
@ -88,7 +88,7 @@ jobs:
|
||||||
name: "Notify about build status"
|
name: "Notify about build status"
|
||||||
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, e2e-js, frontend, packages]
|
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, e2e-js, frontend, packages]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Process data"
|
- name: "Process data"
|
||||||
|
|
|
||||||
2
.github/workflows/ci__full_2_2.yaml
vendored
2
.github/workflows/ci__full_2_2.yaml
vendored
|
|
@ -88,7 +88,7 @@ jobs:
|
||||||
name: "Notify about build status"
|
name: "Notify about build status"
|
||||||
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, e2e-js, frontend, packages]
|
needs: [static-checks, e2e-mariadb, e2e-mysql, e2e-pgsql, e2e-js, frontend, packages]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Process data"
|
- name: "Process data"
|
||||||
|
|
|
||||||
4
.github/workflows/ci_e2e-mariadb.yaml
vendored
4
.github/workflows/ci_e2e-mariadb.yaml
vendored
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
@ -158,7 +158,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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.api_platform && format(', ApiPlatform {0}', matrix.api_platform) || '' }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
|
||||||
4
.github/workflows/ci_e2e-mysql.yaml
vendored
4
.github/workflows/ci_e2e-mysql.yaml
vendored
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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 }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
@ -144,7 +144,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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 }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
|
||||||
4
.github/workflows/ci_e2e-pgsql.yaml
vendored
4
.github/workflows/ci_e2e-pgsql.yaml
vendored
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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 }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
@ -132,7 +132,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
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 }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
|
||||||
6
.github/workflows/ci_e2e-unstable.yaml
vendored
6
.github/workflows/ci_e2e-unstable.yaml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
behat-no-js-unstable:
|
behat-no-js-unstable:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
@ -98,7 +98,7 @@ jobs:
|
||||||
behat-ui-js-chromedriver-unstable:
|
behat-ui-js-chromedriver-unstable:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "JS with Chromedriver, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env || 'test_cached' }}), MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
name: "JS with Chromedriver, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env || 'test_cached' }}), MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -180,7 +180,7 @@ jobs:
|
||||||
behat-ui-js-panther-unstable:
|
behat-ui-js-panther-unstable:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "JS with Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env || 'test_cached' }}), MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
name: "JS with Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env || 'test_cached' }}), MySQL ${{ matrix.mysql }} (Unstable Dependencies)"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
||||||
4
.github/workflows/ci_js.yaml
vendored
4
.github/workflows/ci_js.yaml
vendored
|
|
@ -53,7 +53,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "[${{ matrix.group }}] [PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}]"
|
name: "[${{ matrix.group }}] [PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}]"
|
||||||
timeout-minutes: 10
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||||
|
|
@ -134,7 +134,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}"
|
name: "Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}"
|
||||||
timeout-minutes: 10
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.get-matrix.outputs.panther-matrix) }}
|
matrix: ${{ fromJson(needs.get-matrix.outputs.panther-matrix) }}
|
||||||
|
|
|
||||||
2
.github/workflows/ci_packages-unstable.yaml
vendored
2
.github/workflows/ci_packages-unstable.yaml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
||||||
test_unstable:
|
test_unstable:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (Unstable Dependencies)"
|
name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (Unstable Dependencies)"
|
||||||
timeout-minutes: 25
|
timeout-minutes: 15
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
|
||||||
2
.github/workflows/ci_packages.yaml
vendored
2
.github/workflows/ci_packages.yaml
vendored
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
needs: get-matrix
|
needs: get-matrix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}"
|
name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}"
|
||||||
timeout-minutes: 25
|
timeout-minutes: 15
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
|
||||||
13
.github/workflows/matrix.json
vendored
13
.github/workflows/matrix.json
vendored
|
|
@ -8,7 +8,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"php": "8.4",
|
"php": "8.4",
|
||||||
"symfony": "~7.3.0"
|
"symfony": "~7.4.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"php": "8.5",
|
"php": "8.5",
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"php": "8.5",
|
"php": "8.5",
|
||||||
"symfony": "~7.3.0"
|
"symfony": "~7.4.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,6 @@
|
||||||
],
|
],
|
||||||
"symfony": [
|
"symfony": [
|
||||||
"~6.4.0",
|
"~6.4.0",
|
||||||
"~7.3.0",
|
|
||||||
"~7.4.0"
|
"~7.4.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -151,7 +150,6 @@
|
||||||
],
|
],
|
||||||
"symfony": [
|
"symfony": [
|
||||||
"~6.4.0",
|
"~6.4.0",
|
||||||
"~7.3.0",
|
|
||||||
"~7.4.0"
|
"~7.4.0"
|
||||||
],
|
],
|
||||||
"mariadb": [
|
"mariadb": [
|
||||||
|
|
@ -163,7 +161,7 @@
|
||||||
"include": [
|
"include": [
|
||||||
{
|
{
|
||||||
"php": "8.3",
|
"php": "8.3",
|
||||||
"symfony": "~7.3.0",
|
"symfony": "~7.4.0",
|
||||||
"mariadb": "11.4.7",
|
"mariadb": "11.4.7",
|
||||||
"state_machine_adapter": "winzou_state_machine"
|
"state_machine_adapter": "winzou_state_machine"
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +174,6 @@
|
||||||
],
|
],
|
||||||
"symfony": [
|
"symfony": [
|
||||||
"~6.4.0",
|
"~6.4.0",
|
||||||
"~7.3.0",
|
|
||||||
"~7.4.0"
|
"~7.4.0"
|
||||||
],
|
],
|
||||||
"mysql": [
|
"mysql": [
|
||||||
|
|
@ -188,7 +185,7 @@
|
||||||
"include": [
|
"include": [
|
||||||
{
|
{
|
||||||
"php": "8.3",
|
"php": "8.3",
|
||||||
"symfony": "~7.3.0",
|
"symfony": "~7.4.0",
|
||||||
"mysql": "8.0",
|
"mysql": "8.0",
|
||||||
"twig": "^3.3"
|
"twig": "^3.3"
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +247,6 @@
|
||||||
],
|
],
|
||||||
"symfony": [
|
"symfony": [
|
||||||
"~6.4.0",
|
"~6.4.0",
|
||||||
"~7.3.0",
|
|
||||||
"~7.4.0"
|
"~7.4.0"
|
||||||
],
|
],
|
||||||
"postgres": [
|
"postgres": [
|
||||||
|
|
@ -273,7 +269,6 @@
|
||||||
],
|
],
|
||||||
"symfony": [
|
"symfony": [
|
||||||
"~6.4.0",
|
"~6.4.0",
|
||||||
"~7.3.0",
|
|
||||||
"~7.4.0"
|
"~7.4.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
.github/workflows/refactor.yaml
vendored
2
.github/workflows/refactor.yaml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
|
|
||||||
name: "Coding standard refactor"
|
name: "Coding standard refactor"
|
||||||
|
|
||||||
timeout-minutes: 5
|
timeout-minutes: 15
|
||||||
|
|
||||||
if: github.repository == 'Sylius/Sylius'
|
if: github.repository == 'Sylius/Sylius'
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/workflows/upmerge_pr.yaml
vendored
2
.github/workflows/upmerge_pr.yaml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'Sylius/Sylius'
|
if: github.repository == 'Sylius/Sylius'
|
||||||
name: "Upmerge PR"
|
name: "Upmerge PR"
|
||||||
timeout-minutes: 5
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
||||||
75
AGENTS.md
75
AGENTS.md
|
|
@ -1,18 +1,51 @@
|
||||||
# AI Contribution Guidelines
|
# AI Contribution Guidelines
|
||||||
|
|
||||||
Welcome, 🤖 AI assistant! Please follow these guidelines when contributing to this Sylius:
|
Guidelines for AI assistants contributing to Sylius.
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
When working on specific areas, check these files for patterns:
|
||||||
|
|
||||||
|
### Entities & Models
|
||||||
|
- Entity pattern: `src/Sylius/Component/Core/Model/Product.php`
|
||||||
|
- Interface pattern: `src/Sylius/Component/Core/Model/ProductInterface.php`
|
||||||
|
- Doctrine mapping: `src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/`
|
||||||
|
|
||||||
|
### API Platform 4.x
|
||||||
|
- Resource definitions: `src/Sylius/Bundle/ApiBundle/Resources/config/api_platform/resources/`
|
||||||
|
- Properties/serialization: `src/Sylius/Bundle/ApiBundle/Resources/config/api_platform/properties/`
|
||||||
|
- Admin resources: `resources/admin/Product.xml`
|
||||||
|
- Shop resources: `resources/shop/Product.xml`
|
||||||
|
|
||||||
|
### Services & Configuration
|
||||||
|
- Service definitions: `src/Sylius/Bundle/CoreBundle/Resources/config/services.xml`
|
||||||
|
- Bundle config: `src/Sylius/Bundle/*/Resources/config/`
|
||||||
|
|
||||||
|
### Templates & Hooks
|
||||||
|
- Admin templates: `src/Sylius/Bundle/AdminBundle/templates/`
|
||||||
|
- Shop templates: `src/Sylius/Bundle/ShopBundle/templates/`
|
||||||
|
- Twig hooks: check existing hooks in templates for naming patterns
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- PHPUnit functional: `tests/Functional/`
|
||||||
|
- PHPUnit API: `tests/Api/`
|
||||||
|
- Behat contexts: `src/Sylius/Behat/Context/`
|
||||||
|
|
||||||
|
### Migrations
|
||||||
|
- Location: `src/Sylius/Bundle/CoreBundle/Migrations/`
|
||||||
|
- **ALWAYS create TWO migrations**: one for MySQL, one for PostgreSQL
|
||||||
|
- MySQL: extend `Sylius\Bundle\CoreBundle\Doctrine\Migrations\AbstractMigration`
|
||||||
|
- PostgreSQL: extend `Sylius\Bundle\CoreBundle\Doctrine\Migrations\AbstractPostgreSQLMigration`
|
||||||
|
- When reviewing PRs that touch migrations, verify both versions exist
|
||||||
|
|
||||||
## General Guidelines
|
## General Guidelines
|
||||||
|
|
||||||
### Project Structure & Design
|
### Project Structure & Design
|
||||||
|
|
||||||
- This is Sylius: e-commerce framework
|
- Sylius is an e-commerce framework built on **Symfony**
|
||||||
- Sylius is built on top of **Symfony**
|
- Bundles and Components can be used independently
|
||||||
- Sylius must be easily extendable in the end application
|
|
||||||
- Sylius contains Bundles and Components that can be used independently
|
|
||||||
- Sylius is designed to be modular and flexible
|
|
||||||
- Sylius is designed to be fast and efficient
|
|
||||||
- Follow the Sylius Backward Compatibility (BC) policy
|
- Follow the Sylius Backward Compatibility (BC) policy
|
||||||
|
- When changing interfaces, always provide BC layer
|
||||||
|
|
||||||
### Compatibility & Security
|
### Compatibility & Security
|
||||||
|
|
||||||
|
|
@ -80,10 +113,29 @@ Welcome, 🤖 AI assistant! Please follow these guidelines when contributing to
|
||||||
|
|
||||||
## PHPUnit
|
## PHPUnit
|
||||||
|
|
||||||
|
- Place unit tests in `tests/Unit/`
|
||||||
|
- Place functional tests in `tests/Functional/`
|
||||||
|
- Place API tests in `tests/Api/`
|
||||||
|
- Test class names must end with `Test` suffix
|
||||||
|
- Use `#[Covers]` attribute for unit tests
|
||||||
|
- For API tests, extend `ApiTestCase` and use `assertResponse*` methods
|
||||||
|
- Run specific test: `vendor/bin/phpunit --filter TestClassName`
|
||||||
|
|
||||||
## Behat
|
## Behat
|
||||||
|
|
||||||
|
- Place feature files in `features/` directory
|
||||||
|
- Use existing contexts from `src/Sylius/Behat/Context/`
|
||||||
|
- Follow Given-When-Then pattern strictly
|
||||||
|
- Use `@ui` tag for UI tests, `@api` for API tests
|
||||||
|
- Page objects are in `src/Sylius/Behat/Page/`
|
||||||
|
|
||||||
## JavaScript
|
## JavaScript
|
||||||
|
|
||||||
|
- Use TypeScript where possible
|
||||||
|
- Use Stimulus controllers for interactive components
|
||||||
|
- Place controllers in `assets/admin/controllers/` or `assets/shop/controllers/`
|
||||||
|
- Follow existing naming conventions for controller files
|
||||||
|
|
||||||
## CSS
|
## CSS
|
||||||
|
|
||||||
- Use SCSS (`.scss`) syntax – plain CSS files are not allowed
|
- Use SCSS (`.scss`) syntax – plain CSS files are not allowed
|
||||||
|
|
@ -94,3 +146,12 @@ Welcome, 🤖 AI assistant! Please follow these guidelines when contributing to
|
||||||
- Avoid `!important` unless absolutely necessary
|
- Avoid `!important` unless absolutely necessary
|
||||||
- Prefer `rem` over `px` for spacing, font size, etc.
|
- Prefer `rem` over `px` for spacing, font size, etc.
|
||||||
- Use `mixins/` for reusable logic (e.g., `@include icon-size(24px)`)
|
- Use `mixins/` for reusable logic (e.g., `@include icon-size(24px)`)
|
||||||
|
|
||||||
|
## Common Mistakes to Avoid
|
||||||
|
|
||||||
|
- **BC breaks**: Never change method signatures in interfaces without deprecation layer
|
||||||
|
- **Missing Doctrine mapping**: New entity properties need XML mapping in `Resources/config/doctrine/`
|
||||||
|
- **Hardcoded strings**: Always use translation keys in templates
|
||||||
|
- **Missing serialization groups**: API properties need proper groups in `properties/*.xml`
|
||||||
|
- **Wrong namespace**: Components have no Symfony dependency, Bundles can
|
||||||
|
- **Forgetting tests**: API changes need PHPUnit tests in `tests/Api/`
|
||||||
|
|
|
||||||
115
CHANGELOG-2.2.md
Normal file
115
CHANGELOG-2.2.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# CHANGELOG FOR `2.2.X`
|
||||||
|
|
||||||
|
## v2.2.5 (2026-04-10)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18579](https://github.com/Sylius/Sylius/pull/18579) Fix problem with empty taxon product index ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18932](https://github.com/Sylius/Sylius/pull/18932) TASK: improve german translations for CH, DE, and AT ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#18933](https://github.com/Sylius/Sylius/pull/18933) fix: add default filter to breadcrumbs configuration title to prevent ScalarDataBag exception ([@camilleislasse](https://github.com/camilleislasse))
|
||||||
|
- [#18943](https://github.com/Sylius/Sylius/pull/18943) Fix build after APIPlatform 4.3.2 release ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18941](https://github.com/Sylius/Sylius/pull/18941) Add payment enabled in channel validation ([@marekrzytki](https://github.com/marekrzytki))
|
||||||
|
- [#18926](https://github.com/Sylius/Sylius/pull/18926) [BUGFIX] Allow updating provinceName when provinceCode is null in API ([@rust-le](https://github.com/rust-le))
|
||||||
|
- [#18953](https://github.com/Sylius/Sylius/pull/18953) [BUGFIX] Make mailer services public to allow usage in state machine callbacks ([@rust-le](https://github.com/rust-le))
|
||||||
|
- [#18961](https://github.com/Sylius/Sylius/pull/18961) Resolve flash alert correctly without breaking translation ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18940](https://github.com/Sylius/Sylius/pull/18940) Restore missing page titles for admin resource show pages ([@marekrzytki](https://github.com/marekrzytki))
|
||||||
|
- [#18922](https://github.com/Sylius/Sylius/pull/18922) Unified name translations handling in admin grid index ([@pbalcerzak](https://github.com/pbalcerzak))
|
||||||
|
- [#18965](https://github.com/Sylius/Sylius/pull/18965) Allow choosing different payment method with skip payment step when it gets disabled ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18969](https://github.com/Sylius/Sylius/pull/18969) Add missing status code mapping for OrderItemNotFoundException ([@marekrzytki](https://github.com/marekrzytki))
|
||||||
|
- [#18970](https://github.com/Sylius/Sylius/pull/18970) Add email validation constraints to cart update ([@marekrzytki](https://github.com/marekrzytki))
|
||||||
|
- [#18972](https://github.com/Sylius/Sylius/pull/18972) Fix admin templates ([@loic425](https://github.com/loic425))
|
||||||
|
- [#18958](https://github.com/Sylius/Sylius/pull/18958) [API] Make API Platform resource command classes overridable via container parameters ([@Prometee](https://github.com/Prometee))
|
||||||
|
- [#18974](https://github.com/Sylius/Sylius/pull/18974) Unify Tests directory with tests ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
|
||||||
|
## v2.2.4 (2026-03-18)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18904](https://github.com/sylius/sylius/issues/18904) [BUGFIX] remove redundant `object` from PHPDoc union types
|
||||||
|
- [#18899](https://github.com/sylius/sylius/issues/18899) [CS][DX] Refactor
|
||||||
|
- [#18898](https://github.com/sylius/sylius/issues/18898) [CS][DX] Refactor
|
||||||
|
- [#18895](https://github.com/sylius/sylius/issues/18895) [Admin] Fix product taxon grid `enabled` field always showing `true`
|
||||||
|
- [#18911](https://github.com/sylius/sylius/issues/18911) [BUGFIX] fix build errors
|
||||||
|
- [#18920](https://github.com/sylius/sylius/issues/18920) Telemetry improvements 2.1
|
||||||
|
|
||||||
|
## v2.2.3 (2026-03-09)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18747](https://github.com/Sylius/Sylius/pull/18747) Fix panther build ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18758](https://github.com/Sylius/Sylius/pull/18758) Remove duplicated serialization group field ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18785](https://github.com/Sylius/Sylius/pull/18785) Try to fix build after ResourceBundle release ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18742](https://github.com/Sylius/Sylius/pull/18742) [Admin] Fix order history address fields not displaying empty values ([@Wojdylak](https://github.com/Wojdylak))
|
||||||
|
- [#18806](https://github.com/Sylius/Sylius/pull/18806) Fix after new release of PayumBundle ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18836](https://github.com/Sylius/Sylius/pull/18836) Upgrade BuildTestAppAction from v3.0.1 to v4 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18832](https://github.com/Sylius/Sylius/pull/18832) [Admin] Fix images not being emitted with Webpack 5.105+ ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18841](https://github.com/Sylius/Sylius/pull/18841) Fix Dutch translation for payment method ([@JordiDekker](https://github.com/JordiDekker))
|
||||||
|
- [#18871](https://github.com/Sylius/Sylius/pull/18871) Add conflict to api-platform/serializer 4.2.17 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18888](https://github.com/Sylius/Sylius/pull/18888) Remove redundant check with apip4.1.7 in matrix ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18887](https://github.com/Sylius/Sylius/pull/18887) [BUGFIX] Make GitHub actions green again 2.1 ([@rust-le](https://github.com/rust-le))
|
||||||
|
- [#18844](https://github.com/Sylius/Sylius/pull/18844) Fix formatting in UPGRADE-2.0.md ([@LucaGallinari](https://github.com/LucaGallinari))
|
||||||
|
|
||||||
|
## v2.2.2 (2026-01-20)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18691](https://github.com/Sylius/Sylius/pull/18691) [CS][DX] Refactor
|
||||||
|
- [#18690](https://github.com/Sylius/Sylius/pull/18690) [CS][DX] Refactor
|
||||||
|
- [#18689](https://github.com/Sylius/Sylius/pull/18689) [CS][DX] Refactor
|
||||||
|
- [#18693](https://github.com/Sylius/Sylius/pull/18693) [CI] Standardize job timeouts to 15 minutes ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18703](https://github.com/Sylius/Sylius/pull/18703) [Deps] Allow psr/http-message ^2.0 ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18716](https://github.com/Sylius/Sylius/pull/18716) Try to fix build with PHP 8.5 and 8.4 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18707](https://github.com/Sylius/Sylius/pull/18707) [DX] Update branch aliases to 1.14-dev ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18721](https://github.com/Sylius/Sylius/pull/18721) Fix tests application error templates ([@loic425](https://github.com/loic425))
|
||||||
|
- [#16892](https://github.com/Sylius/Sylius/pull/16892) [FIXTURES] Fix menu taxon code ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18366](https://github.com/Sylius/Sylius/pull/18366) Fix ro PayumBundle Translation ([@revoltek-daniel](https://github.com/revoltek-daniel))
|
||||||
|
- [#18725](https://github.com/Sylius/Sylius/pull/18725) Bugfix/merged overrides missing operations ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18702](https://github.com/Sylius/Sylius/pull/18702) [Composer] Remove outdated twig/twig conflicts from bundles ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18722](https://github.com/Sylius/Sylius/pull/18722) [API] Payment Request fix default action when IRI is given ([@Prometee](https://github.com/Prometee))
|
||||||
|
- [#18732](https://github.com/Sylius/Sylius/pull/18732) Make PostgreSQL telemetry migration extend dedicated abstract ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18369](https://github.com/Sylius/Sylius/pull/18369) Add form help rendering to forms ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18735](https://github.com/Sylius/Sylius/pull/18735) [CS][DX] Refactor
|
||||||
|
- [#18726](https://github.com/Sylius/Sylius/pull/18726) [Channel] Resolve localhost equivalents when matching channel by hostname ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18708](https://github.com/Sylius/Sylius/pull/18708) [DX] Improve AI contribution guidelines ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18711](https://github.com/Sylius/Sylius/pull/18711) Fix typo in first_name.html.twig ([@LucaGallinari](https://github.com/LucaGallinari))
|
||||||
|
- [#18736](https://github.com/Sylius/Sylius/pull/18736) TASK: [2.2] [UiBundle] adjust translations for admin password reset page ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#18565](https://github.com/Sylius/Sylius/pull/18565) Add ternary operator to fix related with empty key ([@michalkaczmarek-bitbag](https://github.com/michalkaczmarek-bitbag))
|
||||||
|
- [#18572](https://github.com/Sylius/Sylius/pull/18572) Bugfix/fix autocomplete in admin to be case insensitive ([@michalkaczmarek-bitbag](https://github.com/michalkaczmarek-bitbag))
|
||||||
|
|
||||||
|
## v2.2.1 (2025-12-18)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18680](https://github.com/Sylius/Sylius/pull/18680) [Telemetry] Fixes and improvements 1.14 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
|
||||||
|
## v2.2.0 (2025-12-17)
|
||||||
|
|
||||||
|
#### Details
|
||||||
|
|
||||||
|
- [#18365](https://github.com/Sylius/Sylius/pull/18365) [Feature] Prioritize default locale in TranslationLocaleProvider output ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18412](https://github.com/Sylius/Sylius/pull/18412) [Maintenance] Raise minimal version of `api-platform/symfony` to `^4.2.1` ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18410](https://github.com/Sylius/Sylius/pull/18410) [SHOP][FEATURE] Introduce address form modifiers in checkout component ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18438](https://github.com/Sylius/Sylius/pull/18438) Remove phpspec/prophecy-phpunit dependency ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18374](https://github.com/Sylius/Sylius/pull/18374) [CORE] Add priority to Zone ([@tomkalon](https://github.com/tomkalon))
|
||||||
|
- [#18546](https://github.com/Sylius/Sylius/pull/18546) [API] Return 422 instead of 400 for missing required fields ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18544](https://github.com/Sylius/Sylius/pull/18544) [FEATURE] Add estimated delivery time functionality ([@Cholin2000](https://github.com/Cholin2000))
|
||||||
|
- [#18508](https://github.com/Sylius/Sylius/pull/18508) FEATURE: [2.2] [AdminBundle] make search menu in admin sidebar component hookable ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#18583](https://github.com/Sylius/Sylius/pull/18583) Mark method as deprecated because it is not used already ([@michalkaczmarek-bitbag](https://github.com/michalkaczmarek-bitbag))
|
||||||
|
- [#18614](https://github.com/Sylius/Sylius/pull/18614) [CS][DX] Refactor
|
||||||
|
- [#18594](https://github.com/Sylius/Sylius/pull/18594) [Maintenance] Bump Symfony to `^6.4 || ^7.4` ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18568](https://github.com/Sylius/Sylius/pull/18568) [API][PaymentRequest] Rename payment request operations for shop context ([@Rafikooo](https://github.com/Rafikooo))
|
||||||
|
- [#18554](https://github.com/Sylius/Sylius/pull/18554) [AdminBundle] Allowing to use translation domain on menu ([@loic425](https://github.com/loic425))
|
||||||
|
- [#18454](https://github.com/Sylius/Sylius/pull/18454) FEAT: [2.2] [CoreBundle] extend sylius:install:setup command to set admin first/last name ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#15889](https://github.com/Sylius/Sylius/pull/15889) FEATURE: [2.2] [AdminBundle] Introduce sylius:admin-user:delete CLI Command ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#15946](https://github.com/Sylius/Sylius/pull/15946) FEATURE: [2.2] [AdminBundle] Introduce sylius:admin-user:list command ([@crydotsnake](https://github.com/crydotsnake))
|
||||||
|
- [#18646](https://github.com/Sylius/Sylius/pull/18646) [Maintenance] validation fixes ([@Cholin2000](https://github.com/Cholin2000))
|
||||||
|
- [#18654](https://github.com/Sylius/Sylius/pull/18654) [CS][DX] Refactor
|
||||||
|
- [#18647](https://github.com/Sylius/Sylius/pull/18647) [Composer] Bump dependencies + remove unused dev dependencies ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18655](https://github.com/Sylius/Sylius/pull/18655) Add missing upgrade note to the 2.1 ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18652](https://github.com/Sylius/Sylius/pull/18652) [CS][DX] Refactor
|
||||||
|
- [#18659](https://github.com/Sylius/Sylius/pull/18659) Add missing index to Order XML mapping ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18661](https://github.com/Sylius/Sylius/pull/18661) Add config/reference.php to .gitignore ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18662](https://github.com/Sylius/Sylius/pull/18662) Make new Zone priority field sortable by Gedmo ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18669](https://github.com/Sylius/Sylius/pull/18669) Fix migrations skip commands 1.14 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
- [#18670](https://github.com/Sylius/Sylius/pull/18670) Remove Gedmo sortable-position from new Zone priority field ([@GSadee](https://github.com/GSadee))
|
||||||
|
- [#18672](https://github.com/Sylius/Sylius/pull/18672) Fix migrations skip commands 2.1 ([@TheMilek](https://github.com/TheMilek))
|
||||||
|
|
@ -1,6 +1,66 @@
|
||||||
# UPGRADE FROM `2.1` TO `2.2`
|
# UPGRADE FROM `2.1` TO `2.2`
|
||||||
|
|
||||||
### Deprecations
|
## Telemetry
|
||||||
|
|
||||||
|
Sylius 2.2.0 introduces anonymous telemetry to help us understand how Sylius is used and improve the platform.
|
||||||
|
|
||||||
|
**What data is collected:**
|
||||||
|
- Anonymous installation ID (hashed, non-reversible)
|
||||||
|
- Sylius and PHP versions, default locale
|
||||||
|
- Aggregated statistics as segments (broad ranges, not exact values):
|
||||||
|
- Customers/products/variants count (e.g., "1K-10K", "100K-1M")
|
||||||
|
- GMV and AOV ranges per month (e.g., "100K-500K", "50-100")
|
||||||
|
|
||||||
|
**No sensitive data is ever collected** - no customer information, no order details, no personal data.
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
|
||||||
|
Telemetry is enabled by default and uses a default salt for hashing the installation ID.
|
||||||
|
|
||||||
|
To disable telemetry, set the following environment variable in your `.env` file:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
SYLIUS_TELEMETRY_ENABLED=0
|
||||||
|
```
|
||||||
|
|
||||||
|
To change the salt, set the `SYLIUS_TELEMETRY_SALT` environment variable:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
SYLIUS_TELEMETRY_SALT=your-custom-salt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
1. Not injecting a `tagged_iterator` with the tag `sylius_shop.modifier.address_form_values` into the constructor of `Sylius\Bundle\ShopBundle\Twig\Component\Checkout\Address\FormComponent` is deprecated since Sylius 2.2 and will be required in Sylius 3.0.
|
||||||
|
|
||||||
|
This change enables extending the checkout address form with custom fields or logic by registering services tagged with `sylius_shop.modifier.address_form_values`, which implement the `AddressFormValuesModifierInterface`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function __construct(
|
||||||
|
OrderRepositoryInterface $repository,
|
||||||
|
FormFactoryInterface $formFactory,
|
||||||
|
string $resourceClass,
|
||||||
|
string $formClass,
|
||||||
|
protected readonly CustomerContextInterface $customerContext,
|
||||||
|
protected readonly UserRepositoryInterface $shopUserRepository,
|
||||||
|
protected readonly AddressRepositoryInterface $addressRepository,
|
||||||
|
+ protected readonly ?iterable $addressFormValuesModifiers = null,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
1. Direct usage of `loader.svg` and `loader.gif` assets is deprecated.
|
1. Direct usage of `loader.svg` and `loader.gif` assets is deprecated.
|
||||||
Use `@SyliusAdmin/shared/helper/loader.html.twig` or `@SyliusShop/shared/macro/loader.html.twig` instead.
|
Use `@SyliusAdmin/shared/helper/loader.html.twig` or `@SyliusShop/shared/macro/loader.html.twig` instead.
|
||||||
|
|
||||||
|
1. The `Sylius\Bundle\ReviewBundle\Updater\ReviewableRatingUpdaterInterface::updateFromReview()` method has been deprecated and will be removed in Sylius 3.0. Use state machine mechanism implemented by Symfony Workflow instead.
|
||||||
|
|
||||||
|
## Admin UI
|
||||||
|
|
||||||
|
1. A new `modal-portal.js` script has been added to `AdminBundle`.
|
||||||
|
It moves Bootstrap modal elements to `<body>` before they are displayed,
|
||||||
|
preventing them from being rendered behind the Bootstrap backdrop when nested inside a CSS stacking context (e.g. the sticky `.page-header`).
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
1. The `TranslationLocaleProvider` now ensures that the default locale (configured as `locale` in `config/parameters.yaml`)
|
||||||
|
is always placed at the beginning of the returned locales array.
|
||||||
|
Other locales remain in the same order as returned by the repository.
|
||||||
|
|
|
||||||
70
UPGRADE-API-2.2.md
Normal file
70
UPGRADE-API-2.2.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# UPGRADE FROM `2.1` TO `2.2`
|
||||||
|
|
||||||
|
## Modified routes
|
||||||
|
|
||||||
|
1. The routes for payment requests resource have been renamed to follow the shop API naming convention.
|
||||||
|
|
||||||
|
| Old route | New route |
|
||||||
|
|----------------------------------------|----------------------------------------|
|
||||||
|
| `sylius_api_show_payment_request_get` | `sylius_api_shop_payment_request_get` |
|
||||||
|
| `sylius_api_show_payment_request_post` | `sylius_api_shop_payment_request_post` |
|
||||||
|
| `sylius_api_show_payment_request_put` | `sylius_api_shop_payment_request_put` |
|
||||||
|
|
||||||
|
## HTTP Status Code Changes
|
||||||
|
|
||||||
|
### Missing Required Fields Validation
|
||||||
|
|
||||||
|
The HTTP status code for missing required fields in API requests has been changed from `400 Bad Request` to `422 Unprocessable Content` to follow REST API best practices and RFC 9110 semantics.
|
||||||
|
|
||||||
|
Additionally, the redundant `code` field has been removed from the error response body, as the status code is already available in the HTTP response headers.
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "/api/v2/contexts/Error",
|
||||||
|
"@type": "hydra:Error",
|
||||||
|
"status": 400,
|
||||||
|
"detail": "Request does not have the following required fields specified: email."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "/api/v2/contexts/Error",
|
||||||
|
"@type": "hydra:Error",
|
||||||
|
"status": 422,
|
||||||
|
"detail": "Request does not have the following required fields specified: email."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Breaking changes:**
|
||||||
|
1. HTTP status code changed: `400` → `422`
|
||||||
|
2. Response body field `code` removed (was redundant with HTTP status header)
|
||||||
|
|
||||||
|
**Affected endpoints:** All POST/PATCH endpoints that validate required fields (e.g., `/api/v2/shop/customers`, `/api/v2/shop/orders/{token}/items`, etc.)
|
||||||
|
|
||||||
|
**Migration guide:**
|
||||||
|
- If your API client checks `response.status === 400` for missing fields, change it to `response.status === 422`
|
||||||
|
- If your API client reads `response.data.code`, use `response.status` (HTTP header) instead
|
||||||
|
|
||||||
|
**References:** RFC 9110 (400 = syntactic errors, 422 = semantic/validation errors)
|
||||||
|
|
||||||
|
|
||||||
|
## API Platform resource classes as container parameters
|
||||||
|
|
||||||
|
The following API Platform resource classes are now defined as container parameters, allowing you to override them:
|
||||||
|
|
||||||
|
| Parameter | Default class |
|
||||||
|
|---|---|
|
||||||
|
| `sylius_api.command.account.reset_password.class` | `Sylius\Bundle\ApiBundle\Command\Account\ResetPassword` |
|
||||||
|
| `sylius_api.command.account.verify_shop_user.class` | `Sylius\Bundle\ApiBundle\Command\Account\VerifyShopUser` |
|
||||||
|
| `sylius_api.command.admin.account.reset_password.class` | `Sylius\Bundle\ApiBundle\Command\Admin\Account\ResetPassword` |
|
||||||
|
| `sylius_api.command.send_contact_request.class` | `Sylius\Bundle\ApiBundle\Command\SendContactRequest` |
|
||||||
|
|
||||||
|
To override a class, define the parameter in your configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
parameters:
|
||||||
|
sylius_api.command.account.reset_password.class: App\Api\Command\CustomResetPassword
|
||||||
|
```
|
||||||
132
composer.json
132
composer.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "sylius/sylius",
|
"name": "sylius/sylius",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"version": "v2.1.16-dev",
|
"version": "v2.2.6-dev",
|
||||||
"description": "E-Commerce platform for PHP, based on Symfony framework.",
|
"description": "E-Commerce platform for PHP, based on Symfony framework.",
|
||||||
"homepage": "https://sylius.com",
|
"homepage": "https://sylius.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -31,10 +31,10 @@
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-simplexml": "*",
|
"ext-simplexml": "*",
|
||||||
"ext-sodium": "*",
|
"ext-sodium": "*",
|
||||||
"api-platform/doctrine-orm": "^4.1.7",
|
"api-platform/doctrine-orm": "^4.2.1",
|
||||||
"api-platform/state": "^4.1.7",
|
"api-platform/state": "^4.2.1",
|
||||||
"api-platform/symfony": "^4.1.7",
|
"api-platform/symfony": "^4.2.1",
|
||||||
"babdev/pagerfanta-bundle": "^4.4",
|
"babdev/pagerfanta-bundle": "^4.6",
|
||||||
"behat/transliterator": "^1.5",
|
"behat/transliterator": "^1.5",
|
||||||
"doctrine/collections": "^2.2",
|
"doctrine/collections": "^2.2",
|
||||||
"doctrine/common": "^3.2",
|
"doctrine/common": "^3.2",
|
||||||
|
|
@ -44,12 +44,12 @@
|
||||||
"doctrine/event-manager": "^2.0",
|
"doctrine/event-manager": "^2.0",
|
||||||
"doctrine/inflector": "^2.0",
|
"doctrine/inflector": "^2.0",
|
||||||
"doctrine/migrations": "^3.8",
|
"doctrine/migrations": "^3.8",
|
||||||
"doctrine/orm": "^2.18 || ^3.3",
|
"doctrine/orm": "^2.18 || ^3.5",
|
||||||
"doctrine/persistence": "^3.3",
|
"doctrine/persistence": "^3.3",
|
||||||
"egulias/email-validator": "^4.0",
|
"egulias/email-validator": "^4.0",
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
"friendsofphp/proxy-manager-lts": "^1.0",
|
"friendsofphp/proxy-manager-lts": "^1.0",
|
||||||
"gedmo/doctrine-extensions": "^3.17.1",
|
"gedmo/doctrine-extensions": "^3.20",
|
||||||
"guzzlehttp/guzzle": "^7.9",
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
"guzzlehttp/psr7": "^2.5",
|
"guzzlehttp/psr7": "^2.5",
|
||||||
"knplabs/gaufrette": "^0.11",
|
"knplabs/gaufrette": "^0.11",
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
"laminas/laminas-stdlib": "^3.19",
|
"laminas/laminas-stdlib": "^3.19",
|
||||||
"league/flysystem-bundle": "^3.3",
|
"league/flysystem-bundle": "^3.3",
|
||||||
"lexik/jwt-authentication-bundle": "^3.1",
|
"lexik/jwt-authentication-bundle": "^3.1",
|
||||||
"liip/imagine-bundle": "^2.13",
|
"liip/imagine-bundle": "^2.15",
|
||||||
"pagerfanta/pagerfanta": "^4.0",
|
"pagerfanta/pagerfanta": "^4.0",
|
||||||
"paragonie/halite": "^5.0",
|
"paragonie/halite": "^5.0",
|
||||||
"payum/offline": "^1.7.5",
|
"payum/offline": "^1.7.5",
|
||||||
|
|
@ -85,65 +85,65 @@
|
||||||
"sylius/resource": "^1.12",
|
"sylius/resource": "^1.12",
|
||||||
"sylius/resource-bundle": "^1.12",
|
"sylius/resource-bundle": "^1.12",
|
||||||
"sylius/theme-bundle": "^2.4",
|
"sylius/theme-bundle": "^2.4",
|
||||||
"sylius/twig-extra": "^0.8",
|
"sylius/twig-extra": "^0.9",
|
||||||
"sylius/twig-hooks": "^0.8",
|
"sylius/twig-hooks": "^0.9",
|
||||||
"symfony/asset": "^6.4 || ^7.2",
|
"symfony/asset": "^6.4 || ^7.4",
|
||||||
"symfony/cache-contracts": "^3.5",
|
"symfony/cache-contracts": "^3.5",
|
||||||
"symfony/clock": "^6.4 || ^7.2",
|
"symfony/clock": "^6.4 || ^7.4",
|
||||||
"symfony/config": "^6.4 || ^7.2",
|
"symfony/config": "^6.4 || ^7.4",
|
||||||
"symfony/console": "^6.4.1 || ^7.2",
|
"symfony/console": "^6.4.1 || ^7.4",
|
||||||
"symfony/dependency-injection": "^6.4.1 || ^7.2",
|
"symfony/dependency-injection": "^6.4.1 || ^7.4",
|
||||||
"symfony/deprecation-contracts": "^3.5",
|
"symfony/deprecation-contracts": "^3.5",
|
||||||
"symfony/doctrine-bridge": "^6.4 || ^7.2",
|
"symfony/doctrine-bridge": "^6.4 || ^7.4",
|
||||||
"symfony/doctrine-messenger": "^6.4.1 || ^7.2",
|
"symfony/doctrine-messenger": "^6.4.1 || ^7.4",
|
||||||
"symfony/event-dispatcher": "^6.4 || ^7.2",
|
"symfony/event-dispatcher": "^6.4 || ^7.4",
|
||||||
"symfony/expression-language": "^6.4 || ^7.2",
|
"symfony/expression-language": "^6.4 || ^7.4",
|
||||||
"symfony/filesystem": "^6.4 || ^7.2",
|
"symfony/filesystem": "^6.4 || ^7.4",
|
||||||
"symfony/finder": "^6.4 || ^7.2",
|
"symfony/finder": "^6.4 || ^7.4",
|
||||||
"symfony/form": "^6.4.1 || ^7.2",
|
"symfony/form": "^6.4.1 || ^7.4",
|
||||||
"symfony/framework-bundle": "^6.4.1 || ^7.2",
|
"symfony/framework-bundle": "^6.4.1 || ^7.4",
|
||||||
"symfony/http-client": "^6.4 || ^7.2",
|
"symfony/http-client": "^6.4 || ^7.4",
|
||||||
"symfony/http-foundation": "^6.4 || ^7.2",
|
"symfony/http-foundation": "^6.4 || ^7.4",
|
||||||
"symfony/http-kernel": "^6.4.1 || ^7.2",
|
"symfony/http-kernel": "^6.4.1 || ^7.4",
|
||||||
"symfony/intl": "^6.4 || ^7.2",
|
"symfony/intl": "^6.4 || ^7.4",
|
||||||
"symfony/mailer": "^6.4 || ^7.2",
|
"symfony/mailer": "^6.4 || ^7.4",
|
||||||
"symfony/messenger": "^6.4 || ^7.2",
|
"symfony/messenger": "^6.4 || ^7.4",
|
||||||
"symfony/monolog-bundle": "^3.8.0",
|
"symfony/monolog-bundle": "^3.8.0",
|
||||||
"symfony/options-resolver": "^6.4 || ^7.2",
|
"symfony/options-resolver": "^6.4 || ^7.4",
|
||||||
"symfony/password-hasher": "^6.4 || ^7.2",
|
"symfony/password-hasher": "^6.4 || ^7.4",
|
||||||
"symfony/polyfill-iconv": "^1.31",
|
"symfony/polyfill-iconv": "^1.31",
|
||||||
"symfony/polyfill-intl-icu": "^1.31",
|
"symfony/polyfill-intl-icu": "^1.31",
|
||||||
"symfony/polyfill-mbstring": "^1.31",
|
"symfony/polyfill-mbstring": "^1.31",
|
||||||
"symfony/process": "^6.4 || ^7.2",
|
"symfony/process": "^6.4 || ^7.4",
|
||||||
"symfony/property-access": "^6.4 || ^7.2",
|
"symfony/property-access": "^6.4 || ^7.4",
|
||||||
"symfony/property-info": "^6.4 || ^7.2",
|
"symfony/property-info": "^6.4 || ^7.4",
|
||||||
"symfony/proxy-manager-bridge": "^6.4 || ^7.2",
|
"symfony/proxy-manager-bridge": "^6.4 || ^7.4",
|
||||||
"symfony/routing": "^6.4.1 || ^7.2",
|
"symfony/routing": "^6.4.1 || ^7.4",
|
||||||
"symfony/security-bundle": "^6.4 || ^7.2",
|
"symfony/security-bundle": "^6.4 || ^7.4",
|
||||||
"symfony/security-core": "^6.4 || ^7.2",
|
"symfony/security-core": "^6.4 || ^7.4",
|
||||||
"symfony/security-csrf": "^6.4 || ^7.2",
|
"symfony/security-csrf": "^6.4 || ^7.4",
|
||||||
"symfony/security-http": "^6.4 || ^7.2",
|
"symfony/security-http": "^6.4 || ^7.4",
|
||||||
"symfony/serializer": "^6.4 || ^7.2",
|
"symfony/serializer": "^6.4 || ^7.4",
|
||||||
"symfony/service-contracts": "^3.5",
|
"symfony/service-contracts": "^3.5",
|
||||||
"symfony/stimulus-bundle": "^2.20",
|
"symfony/stimulus-bundle": "^2.25",
|
||||||
"symfony/string": "^6.4 || ^7.2",
|
"symfony/string": "^6.4 || ^7.4",
|
||||||
"symfony/translation": "^6.4 || ^7.2",
|
"symfony/translation": "^6.4 || ^7.4",
|
||||||
"symfony/translation-contracts": "^3.3",
|
"symfony/translation-contracts": "^3.3",
|
||||||
"symfony/twig-bundle": "^6.4 || ^7.2",
|
"symfony/twig-bundle": "^6.4 || ^7.4",
|
||||||
"symfony/uid": "^6.4 || ^7.2",
|
"symfony/uid": "^6.4 || ^7.4",
|
||||||
"symfony/ux-autocomplete": "^2.20",
|
"symfony/ux-autocomplete": "^2.25",
|
||||||
"symfony/ux-icons": "^2.20",
|
"symfony/ux-icons": "^2.25",
|
||||||
"symfony/ux-live-component": "^2.20",
|
"symfony/ux-live-component": "^2.25",
|
||||||
"symfony/ux-twig-component": "^2.20",
|
"symfony/ux-twig-component": "^2.25",
|
||||||
"symfony/validator": "^6.4 || ^7.2",
|
"symfony/validator": "^6.4 || ^7.4",
|
||||||
"symfony/webpack-encore-bundle": "^2.2",
|
"symfony/webpack-encore-bundle": "^2.2",
|
||||||
"symfony/workflow": "^6.4 || ^7.2",
|
"symfony/workflow": "^6.4 || ^7.4",
|
||||||
"symfony/yaml": "^6.4 || ^7.2",
|
"symfony/yaml": "^6.4 || ^7.4",
|
||||||
"symfonycasts/dynamic-forms": "^0.1",
|
"symfonycasts/dynamic-forms": "^0.1",
|
||||||
"twig/extra-bundle": "^3.13",
|
"twig/extra-bundle": "^3.16",
|
||||||
"twig/intl-extra": "^3.13",
|
"twig/intl-extra": "^3.16",
|
||||||
"twig/string-extra": "^3.13",
|
"twig/string-extra": "^3.16",
|
||||||
"twig/twig": "^3.14",
|
"twig/twig": "^3.16",
|
||||||
"webmozart/assert": "^1.11"
|
"webmozart/assert": "^1.11"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
|
|
@ -206,15 +206,11 @@
|
||||||
"friends-of-behat/symfony-extension": "^2.6.2",
|
"friends-of-behat/symfony-extension": "^2.6.2",
|
||||||
"friends-of-behat/variadic-extension": "^1.6",
|
"friends-of-behat/variadic-extension": "^1.6",
|
||||||
"hwi/oauth-bundle": "^2.2",
|
"hwi/oauth-bundle": "^2.2",
|
||||||
"jangregor/phpstan-prophecy": "^2.0",
|
|
||||||
"lchrusciel/api-test-case": "^5.3",
|
"lchrusciel/api-test-case": "^5.3",
|
||||||
"matthiasnoback/symfony-config-test": "^6.0",
|
"matthiasnoback/symfony-config-test": "^6.0",
|
||||||
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
|
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
|
||||||
"mikey179/vfsstream": "^1.6",
|
|
||||||
"mockery/mockery": "^1.6",
|
|
||||||
"nyholm/psr7": "^1.8",
|
"nyholm/psr7": "^1.8",
|
||||||
"phparkitect/phparkitect": "^0.6",
|
"phparkitect/phparkitect": "^0.6",
|
||||||
"phpspec/prophecy-phpunit": "^2.2",
|
|
||||||
"phpstan/extension-installer": "^1.4",
|
"phpstan/extension-installer": "^1.4",
|
||||||
"phpstan/phpstan": "^2.0",
|
"phpstan/phpstan": "^2.0",
|
||||||
"phpstan/phpstan-doctrine": "^2.0",
|
"phpstan/phpstan-doctrine": "^2.0",
|
||||||
|
|
@ -226,12 +222,12 @@
|
||||||
"robertfausk/behat-panther-extension": "^1.1",
|
"robertfausk/behat-panther-extension": "^1.1",
|
||||||
"sylius-labs/coding-standard": "^4.4",
|
"sylius-labs/coding-standard": "^4.4",
|
||||||
"sylius-labs/suite-tags-extension": "~0.2",
|
"sylius-labs/suite-tags-extension": "~0.2",
|
||||||
"symfony/browser-kit": "^6.4 || ^7.2",
|
"symfony/browser-kit": "^6.4 || ^7.4",
|
||||||
"symfony/debug-bundle": "^6.4 || ^7.2",
|
"symfony/debug-bundle": "^6.4 || ^7.4",
|
||||||
"symfony/dotenv": "^6.4 || ^7.2",
|
"symfony/dotenv": "^6.4 || ^7.4",
|
||||||
"symfony/flex": "^2.4",
|
"symfony/flex": "^2.4",
|
||||||
"symfony/runtime": "^6.4 || ^7.2",
|
"symfony/runtime": "^6.4 || ^7.4",
|
||||||
"symfony/web-profiler-bundle": "^6.4 || ^7.2",
|
"symfony/web-profiler-bundle": "^6.4 || ^7.4",
|
||||||
"symplify/monorepo-builder": "^11.0"
|
"symplify/monorepo-builder": "^11.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
|
@ -263,10 +259,10 @@
|
||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "^7.2"
|
"require": "^7.4"
|
||||||
},
|
},
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "2.0-dev"
|
"dev-main": "2.2-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
|
||||||
|
|
@ -67,3 +67,13 @@ Feature: Editing a zone
|
||||||
And I save my changes
|
And I save my changes
|
||||||
Then I should be notified that it has been successfully edited
|
Then I should be notified that it has been successfully edited
|
||||||
And this zone name should be "EU"
|
And this zone name should be "EU"
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Setting zone to the highest priority
|
||||||
|
Given the store has a zone "European Union" with code "EU" and priority 0
|
||||||
|
And it has the "France" country member
|
||||||
|
When I want to modify the zone named "European Union"
|
||||||
|
And I set its priority to "5"
|
||||||
|
And I save my changes
|
||||||
|
Then I should be notified that it has been successfully edited
|
||||||
|
And the "European Union" zone should have priority 5
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
@managing_zones
|
||||||
|
Feature: Editing a zone
|
||||||
|
In order to change my my tax and shipping configuration
|
||||||
|
As an Administrator
|
||||||
|
I want to be able to edit a zone
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the store also has country "France"
|
||||||
|
And the store has a zone "North America" with code "NA" and priority 2
|
||||||
|
And the store has a zone "South America" with code "SA" and priority 1
|
||||||
|
And the store has a zone "Australia" with code "AU" and priority 0
|
||||||
|
And I am logged in as an administrator
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Zones are sorted by priority in descending order by default
|
||||||
|
When I want to see all zones in store
|
||||||
|
Then I should see 3 zones in the list
|
||||||
|
Then the first zone on the list should have name "North America"
|
||||||
|
Then the last zone on the list should have name "Australia"
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Zone's default priority is 0 which puts it at the bottom of the list
|
||||||
|
Given the store has a zone "European Union" with code "EU"
|
||||||
|
When I want to see all zones in store
|
||||||
|
Then I should see 4 zones in the list
|
||||||
|
And the last zone on the list should have name "European Union"
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
@managing_shipping_methods
|
||||||
|
Feature: Validating delivery time range on shipping method form
|
||||||
|
In order to avoid invalid delivery time configuration
|
||||||
|
As an Administrator
|
||||||
|
I want to be prevented from saving a shipping method when maximum delivery time is less than minimum
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the store operates on a channel named "Web-US" in "USD" currency
|
||||||
|
And the store is available in "English (United States)"
|
||||||
|
And the store has a zone "United States" with code "US"
|
||||||
|
And I am logged in as an administrator
|
||||||
|
|
||||||
|
@ui @mink:chromedriver
|
||||||
|
Scenario: Trying to add a shipping method with invalid delivery time range
|
||||||
|
When I want to create a new shipping method
|
||||||
|
And I specify its code as "DELIVERY_TIME_INVALID"
|
||||||
|
And I name it "Delivery Time Invalid" in "English (United States)"
|
||||||
|
And I define it for the zone named "United States"
|
||||||
|
And I choose "Flat rate per shipment" calculator
|
||||||
|
And I specify its amount as 10 for "Web-US" channel
|
||||||
|
And I fill in "Minimum delivery time (days)" with "5"
|
||||||
|
And I fill in "Maximum delivery time (days)" with "3"
|
||||||
|
And I try to add it
|
||||||
|
Then I should be notified that Maximum delivery time must be greater than or equal to the minimum.
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
@applying_taxes
|
||||||
|
Feature: Apply taxes based on zone priority
|
||||||
|
In order to pay correct taxes when a country belongs to multiple zones
|
||||||
|
As a Customer
|
||||||
|
I want the zone with higher priority to be used for tax calculation
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the store operates on a single channel
|
||||||
|
Given the store operates in "France" and "Belgium"
|
||||||
|
And there is a zone "The Rest of the World" containing all other countries
|
||||||
|
And the store has a zone "European Union" with code "EU" and priority 2
|
||||||
|
And it has the "France" country member
|
||||||
|
And it also has the "Belgium" country member
|
||||||
|
And default tax zone is "RoW"
|
||||||
|
And the store has "No tax" tax rate of 0% for "Clothes" for the rest of the world
|
||||||
|
And the store has "VAT" tax rate of 23% for "Clothes" within the "European Union" zone
|
||||||
|
And the store has a product "T-Shirt" priced at "$100.00"
|
||||||
|
And it belongs to "Clothes" tax category
|
||||||
|
And I am a logged in customer
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Applying tax from the higher priority zone
|
||||||
|
Given I added product "T-Shirt" to the cart
|
||||||
|
And I addressed the cart to "Belgium"
|
||||||
|
When I check the details of my cart
|
||||||
|
Then my cart total should be "$123.00"
|
||||||
|
And my cart taxes should be "$23.00"
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Applying tax from new zone zone when it has higher priority
|
||||||
|
Given the store has a zone "Benelux countries" with code "BC" and priority 3
|
||||||
|
And it also has the "Belgium" country member
|
||||||
|
And the store has "VAT" tax rate of 0% for "Clothes" within the "Benelux countries" zone
|
||||||
|
And I added product "T-Shirt" to the cart
|
||||||
|
And I addressed the cart to "Belgium"
|
||||||
|
When I check the details of my cart
|
||||||
|
Then my cart total should be "$100.00"
|
||||||
|
And there should be no taxes charged
|
||||||
|
|
||||||
|
@api @ui
|
||||||
|
Scenario: Applying tax from current zone when the new one has lower priority
|
||||||
|
Given the store has a zone "Benelux countries" with code "GU" and priority 1
|
||||||
|
And it also has the "Belgium" country member
|
||||||
|
And the store has "VAT" tax rate of 0% for "Clothes" within the "Benelux countries" zone
|
||||||
|
And I added product "T-Shirt" to the cart
|
||||||
|
And I addressed the cart to "Belgium"
|
||||||
|
When I check the details of my cart
|
||||||
|
Then my cart total should be "$123.00"
|
||||||
|
And my cart taxes should be "$23.00"
|
||||||
|
|
@ -21,9 +21,9 @@
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"symfony/dependency-injection": "^6.4.1 || ^7.2",
|
"symfony/dependency-injection": "^6.4.1 || ^7.4",
|
||||||
"symfony/http-kernel": "^6.4.1 || ^7.2",
|
"symfony/http-kernel": "^6.4.1 || ^7.4",
|
||||||
"symfony/workflow": "^6.4 || ^7.2"
|
"symfony/workflow": "^6.4 || ^7.4"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"twig/twig": "Access the state machine in your twig templates (^2.12|^3.3)",
|
"twig/twig": "Access the state machine in your twig templates (^2.12|^3.3)",
|
||||||
|
|
@ -37,10 +37,10 @@
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "2.0-dev"
|
"dev-main": "2.2-dev"
|
||||||
},
|
},
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"require": "^7.2"
|
"require": "^7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ interface ResponseCheckerInterface
|
||||||
|
|
||||||
public function hasViolationWithMessage(Response $response, string $message, ?string $property = null): bool;
|
public function hasViolationWithMessage(Response $response, string $message, ?string $property = null): bool;
|
||||||
|
|
||||||
|
public function appendError(Response $response): ResponseCheckerInterface;
|
||||||
|
|
||||||
public function cleanErrors(): void;
|
public function cleanErrors(): void;
|
||||||
|
|
||||||
/** @return array{
|
/** @return array{
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,14 @@ final readonly class ManagingZonesContext implements Context
|
||||||
$this->client->addRequestData('scope', $scope);
|
$this->client->addRequestData('scope', $scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When I set its priority to :priority
|
||||||
|
*/
|
||||||
|
public function iSetsItsPriorityTo(int $priority): void
|
||||||
|
{
|
||||||
|
$this->client->addRequestData('priority', $priority);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When I (try to) add it
|
* @When I (try to) add it
|
||||||
*/
|
*/
|
||||||
|
|
@ -496,6 +504,37 @@ final readonly class ManagingZonesContext implements Context
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then /^the (first|last) zone on the list should have ([^"]+) "([^"]+)"$/
|
||||||
|
*/
|
||||||
|
public function theFirstZoneOnTheListShouldHave(string $togglePosition, string $field, string $value): void
|
||||||
|
{
|
||||||
|
$items = $this->responseChecker->getValue($this->client->getLastResponse(), 'hydra:member');
|
||||||
|
if ('first' === $togglePosition) {
|
||||||
|
$item = reset($items);
|
||||||
|
} else {
|
||||||
|
$item = end($items);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::same($item[$field], $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then the :zone zone should have priority :priority
|
||||||
|
*/
|
||||||
|
public function theZoneShouldHavePriority(ZoneInterface $zone, int $priority): void
|
||||||
|
{
|
||||||
|
Assert::true(
|
||||||
|
$this->responseChecker->hasItemWithValues(
|
||||||
|
$this->client->index(Resources::ZONES),
|
||||||
|
[
|
||||||
|
'name' => $zone->getName(),
|
||||||
|
'priority' => $priority,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function removeZoneMember(CountryInterface|ProvinceInterface|ZoneInterface $objectToRemove): void
|
private function removeZoneMember(CountryInterface|ProvinceInterface|ZoneInterface $objectToRemove): void
|
||||||
{
|
{
|
||||||
$members = $this->client->getContent()['members'];
|
$members = $this->client->getContent()['members'];
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use Sylius\Behat\Context\Ui\Admin\Helper\SecurePasswordTrait;
|
||||||
use Sylius\Behat\Service\SharedStorageInterface;
|
use Sylius\Behat\Service\SharedStorageInterface;
|
||||||
use Sylius\Component\Core\Model\CustomerInterface;
|
use Sylius\Component\Core\Model\CustomerInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request as HttpRequest;
|
use Symfony\Component\HttpFoundation\Request as HttpRequest;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Webmozart\Assert\Assert;
|
use Webmozart\Assert\Assert;
|
||||||
|
|
||||||
final class RegistrationContext implements Context
|
final class RegistrationContext implements Context
|
||||||
|
|
@ -206,7 +207,7 @@ final class RegistrationContext implements Context
|
||||||
$content['hydra:description'],
|
$content['hydra:description'],
|
||||||
'Request does not have the following required fields specified: ' . implode(', ', $fields) . '.',
|
'Request does not have the following required fields specified: ' . implode(', ', $fields) . '.',
|
||||||
);
|
);
|
||||||
Assert::same($this->shopClient->getLastResponse()->getStatusCode(), 400);
|
Assert::same($this->shopClient->getLastResponse()->getStatusCode(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ final class InstallerContext implements Context
|
||||||
'locale' => 'en_US',
|
'locale' => 'en_US',
|
||||||
'e-mail' => 'test@email.com',
|
'e-mail' => 'test@email.com',
|
||||||
'username' => 'test',
|
'username' => 'test',
|
||||||
|
'firstName' => '',
|
||||||
|
'lastName' => '',
|
||||||
'password' => 'pswd',
|
'password' => 'pswd',
|
||||||
'confirmation' => 'pswd',
|
'confirmation' => 'pswd',
|
||||||
];
|
];
|
||||||
|
|
@ -146,6 +148,8 @@ final class InstallerContext implements Context
|
||||||
{
|
{
|
||||||
$this->inputChoices['e-mail'] = 'test@admin.com';
|
$this->inputChoices['e-mail'] = 'test@admin.com';
|
||||||
$this->inputChoices['username'] = 'test';
|
$this->inputChoices['username'] = 'test';
|
||||||
|
$this->inputChoices['firstName'] = 'John';
|
||||||
|
$this->inputChoices['lastName'] = 'Doe';
|
||||||
$this->inputChoices['password'] = 'pswd1$';
|
$this->inputChoices['password'] = 'pswd1$';
|
||||||
$this->inputChoices['confirmation'] = $this->inputChoices['password'];
|
$this->inputChoices['confirmation'] = $this->inputChoices['password'];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,14 @@ final readonly class ZoneContext implements Context
|
||||||
* @Given the store has (also) a zone :zoneName
|
* @Given the store has (also) a zone :zoneName
|
||||||
* @Given the store has a zone :zoneName with code :code
|
* @Given the store has a zone :zoneName with code :code
|
||||||
* @Given the store also has a zone :zoneName with code :code
|
* @Given the store also has a zone :zoneName with code :code
|
||||||
|
* @Given the store has a zone :zoneName with code :code and priority :priority
|
||||||
*/
|
*/
|
||||||
public function theStoreHasAZoneWithCode(string $zoneName, ?string $code = null): void
|
public function theStoreHasAZoneWithCode(string $zoneName, ?string $code = null, ?int $priority = 0): void
|
||||||
{
|
{
|
||||||
$this->saveZone($this->createZone($zoneName, $code, Scope::ALL), 'zone');
|
$zone = $this->createZone($zoneName, $code, Scope::ALL);
|
||||||
|
$zone->setPriority($priority);
|
||||||
|
|
||||||
|
$this->saveZone($zone, 'zone');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
||||||
namespace Sylius\Behat\Context\Ui\Admin;
|
namespace Sylius\Behat\Context\Ui\Admin;
|
||||||
|
|
||||||
use Behat\Behat\Context\Context;
|
use Behat\Behat\Context\Context;
|
||||||
|
use Behat\Step\When;
|
||||||
use Sylius\Behat\Element\Admin\ShippingMethod\FormElementInterface;
|
use Sylius\Behat\Element\Admin\ShippingMethod\FormElementInterface;
|
||||||
use Sylius\Behat\Page\Admin\ShippingMethod\CreatePageInterface;
|
use Sylius\Behat\Page\Admin\ShippingMethod\CreatePageInterface;
|
||||||
use Sylius\Behat\Page\Admin\ShippingMethod\IndexPageInterface;
|
use Sylius\Behat\Page\Admin\ShippingMethod\IndexPageInterface;
|
||||||
|
|
@ -124,6 +125,12 @@ final readonly class ManagingShippingMethodsContext implements Context
|
||||||
$this->shippingMethodFormElement->chooseCalculator($calculatorName);
|
$this->shippingMethodFormElement->chooseCalculator($calculatorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[When('I fill in :label with :value')]
|
||||||
|
public function iFillInWith(string $label, string $value): void
|
||||||
|
{
|
||||||
|
$this->shippingMethodFormElement->setField($label, $value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When I check (also) the :shippingMethodName shipping method
|
* @When I check (also) the :shippingMethodName shipping method
|
||||||
*/
|
*/
|
||||||
|
|
@ -625,4 +632,12 @@ final readonly class ManagingShippingMethodsContext implements Context
|
||||||
{
|
{
|
||||||
Assert::same($this->shippingMethodFormElement->getValidationMessage($element), $expectedMessage);
|
Assert::same($this->shippingMethodFormElement->getValidationMessage($element), $expectedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then I should be notified that Maximum delivery time must be greater than or equal to the minimum.
|
||||||
|
*/
|
||||||
|
public function iShouldBeNotifiedThatMaxDeliveryIsGreaterOrEqualMin(): void
|
||||||
|
{
|
||||||
|
$this->assertFieldValidationMessage('max_delivery_time_days', 'Maximum delivery time must be greater than or equal to the minimum.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,14 @@ final class ManagingZonesContext implements Context
|
||||||
$this->formElement->selectScope($scope);
|
$this->formElement->selectScope($scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When I set its priority to :priority
|
||||||
|
*/
|
||||||
|
public function iSetItsPriorityTo(int $priority): void
|
||||||
|
{
|
||||||
|
$this->formElement->prioritizeIt($priority);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When I add it
|
* @When I add it
|
||||||
* @When I try to add it
|
* @When I try to add it
|
||||||
|
|
@ -338,4 +346,44 @@ final class ManagingZonesContext implements Context
|
||||||
|
|
||||||
throw new \InvalidArgumentException(sprintf('Member "%s" should not be selectable.', $name));
|
throw new \InvalidArgumentException(sprintf('Member "%s" should not be selectable.', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then the first zone on the list should have :field :value
|
||||||
|
*/
|
||||||
|
public function theFirstZoneOnTheListShouldHave(string $field, string $value): void
|
||||||
|
{
|
||||||
|
$fields = $this->indexPage->getColumnFields($field);
|
||||||
|
$actualValue = reset($fields);
|
||||||
|
|
||||||
|
Assert::same(
|
||||||
|
$actualValue,
|
||||||
|
$value,
|
||||||
|
sprintf('Expected first zone\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then the last zone on the list should have :field :value
|
||||||
|
*/
|
||||||
|
public function theLastZoneOnTheListShouldHave(string $field, string $value): void
|
||||||
|
{
|
||||||
|
$fields = $this->indexPage->getColumnFields($field);
|
||||||
|
$actualValue = end($fields);
|
||||||
|
|
||||||
|
Assert::same(
|
||||||
|
$actualValue,
|
||||||
|
$value,
|
||||||
|
sprintf('Expected last zone\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given the :zone zone should have priority :priority
|
||||||
|
*/
|
||||||
|
public function theZoneShouldHavePriority(ZoneInterface $zone, int $priority)
|
||||||
|
{
|
||||||
|
$this->iWantToModifyAZoneNamed($zone);
|
||||||
|
|
||||||
|
Assert::same($this->formElement->getPriority(), $priority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,8 @@ class FormElement extends BaseFormElement implements FormElementInterface
|
||||||
'last_rule' => '[data-test-rules] [data-test-entry-row]:last-child',
|
'last_rule' => '[data-test-rules] [data-test-entry-row]:last-child',
|
||||||
'last_rule_amount' => '[data-test-rules] [data-test-entry-row]:last-child [id$="_configuration_%channelCode%_amount"]',
|
'last_rule_amount' => '[data-test-rules] [data-test-entry-row]:last-child [id$="_configuration_%channelCode%_amount"]',
|
||||||
'last_rule_weight' => '[data-test-rules] [data-test-entry-row]:last-child [id$="_configuration_weight"]',
|
'last_rule_weight' => '[data-test-rules] [data-test-entry-row]:last-child [id$="_configuration_weight"]',
|
||||||
|
'max_delivery_time_days' => '#sylius_admin_shipping_method_maxDeliveryTimeDays',
|
||||||
|
'min_delivery_time_days' => '#sylius_admin_shipping_method_minDeliveryTimeDays',
|
||||||
'name' => '#sylius_admin_shipping_method_translations_%localeCode%_name',
|
'name' => '#sylius_admin_shipping_method_translations_%localeCode%_name',
|
||||||
'position' => '#sylius_admin_shipping_method_position',
|
'position' => '#sylius_admin_shipping_method_position',
|
||||||
'zone' => '#sylius_admin_shipping_method_zone',
|
'zone' => '#sylius_admin_shipping_method_zone',
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class TreeElement extends SyliusElement implements TreeElementInterface
|
||||||
protected function getDefinedElements(): array
|
protected function getDefinedElements(): array
|
||||||
{
|
{
|
||||||
return array_merge(parent::getDefinedElements(), [
|
return array_merge(parent::getDefinedElements(), [
|
||||||
'confirm_delete_button' => '[data-test-tree-taxons] [data-test-tree-taxon="%name%"] [data-test-delete-modal] [data-test-confirm-button]',
|
'confirm_delete_button' => '.modal.show[data-test-delete-modal] [data-test-confirm-button]',
|
||||||
'first_tree_taxon' => '[data-test-tree-taxons] [data-test-tree-taxon]:first-child',
|
'first_tree_taxon' => '[data-test-tree-taxons] [data-test-tree-taxon]:first-child',
|
||||||
'last_tree_taxon' => '[data-test-tree-taxons] [data-test-tree-taxon]:last-child',
|
'last_tree_taxon' => '[data-test-tree-taxons] [data-test-tree-taxon]:last-child',
|
||||||
'tree_taxons' => '[data-test-tree-taxons]',
|
'tree_taxons' => '[data-test-tree-taxons]',
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@ class FormElement extends BaseFormElement implements FormElementInterface
|
||||||
return $this->getElement('name')->getValue();
|
return $this->getElement('name')->getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPriority(): int
|
||||||
|
{
|
||||||
|
return (int) $this->getElement('priority')->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
public function getType(): string
|
public function getType(): string
|
||||||
{
|
{
|
||||||
return $this->getElement('type')->getValue();
|
return $this->getElement('type')->getValue();
|
||||||
|
|
@ -67,6 +72,11 @@ class FormElement extends BaseFormElement implements FormElementInterface
|
||||||
$this->waitForElement(5, 'zone_member_added');
|
$this->waitForElement(5, 'zone_member_added');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function prioritizeIt(int $priority): void
|
||||||
|
{
|
||||||
|
$this->getElement('priority')->setValue($priority);
|
||||||
|
}
|
||||||
|
|
||||||
public function removeMember(string $member): void
|
public function removeMember(string $member): void
|
||||||
{
|
{
|
||||||
$this->getElement('zone_member_delete', ['%name%' => $member])->click();
|
$this->getElement('zone_member_delete', ['%name%' => $member])->click();
|
||||||
|
|
@ -100,6 +110,7 @@ class FormElement extends BaseFormElement implements FormElementInterface
|
||||||
'code' => '[data-test-code]',
|
'code' => '[data-test-code]',
|
||||||
'form_validation_message' => 'form > div.alert.alert-danger.d-block',
|
'form_validation_message' => 'form > div.alert.alert-danger.d-block',
|
||||||
'name' => '[data-test-name]',
|
'name' => '[data-test-name]',
|
||||||
|
'priority' => '#sylius_admin_zone_priority',
|
||||||
'scope' => '[data-test-scope]',
|
'scope' => '[data-test-scope]',
|
||||||
'type' => '[data-test-type]',
|
'type' => '[data-test-type]',
|
||||||
'zone_member' => '[data-test-zone-member]:contains("%name%")',
|
'zone_member' => '[data-test-zone-member]:contains("%name%")',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ interface FormElementInterface extends BaseFormElementInterface
|
||||||
{
|
{
|
||||||
public function getName(): string;
|
public function getName(): string;
|
||||||
|
|
||||||
|
public function getPriority(): int;
|
||||||
|
|
||||||
public function nameIt(string $name): void;
|
public function nameIt(string $name): void;
|
||||||
|
|
||||||
public function getType(): string;
|
public function getType(): string;
|
||||||
|
|
@ -31,6 +33,8 @@ interface FormElementInterface extends BaseFormElementInterface
|
||||||
|
|
||||||
public function addMember(): void;
|
public function addMember(): void;
|
||||||
|
|
||||||
|
public function prioritizeIt(int $priority): void;
|
||||||
|
|
||||||
public function getScope(): string;
|
public function getScope(): string;
|
||||||
|
|
||||||
public function selectScope(string $scope): void;
|
public function selectScope(string $scope): void;
|
||||||
|
|
|
||||||
|
|
@ -39,17 +39,15 @@ class IndexPage extends BaseIndexPage implements IndexPageInterface
|
||||||
public function archiveShippingMethod(string $name): void
|
public function archiveShippingMethod(string $name): void
|
||||||
{
|
{
|
||||||
$actions = $this->getActionsForResource(['name' => $name]);
|
$actions = $this->getActionsForResource(['name' => $name]);
|
||||||
$archiveRestoreModal = $actions->find('css', '[data-test-modal="archive-restore"]');
|
$actions->find('css', '[data-test-trigger-button="Archive"]')->press();
|
||||||
$archiveRestoreModal->find('css', '[data-test-trigger-button="Archive"]')->press();
|
$this->getDocument()->find('css', '.modal.show[data-test-modal="archive-restore"] [data-test-confirm-button]')->press();
|
||||||
$archiveRestoreModal->find('css', '[data-test-confirm-button]')->press();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restoreShippingMethod(string $name): void
|
public function restoreShippingMethod(string $name): void
|
||||||
{
|
{
|
||||||
$actions = $this->getActionsForResource(['name' => $name]);
|
$actions = $this->getActionsForResource(['name' => $name]);
|
||||||
$archiveRestoreModal = $actions->find('css', '[data-test-modal="archive-restore"]');
|
$actions->find('css', '[data-test-trigger-button="Restore"]')->press();
|
||||||
$archiveRestoreModal->find('css', '[data-test-trigger-button="Restore"]')->press();
|
$this->getDocument()->find('css', '.modal.show[data-test-modal="archive-restore"] [data-test-confirm-button]')->press();
|
||||||
$archiveRestoreModal->find('css', '[data-test-confirm-button]')->press();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isShippingMethodDisabled(ShippingMethodInterface $shippingMethod): bool
|
public function isShippingMethodDisabled(ShippingMethodInterface $shippingMethod): bool
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
|
||||||
use Sylius\Component\Addressing\Model\ZoneInterface;
|
use Sylius\Component\Addressing\Model\ZoneInterface;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
|
|
@ -46,6 +47,10 @@ final class ZoneType extends AbstractResourceType
|
||||||
->add('type', ZoneTypeChoiceType::class, [
|
->add('type', ZoneTypeChoiceType::class, [
|
||||||
'disabled' => true,
|
'disabled' => true,
|
||||||
])
|
])
|
||||||
|
->add('priority', IntegerType::class, [
|
||||||
|
'label' => 'sylius.form.zone.priority',
|
||||||
|
'required' => true,
|
||||||
|
])
|
||||||
;
|
;
|
||||||
|
|
||||||
if (!empty($this->scopeChoices)) {
|
if (!empty($this->scopeChoices)) {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class ZoneRepository extends EntityRepository implements ZoneRepositoryInterface
|
||||||
->andWhere($queryBuilder->expr()->eq('o.type', ':type'))
|
->andWhere($queryBuilder->expr()->eq('o.type', ':type'))
|
||||||
->setParameter('type', $type)
|
->setParameter('type', $type)
|
||||||
->setMaxResults(1)
|
->setMaxResults(1)
|
||||||
|
->addOrderBy('o.priority', 'DESC')
|
||||||
;
|
;
|
||||||
|
|
||||||
return $queryBuilder->getQuery()->getOneOrNullResult();
|
return $queryBuilder->getQuery()->getOneOrNullResult();
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,12 @@
|
||||||
<field name="type" column="type" type="string" length="8" />
|
<field name="type" column="type" type="string" length="8" />
|
||||||
<field name="scope" column="scope" type="string" nullable="true"/>
|
<field name="scope" column="scope" type="string" nullable="true"/>
|
||||||
|
|
||||||
|
<field name="priority" type="integer">
|
||||||
|
<options>
|
||||||
|
<option name="default">0</option>
|
||||||
|
</options>
|
||||||
|
</field>
|
||||||
|
|
||||||
<one-to-many field="members" target-entity="Sylius\Component\Addressing\Model\ZoneMemberInterface" mapped-by="belongsTo" orphan-removal="true">
|
<one-to-many field="members" target-entity="Sylius\Component\Addressing\Model\ZoneMemberInterface" mapped-by="belongsTo" orphan-removal="true">
|
||||||
<cascade>
|
<cascade>
|
||||||
<cascade-all/>
|
<cascade-all/>
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ sylius:
|
||||||
zone: Area
|
zone: Area
|
||||||
scope: Gebied
|
scope: Gebied
|
||||||
select: Kies
|
select: Kies
|
||||||
|
priority: Prioriteit
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Kies
|
select: Kies
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: الكل
|
all: الكل
|
||||||
select: اختر
|
select: اختر
|
||||||
select_scope: اختر النطاق
|
select_scope: اختر النطاق
|
||||||
|
priority: الأولوية
|
||||||
zone_member:
|
zone_member:
|
||||||
select: اختر
|
select: اختر
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Усе
|
all: Усе
|
||||||
select: Выберыце
|
select: Выберыце
|
||||||
select_scope: Выбярыце сферу ўжывання
|
select_scope: Выбярыце сферу ўжывання
|
||||||
|
priority: Прыярытэт
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Выберыце
|
select: Выберыце
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Всички
|
all: Всички
|
||||||
select: Изберете
|
select: Изберете
|
||||||
select_scope: Изберете обхват
|
select_scope: Изберете обхват
|
||||||
|
priority: Приоритет
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Изберете
|
select: Изберете
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Totes
|
all: Totes
|
||||||
select: Tria
|
select: Tria
|
||||||
select_scope: Seleccioneu l'àmbit
|
select_scope: Seleccioneu l'àmbit
|
||||||
|
priority: Prioritat
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Tria
|
select: Tria
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Všechny'
|
all: 'Všechny'
|
||||||
select: 'Vybrat'
|
select: 'Vybrat'
|
||||||
|
priority: 'Priorita'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Vybrat'
|
select: 'Vybrat'
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Alle
|
all: Alle
|
||||||
select: Vælg
|
select: Vælg
|
||||||
select_scope: Vælg omfang
|
select_scope: Vælg omfang
|
||||||
|
priority: Prioritet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Vælg
|
select: Vælg
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: 'Alle'
|
all: 'Alle'
|
||||||
select: 'Zone auswählen'
|
select: 'Zone auswählen'
|
||||||
select_scope: 'Geltungsbereich auswählen'
|
select_scope: 'Geltungsbereich auswählen'
|
||||||
|
priority: 'Priorität'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Mitglied auswählen'
|
select: 'Mitglied auswählen'
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Alle'
|
all: 'Alle'
|
||||||
select: 'Auswählen'
|
select: 'Auswählen'
|
||||||
|
priority: 'Priorität'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Auswählen'
|
select: 'Auswählen'
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Alle'
|
all: 'Alle'
|
||||||
select: 'Auswählen'
|
select: 'Auswählen'
|
||||||
|
priority: 'Priorität'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Auswählen'
|
select: 'Auswählen'
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Όλα
|
all: Όλα
|
||||||
select: Επιλέξτε
|
select: Επιλέξτε
|
||||||
select_scope: Επιλέξτε πεδίο
|
select_scope: Επιλέξτε πεδίο
|
||||||
|
priority: Προτεραιότητα
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Επιλέξτε
|
select: Επιλέξτε
|
||||||
|
|
|
||||||
|
|
@ -34,5 +34,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: All
|
all: All
|
||||||
select: Select
|
select: Select
|
||||||
|
priority: Priority
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Select
|
select: Select
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: All
|
all: All
|
||||||
select: Choose
|
select: Choose
|
||||||
|
priority: Priority
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Choose
|
select: Choose
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Todos
|
all: Todos
|
||||||
select: Seleccionar
|
select: Seleccionar
|
||||||
select_scope: Seleccione ámbito
|
select_scope: Seleccione ámbito
|
||||||
|
priority: Prioridad
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Selección
|
select: Selección
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Kõik
|
all: Kõik
|
||||||
select: Vali
|
select: Vali
|
||||||
select_scope: Valige
|
select_scope: Valige
|
||||||
|
priority: Prioriteet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Vali
|
select: Vali
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: همه
|
all: همه
|
||||||
select: انتخاب
|
select: انتخاب
|
||||||
select_scope: انتخاب محدوده
|
select_scope: انتخاب محدوده
|
||||||
|
priority: اولویت
|
||||||
zone_member:
|
zone_member:
|
||||||
select: انتخاب
|
select: انتخاب
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Kaikki
|
all: Kaikki
|
||||||
select: Valitse
|
select: Valitse
|
||||||
select_scope: Valitse alue
|
select_scope: Valitse alue
|
||||||
|
priority: Prioriteetti
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Valitse
|
select: Valitse
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: Tous
|
all: Tous
|
||||||
select: Sélectionner
|
select: Sélectionner
|
||||||
|
priority: Priorité
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Sélectionner
|
select: Sélectionner
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Tout
|
all: Tout
|
||||||
select: Sélectionner
|
select: Sélectionner
|
||||||
select_scope: Sélectionner la portée
|
select_scope: Sélectionner la portée
|
||||||
|
priority: Priorité
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Sélectionner
|
select: Sélectionner
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Tous
|
all: Tous
|
||||||
select: Choisir
|
select: Choisir
|
||||||
select_scope: Choisissez la portée
|
select_scope: Choisissez la portée
|
||||||
|
priority: Priorité
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Choisir
|
select: Choisir
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: הכל
|
all: הכל
|
||||||
select: בחירה
|
select: בחירה
|
||||||
select_scope: בחרו תחום
|
select_scope: בחרו תחום
|
||||||
|
priority: עדיפות
|
||||||
zone_member:
|
zone_member:
|
||||||
select: בחירה
|
select: בחירה
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'सभी'
|
all: 'सभी'
|
||||||
select: 'चयन करें'
|
select: 'चयन करें'
|
||||||
|
priority: 'प्राथमिकता'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'चयन करें'
|
select: 'चयन करें'
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Sve
|
all: Sve
|
||||||
select: Odaberite
|
select: Odaberite
|
||||||
select_scope: Odaberite doseg
|
select_scope: Odaberite doseg
|
||||||
|
priority: Prioritet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Odaberite
|
select: Odaberite
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Mind
|
all: Mind
|
||||||
select: Válassza ki
|
select: Válassza ki
|
||||||
select_scope: Hatókör kiválasztása
|
select_scope: Hatókör kiválasztása
|
||||||
|
priority: Prioritás
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Válassza ki
|
select: Válassza ki
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Semua
|
all: Semua
|
||||||
select: Pilih
|
select: Pilih
|
||||||
select_scope: Pilih lingkup
|
select_scope: Pilih lingkup
|
||||||
|
priority: Prioritas
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Pilih
|
select: Pilih
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Allt
|
all: Allt
|
||||||
select: Vallisti
|
select: Vallisti
|
||||||
select_scope: Velja umfang
|
select_scope: Velja umfang
|
||||||
|
priority: Forgangur
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Vallisti
|
select: Vallisti
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Tutti'
|
all: 'Tutti'
|
||||||
select: 'Seleziona'
|
select: 'Seleziona'
|
||||||
|
priority: 'Priorità'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Seleziona'
|
select: 'Seleziona'
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'すべて'
|
all: 'すべて'
|
||||||
select: '選択'
|
select: '選択'
|
||||||
|
priority: '優先度'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: '選択'
|
select: '選択'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: '전체'
|
all: '전체'
|
||||||
select: '선택'
|
select: '선택'
|
||||||
|
priority: '우선순위'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: '선택'
|
select: '선택'
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Visi
|
all: Visi
|
||||||
select: Pasirinkite
|
select: Pasirinkite
|
||||||
select_scope: Pasirinkite sritį
|
select_scope: Pasirinkite sritį
|
||||||
|
priority: Prioritetas
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Pasirinkite
|
select: Pasirinkite
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,4 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: Visas
|
all: Visas
|
||||||
select_scope: Izvēlēties tvērumu
|
select_scope: Izvēlēties tvērumu
|
||||||
|
priority: Prioritāte
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Сите
|
all: Сите
|
||||||
select: Изберете
|
select: Изберете
|
||||||
select_scope: Изберете опсег
|
select_scope: Изберете опсег
|
||||||
|
priority: Приоритет
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Изберете
|
select: Изберете
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Бүгд
|
all: Бүгд
|
||||||
select: Сонгох
|
select: Сонгох
|
||||||
select_scope: Бүс сонгох
|
select_scope: Бүс сонгох
|
||||||
|
priority: Эрэмбэ
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Сонгох
|
select: Сонгох
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Semua
|
all: Semua
|
||||||
select: Pilih
|
select: Pilih
|
||||||
select_scope: Pilih skop
|
select_scope: Pilih skop
|
||||||
|
priority: Keutamaan
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Pilih
|
select: Pilih
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Alle'
|
all: 'Alle'
|
||||||
select: 'Selecteer'
|
select: 'Selecteer'
|
||||||
|
priority: 'Prioriteit'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Selecteer'
|
select: 'Selecteer'
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Alle'
|
all: 'Alle'
|
||||||
select: 'Velg'
|
select: 'Velg'
|
||||||
|
priority: 'Prioritet'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Velg'
|
select: 'Velg'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: Wszystko
|
all: Wszystko
|
||||||
select: Wybierz
|
select: Wybierz
|
||||||
|
priority: Priorytet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Wybierz
|
select: Wybierz
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Todos'
|
all: 'Todos'
|
||||||
select: 'Selecionar'
|
select: 'Selecionar'
|
||||||
|
priority: 'Prioridade'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Selecionar'
|
select: 'Selecionar'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Todos(as)
|
all: Todos(as)
|
||||||
select: Selecione
|
select: Selecione
|
||||||
select_scope: Selecione o escopo
|
select_scope: Selecione o escopo
|
||||||
|
priority: Prioridade
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Selecione
|
select: Selecione
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Toate'
|
all: 'Toate'
|
||||||
select: 'Selectați'
|
select: 'Selectați'
|
||||||
|
priority: 'Prioritate'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Selectați'
|
select: 'Selectați'
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Все
|
all: Все
|
||||||
select: Выбрать
|
select: Выбрать
|
||||||
select_scope: Выберите область
|
select_scope: Выберите область
|
||||||
|
priority: Приоритет
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Выбрать
|
select: Выбрать
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: 'Všetko'
|
all: 'Všetko'
|
||||||
select: 'Vybrať'
|
select: 'Vybrať'
|
||||||
|
priority: 'Priorita'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 'Vybrať'
|
select: 'Vybrať'
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,6 @@ sylius:
|
||||||
scope: Področje
|
scope: Področje
|
||||||
select: Izberite
|
select: Izberite
|
||||||
select_scope: Izberite področje
|
select_scope: Izberite področje
|
||||||
|
priority: Prednost
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Izberite
|
select: Izberite
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
all: Të gjitha
|
all: Të gjitha
|
||||||
select: Zgjidh
|
select: Zgjidh
|
||||||
select_scope: Zgjidh zonën
|
select_scope: Zgjidh zonën
|
||||||
|
priority: Prioriteti
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Zgjidh
|
select: Zgjidh
|
||||||
|
|
|
||||||
|
|
@ -35,5 +35,6 @@ sylius:
|
||||||
scope: Обухват
|
scope: Обухват
|
||||||
select: Изаберите
|
select: Изаберите
|
||||||
select_scope: Изабери обухват
|
select_scope: Изабери обухват
|
||||||
|
priority: Приоритет
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Изаберите
|
select: Изаберите
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Sve
|
all: Sve
|
||||||
select: Izaberite
|
select: Izaberite
|
||||||
select_scope: Izaberite adresu isporuke
|
select_scope: Izaberite adresu isporuke
|
||||||
|
priority: Prioritet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Izaberite
|
select: Izaberite
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: Alla
|
all: Alla
|
||||||
select: Välj
|
select: Välj
|
||||||
|
priority: Prioritet
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Välj
|
select: Välj
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: ทั้งหมด
|
all: ทั้งหมด
|
||||||
select: เลือก
|
select: เลือก
|
||||||
select_scope: เลือกขอบเขต
|
select_scope: เลือกขอบเขต
|
||||||
|
priority: ลำดับความสำคัญ
|
||||||
zone_member:
|
zone_member:
|
||||||
select: เลือก
|
select: เลือก
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Tümü
|
all: Tümü
|
||||||
select: Seçiniz
|
select: Seçiniz
|
||||||
select_scope: Ölçek seçiniz
|
select_scope: Ölçek seçiniz
|
||||||
|
priority: Öncelik
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Seçiniz
|
select: Seçiniz
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Всі(-е)
|
all: Всі(-е)
|
||||||
select: Виберіть
|
select: Виберіть
|
||||||
select_scope: Виберіть покриття
|
select_scope: Виберіть покриття
|
||||||
|
priority: Пріоритет
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Виберіть
|
select: Виберіть
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@ sylius:
|
||||||
all: Tất cả
|
all: Tất cả
|
||||||
select: Chọn
|
select: Chọn
|
||||||
select_scope: Chọn phạm vi
|
select_scope: Chọn phạm vi
|
||||||
|
priority: Ưu tiên
|
||||||
zone_member:
|
zone_member:
|
||||||
select: Chọn
|
select: Chọn
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
scopes:
|
scopes:
|
||||||
all: '所有'
|
all: '所有'
|
||||||
select: '选择'
|
select: '选择'
|
||||||
|
priority: '优先级'
|
||||||
zone_member:
|
zone_member:
|
||||||
select: '选择'
|
select: '选择'
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ sylius:
|
||||||
all: 全部
|
all: 全部
|
||||||
select: 請選擇
|
select: 請選擇
|
||||||
select_scope: 選擇範圍
|
select_scope: 選擇範圍
|
||||||
|
priority: 優先順序
|
||||||
zone_member:
|
zone_member:
|
||||||
select: 請選擇
|
select: 請選擇
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@
|
||||||
"stof/doctrine-extensions-bundle": "^1.12",
|
"stof/doctrine-extensions-bundle": "^1.12",
|
||||||
"sylius/addressing": "^2.0",
|
"sylius/addressing": "^2.0",
|
||||||
"sylius/resource-bundle": "^1.12",
|
"sylius/resource-bundle": "^1.12",
|
||||||
"symfony/framework-bundle": "^6.4.1 || ^7.2",
|
"symfony/framework-bundle": "^6.4.1 || ^7.4",
|
||||||
"symfony/intl": "^6.4 || ^7.2"
|
"symfony/intl": "^6.4 || ^7.4"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"phpstan/phpdoc-parser": ">= 2.0"
|
"phpstan/phpdoc-parser": ">= 2.0"
|
||||||
|
|
@ -39,14 +39,13 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/data-fixtures": "^1.7",
|
"doctrine/data-fixtures": "^1.7",
|
||||||
"doctrine/doctrine-bundle": "^2.13",
|
"doctrine/doctrine-bundle": "^2.13",
|
||||||
"doctrine/orm": "^2.18 || ^3.3",
|
"doctrine/orm": "^2.18 || ^3.5",
|
||||||
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
|
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
|
||||||
"phpspec/prophecy-phpunit": "^2.2",
|
|
||||||
"phpunit/phpunit": "^11.5",
|
"phpunit/phpunit": "^11.5",
|
||||||
"symfony/browser-kit": "^6.4 || ^7.2",
|
"symfony/browser-kit": "^6.4 || ^7.4",
|
||||||
"symfony/dependency-injection": "^6.4.1 || ^7.2",
|
"symfony/dependency-injection": "^6.4.1 || ^7.4",
|
||||||
"symfony/form": "^6.4.1 || ^7.2",
|
"symfony/form": "^6.4.1 || ^7.4",
|
||||||
"symfony/validator": "^6.4 || ^7.2",
|
"symfony/validator": "^6.4 || ^7.4",
|
||||||
"theofidry/alice-data-fixtures": "^1.7"
|
"theofidry/alice-data-fixtures": "^1.7"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
@ -56,10 +55,10 @@
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "2.0-dev"
|
"dev-main": "2.2-dev"
|
||||||
},
|
},
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"require": "^7.2"
|
"require": "^7.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -15,9 +15,7 @@ namespace Tests\Sylius\Bundle\AddressingBundle\Form\Type;
|
||||||
|
|
||||||
use PHPUnit\Framework\Assert;
|
use PHPUnit\Framework\Assert;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
|
||||||
use Prophecy\Prophecy\ProphecyInterface;
|
|
||||||
use Sylius\Bundle\AddressingBundle\Form\Type\CountryChoiceType;
|
use Sylius\Bundle\AddressingBundle\Form\Type\CountryChoiceType;
|
||||||
use Sylius\Component\Addressing\Model\CountryInterface;
|
use Sylius\Component\Addressing\Model\CountryInterface;
|
||||||
use Sylius\Resource\Doctrine\Persistence\RepositoryInterface;
|
use Sylius\Resource\Doctrine\Persistence\RepositoryInterface;
|
||||||
|
|
@ -27,48 +25,37 @@ use Symfony\Component\Form\Test\TypeTestCase;
|
||||||
|
|
||||||
final class CountryChoiceTypeTest extends TypeTestCase
|
final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
{
|
{
|
||||||
use ProphecyTrait;
|
private MockObject&RepositoryInterface $countryRepository;
|
||||||
|
|
||||||
private ObjectProphecy $countryRepository;
|
private CountryInterface&MockObject $france;
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface */
|
private CountryInterface&MockObject $poland;
|
||||||
private $france;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface */
|
private CountryInterface&MockObject $austria;
|
||||||
private $poland;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface */
|
|
||||||
private $austria;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository = $this->prophesize(RepositoryInterface::class);
|
$this->countryRepository = $this->createMock(RepositoryInterface::class);
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface $france */
|
$this->france = $this->createMock(CountryInterface::class);
|
||||||
$france = $this->prophesize(CountryInterface::class);
|
$this->france->method('getCode')->willReturn('FR');
|
||||||
$france->getCode()->willReturn('FR');
|
$this->france->method('getName')->willReturn('France');
|
||||||
$france->getName()->willReturn('France');
|
|
||||||
$this->france = $france;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface $poland */
|
$this->poland = $this->createMock(CountryInterface::class);
|
||||||
$poland = $this->prophesize(CountryInterface::class);
|
$this->poland->method('getCode')->willReturn('PL');
|
||||||
$poland->getCode()->willReturn('PL');
|
$this->poland->method('getName')->willReturn('Poland');
|
||||||
$poland->getName()->willReturn('Poland');
|
|
||||||
$this->poland = $poland;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|CountryInterface $austria */
|
$this->austria = $this->createMock(CountryInterface::class);
|
||||||
$austria = $this->prophesize(CountryInterface::class);
|
$this->austria->method('getCode')->willReturn('AT');
|
||||||
$austria->getCode()->willReturn('AT');
|
$this->austria->method('getName')->willReturn('Austria');
|
||||||
$austria->getName()->willReturn('Austria');
|
$this->austria->method('isEnabled')->willReturn(false);
|
||||||
$austria->isEnabled()->willReturn(false);
|
|
||||||
$this->austria = $austria;
|
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getExtensions(): array
|
protected function getExtensions(): array
|
||||||
{
|
{
|
||||||
$type = new CountryChoiceType($this->countryRepository->reveal());
|
$type = new CountryChoiceType($this->countryRepository);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PreloadedExtension([$type], []),
|
new PreloadedExtension([$type], []),
|
||||||
|
|
@ -78,9 +65,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_only_enabled_countries_by_default(): void
|
public function it_returns_only_enabled_countries_by_default(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findBy(['enabled' => true])->willReturn([
|
$this->countryRepository
|
||||||
$this->france->reveal(),
|
->expects($this->once())
|
||||||
$this->poland->reveal(),
|
->method('findBy')
|
||||||
|
->with(['enabled' => true])
|
||||||
|
->willReturn([
|
||||||
|
$this->france,
|
||||||
|
$this->poland,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['France', 'Poland']);
|
$this->assertChoicesLabels(['France', 'Poland']);
|
||||||
|
|
@ -89,10 +80,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_all_countries_when_option_enabled_is_false(): void
|
public function it_returns_all_countries_when_option_enabled_is_false(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findAll()->willReturn([
|
$this->countryRepository
|
||||||
$this->france->reveal(),
|
->expects($this->once())
|
||||||
$this->poland->reveal(),
|
->method('findAll')
|
||||||
$this->austria->reveal(),
|
->willReturn([
|
||||||
|
$this->france,
|
||||||
|
$this->poland,
|
||||||
|
$this->austria,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['Austria', 'France', 'Poland'], ['enabled' => false]);
|
$this->assertChoicesLabels(['Austria', 'France', 'Poland'], ['enabled' => false]);
|
||||||
|
|
@ -101,9 +95,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_enabled_countries_in_an_alphabetical_order(): void
|
public function it_returns_enabled_countries_in_an_alphabetical_order(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findBy(['enabled' => true])->willReturn([
|
$this->countryRepository
|
||||||
$this->poland->reveal(),
|
->expects($this->once())
|
||||||
$this->france->reveal(),
|
->method('findBy')
|
||||||
|
->with(['enabled' => true])
|
||||||
|
->willReturn([
|
||||||
|
$this->poland,
|
||||||
|
$this->france,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['France', 'Poland']);
|
$this->assertChoicesLabels(['France', 'Poland']);
|
||||||
|
|
@ -112,10 +110,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_all_countries_in_an_alphabetical_order(): void
|
public function it_returns_all_countries_in_an_alphabetical_order(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findAll()->willReturn([
|
$this->countryRepository
|
||||||
$this->poland->reveal(),
|
->expects($this->once())
|
||||||
$this->france->reveal(),
|
->method('findAll')
|
||||||
$this->austria->reveal(),
|
->willReturn([
|
||||||
|
$this->poland,
|
||||||
|
$this->france,
|
||||||
|
$this->austria,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['Austria', 'France', 'Poland'], ['enabled' => false]);
|
$this->assertChoicesLabels(['Austria', 'France', 'Poland'], ['enabled' => false]);
|
||||||
|
|
@ -124,10 +125,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_all_filtered_out_countries(): void
|
public function it_returns_all_filtered_out_countries(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findAll()->willReturn([
|
$this->countryRepository
|
||||||
$this->france->reveal(),
|
->expects($this->once())
|
||||||
$this->poland->reveal(),
|
->method('findAll')
|
||||||
$this->austria->reveal(),
|
->willReturn([
|
||||||
|
$this->france,
|
||||||
|
$this->poland,
|
||||||
|
$this->austria,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['Poland'], ['choice_filter' => static fn (?CountryInterface $country): bool => $country !== null && $country->getName() === 'Poland', 'enabled' => false]);
|
$this->assertChoicesLabels(['Poland'], ['choice_filter' => static fn (?CountryInterface $country): bool => $country !== null && $country->getName() === 'Poland', 'enabled' => false]);
|
||||||
|
|
@ -136,9 +140,13 @@ final class CountryChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_enabled_filtered_out_countries(): void
|
public function it_returns_enabled_filtered_out_countries(): void
|
||||||
{
|
{
|
||||||
$this->countryRepository->findBy(['enabled' => true])->willReturn([
|
$this->countryRepository
|
||||||
$this->france->reveal(),
|
->expects($this->once())
|
||||||
$this->poland->reveal(),
|
->method('findBy')
|
||||||
|
->with(['enabled' => true])
|
||||||
|
->willReturn([
|
||||||
|
$this->france,
|
||||||
|
$this->poland,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['Poland'], ['choice_filter' => static fn (?CountryInterface $country): bool => $country !== null && $country->getName() === 'Poland']);
|
$this->assertChoicesLabels(['Poland'], ['choice_filter' => static fn (?CountryInterface $country): bool => $country !== null && $country->getName() === 'Poland']);
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ namespace Tests\Sylius\Bundle\AddressingBundle\Form\Type;
|
||||||
|
|
||||||
use PHPUnit\Framework\Assert;
|
use PHPUnit\Framework\Assert;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
|
||||||
use Prophecy\Prophecy\ProphecyInterface;
|
|
||||||
use Sylius\Bundle\AddressingBundle\Form\Type\ZoneChoiceType;
|
use Sylius\Bundle\AddressingBundle\Form\Type\ZoneChoiceType;
|
||||||
use Sylius\Component\Addressing\Model\Scope as AddressingScope;
|
use Sylius\Component\Addressing\Model\Scope as AddressingScope;
|
||||||
use Sylius\Component\Addressing\Model\ZoneInterface;
|
use Sylius\Component\Addressing\Model\ZoneInterface;
|
||||||
|
|
@ -28,39 +26,31 @@ use Symfony\Component\Form\Test\TypeTestCase;
|
||||||
|
|
||||||
final class ZoneChoiceTypeTest extends TypeTestCase
|
final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
{
|
{
|
||||||
use ProphecyTrait;
|
private MockObject&RepositoryInterface $zoneRepository;
|
||||||
|
|
||||||
private ObjectProphecy $zoneRepository;
|
private MockObject&ZoneInterface $zoneAllScopes;
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface */
|
private MockObject&ZoneInterface $zoneTaxScope;
|
||||||
private $zoneAllScopes;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface */
|
private MockObject&ZoneInterface $zoneShippingScope;
|
||||||
private $zoneTaxScope;
|
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface */
|
|
||||||
private $zoneShippingScope;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->zoneRepository = $this->prophesize(RepositoryInterface::class);
|
$this->zoneRepository = $this->createMock(RepositoryInterface::class);
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface $zoneAllScopes */
|
$zoneAllScopes = $this->createMock(ZoneInterface::class);
|
||||||
$zoneAllScopes = $this->prophesize(ZoneInterface::class);
|
$zoneAllScopes->method('getCode')->willReturn('all');
|
||||||
$zoneAllScopes->getCode()->willReturn('all');
|
$zoneAllScopes->method('getName')->willReturn('All');
|
||||||
$zoneAllScopes->getName()->willReturn('All');
|
|
||||||
$this->zoneAllScopes = $zoneAllScopes;
|
$this->zoneAllScopes = $zoneAllScopes;
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface $zoneTaxScope */
|
$zoneTaxScope = $this->createMock(ZoneInterface::class);
|
||||||
$zoneTaxScope = $this->prophesize(ZoneInterface::class);
|
$zoneTaxScope->method('getCode')->willReturn('tax');
|
||||||
$zoneTaxScope->getCode()->willReturn('tax');
|
$zoneTaxScope->method('getName')->willReturn('Tax');
|
||||||
$zoneTaxScope->getName()->willReturn('Tax');
|
|
||||||
$this->zoneTaxScope = $zoneTaxScope;
|
$this->zoneTaxScope = $zoneTaxScope;
|
||||||
|
|
||||||
/** @var ProphecyInterface|ZoneInterface $zoneShippingScope */
|
$zoneShippingScope = $this->createMock(ZoneInterface::class);
|
||||||
$zoneShippingScope = $this->prophesize(ZoneInterface::class);
|
$zoneShippingScope->method('getCode')->willReturn('shipping');
|
||||||
$zoneShippingScope->getCode()->willReturn('shipping');
|
$zoneShippingScope->method('getName')->willReturn('Shipping');
|
||||||
$zoneShippingScope->getName()->willReturn('Shipping');
|
|
||||||
$this->zoneShippingScope = $zoneShippingScope;
|
$this->zoneShippingScope = $zoneShippingScope;
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
@ -74,7 +64,7 @@ final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
'shipping' => 'Shipping',
|
'shipping' => 'Shipping',
|
||||||
];
|
];
|
||||||
|
|
||||||
$type = new ZoneChoiceType($this->zoneRepository->reveal(), $scopeTypes);
|
$type = new ZoneChoiceType($this->zoneRepository, $scopeTypes);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PreloadedExtension([$type], []),
|
new PreloadedExtension([$type], []),
|
||||||
|
|
@ -84,10 +74,10 @@ final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_all_scopes_by_default(): void
|
public function it_returns_all_scopes_by_default(): void
|
||||||
{
|
{
|
||||||
$this->zoneRepository->findBy([])->willReturn([
|
$this->zoneRepository->method('findBy')->with([])->willReturn([
|
||||||
$this->zoneAllScopes->reveal(),
|
$this->zoneAllScopes,
|
||||||
$this->zoneTaxScope->reveal(),
|
$this->zoneTaxScope,
|
||||||
$this->zoneShippingScope->reveal(),
|
$this->zoneShippingScope,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['All', 'Tax', 'Shipping']);
|
$this->assertChoicesLabels(['All', 'Tax', 'Shipping']);
|
||||||
|
|
@ -96,10 +86,10 @@ final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_all_scopes_when_zone_scope_set_to_all(): void
|
public function it_returns_all_scopes_when_zone_scope_set_to_all(): void
|
||||||
{
|
{
|
||||||
$this->zoneRepository->findBy([])->willReturn([
|
$this->zoneRepository->method('findBy')->with([])->willReturn([
|
||||||
$this->zoneAllScopes->reveal(),
|
$this->zoneAllScopes,
|
||||||
$this->zoneTaxScope->reveal(),
|
$this->zoneTaxScope,
|
||||||
$this->zoneShippingScope->reveal(),
|
$this->zoneShippingScope,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['All', 'Tax', 'Shipping'], ['zone_scope' => AddressingScope::ALL]);
|
$this->assertChoicesLabels(['All', 'Tax', 'Shipping'], ['zone_scope' => AddressingScope::ALL]);
|
||||||
|
|
@ -108,9 +98,9 @@ final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_tax_scopes_when_zone_scope_set_to_tax(): void
|
public function it_returns_tax_scopes_when_zone_scope_set_to_tax(): void
|
||||||
{
|
{
|
||||||
$this->zoneRepository->findBy(['scope' => ['tax', AddressingScope::ALL]])->willReturn([
|
$this->zoneRepository->method('findBy')->with(['scope' => ['tax', AddressingScope::ALL]])->willReturn([
|
||||||
$this->zoneAllScopes->reveal(),
|
$this->zoneAllScopes,
|
||||||
$this->zoneTaxScope->reveal(),
|
$this->zoneTaxScope,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['All', 'Tax'], ['zone_scope' => 'tax']);
|
$this->assertChoicesLabels(['All', 'Tax'], ['zone_scope' => 'tax']);
|
||||||
|
|
@ -119,9 +109,9 @@ final class ZoneChoiceTypeTest extends TypeTestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_shipping_scopes_when_zone_scope_set_to_shipping(): void
|
public function it_returns_shipping_scopes_when_zone_scope_set_to_shipping(): void
|
||||||
{
|
{
|
||||||
$this->zoneRepository->findBy(['scope' => ['shipping', AddressingScope::ALL]])->willReturn([
|
$this->zoneRepository->method('findBy')->with(['scope' => ['shipping', AddressingScope::ALL]])->willReturn([
|
||||||
$this->zoneAllScopes->reveal(),
|
$this->zoneAllScopes,
|
||||||
$this->zoneShippingScope->reveal(),
|
$this->zoneShippingScope,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertChoicesLabels(['All', 'Shipping'], ['zone_scope' => 'shipping']);
|
$this->assertChoicesLabels(['All', 'Shipping'], ['zone_scope' => 'shipping']);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ final class RemoveAvatarAction
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AvatarImageRepositoryInterface $avatarRepository,
|
private AvatarImageRepositoryInterface $avatarRepository,
|
||||||
private RouterInterface $router,
|
private RouterInterface $router,
|
||||||
private CsrfTokenManagerInterface $csrfTokenManager,
|
private ?CsrfTokenManagerInterface $csrfTokenManager,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ final class RemoveAvatarAction
|
||||||
{
|
{
|
||||||
$userId = $request->attributes->get('id', '');
|
$userId = $request->attributes->get('id', '');
|
||||||
|
|
||||||
if (!$this->csrfTokenManager->isTokenValid(
|
if ($this->csrfTokenManager && !$this->csrfTokenManager->isTokenValid(
|
||||||
new CsrfToken($userId, (string) $request->query->get('_csrf_token', '')),
|
new CsrfToken($userId, (string) $request->query->get('_csrf_token', '')),
|
||||||
)) {
|
)) {
|
||||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ final readonly class ResendOrderConfirmationEmailAction
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private OrderRepositoryInterface $orderRepository,
|
private OrderRepositoryInterface $orderRepository,
|
||||||
private ResendOrderConfirmationEmailDispatcherInterface $resendOrderConfirmationEmailDispatcher,
|
private ResendOrderConfirmationEmailDispatcherInterface $resendOrderConfirmationEmailDispatcher,
|
||||||
private CsrfTokenManagerInterface $csrfTokenManager,
|
private ?CsrfTokenManagerInterface $csrfTokenManager,
|
||||||
private RequestStack $requestStack,
|
private RequestStack $requestStack,
|
||||||
private RouterInterface $router,
|
private RouterInterface $router,
|
||||||
) {
|
) {
|
||||||
|
|
@ -42,7 +42,7 @@ final readonly class ResendOrderConfirmationEmailAction
|
||||||
{
|
{
|
||||||
$orderId = $request->attributes->get('id', '');
|
$orderId = $request->attributes->get('id', '');
|
||||||
|
|
||||||
if (!$this->csrfTokenManager->isTokenValid(
|
if ($this->csrfTokenManager && !$this->csrfTokenManager->isTokenValid(
|
||||||
new CsrfToken($orderId, (string) $request->query->get('_csrf_token', '')),
|
new CsrfToken($orderId, (string) $request->query->get('_csrf_token', '')),
|
||||||
)) {
|
)) {
|
||||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ final readonly class ResendShipmentConfirmationEmailAction
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ShipmentRepositoryInterface $shipmentRepository,
|
private ShipmentRepositoryInterface $shipmentRepository,
|
||||||
private ResendShipmentConfirmationEmailDispatcherInterface $resendShipmentConfirmationDispatcher,
|
private ResendShipmentConfirmationEmailDispatcherInterface $resendShipmentConfirmationDispatcher,
|
||||||
private CsrfTokenManagerInterface $csrfTokenManager,
|
private ?CsrfTokenManagerInterface $csrfTokenManager,
|
||||||
private RequestStack $requestStack,
|
private RequestStack $requestStack,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ final readonly class ResendShipmentConfirmationEmailAction
|
||||||
{
|
{
|
||||||
$shipmentId = $request->attributes->get('id', '');
|
$shipmentId = $request->attributes->get('id', '');
|
||||||
|
|
||||||
if (!$this->csrfTokenManager->isTokenValid(
|
if ($this->csrfTokenManager && !$this->csrfTokenManager->isTokenValid(
|
||||||
new CsrfToken($shipmentId, (string) $request->query->get('_csrf_token', '')),
|
new CsrfToken($shipmentId, (string) $request->query->get('_csrf_token', '')),
|
||||||
)) {
|
)) {
|
||||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?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\AdminBundle\Console\Command;
|
||||||
|
|
||||||
|
use Sylius\Component\Core\Model\AdminUserInterface;
|
||||||
|
use Sylius\Component\User\Repository\UserRepositoryInterface;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
#[AsCommand(
|
||||||
|
name: 'sylius:admin-user:delete',
|
||||||
|
description: 'Deletes an admin user account by the given email',
|
||||||
|
)]
|
||||||
|
final class DeleteAdminUserCommand extends Command
|
||||||
|
{
|
||||||
|
protected SymfonyStyle $io;
|
||||||
|
|
||||||
|
/** @param UserRepositoryInterface<AdminUserInterface> $userRepository */
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('Deletes an admin user')
|
||||||
|
->addArgument('email', InputArgument::REQUIRED, 'The email of the admin user to delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initialize(InputInterface $input, OutputInterface $output): void
|
||||||
|
{
|
||||||
|
$this->io = new SymfonyStyle($input, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$this->io->title('Delete admin user');
|
||||||
|
|
||||||
|
$email = $input->getArgument('email');
|
||||||
|
|
||||||
|
$adminUser = $this->userRepository->findOneBy(['email' => $email]);
|
||||||
|
if ($adminUser === null) {
|
||||||
|
$this->io->error(sprintf('Admin Account with the email "%s" does not exist', $email));
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->isInteractive() && !$this->io->confirm(
|
||||||
|
sprintf('Are you sure you want to delete the admin user "%s"?', $email),
|
||||||
|
false,
|
||||||
|
)) {
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->userRepository->remove($adminUser);
|
||||||
|
$this->io->success(sprintf('Admin Account with the email "%s" has been deleted successfully', $email));
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
<?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\AdminBundle\Console\Command;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Sylius\Component\Core\Model\AdminUserInterface;
|
||||||
|
use Sylius\Component\User\Repository\UserRepositoryInterface;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
#[AsCommand(
|
||||||
|
name: 'sylius:admin-user:list',
|
||||||
|
description: 'Displays a list of all admin users along with their details.',
|
||||||
|
)]
|
||||||
|
final class ListAdminUsersCommand extends Command
|
||||||
|
{
|
||||||
|
protected SymfonyStyle $io;
|
||||||
|
|
||||||
|
/** @param UserRepositoryInterface<AdminUserInterface> $adminUserRepository */
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserRepositoryInterface $adminUserRepository,
|
||||||
|
private readonly EntityManagerInterface $entityManager,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initialize(InputInterface $input, OutputInterface $output): void
|
||||||
|
{
|
||||||
|
$this->io = new SymfonyStyle($input, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->setAliases(['sylius:admin-users:list']);
|
||||||
|
$this->addOption(
|
||||||
|
'search',
|
||||||
|
's',
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
'Filter users based on a search term. If not provided, all users will be displayed.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$this->io->title('List available admin users');
|
||||||
|
|
||||||
|
$searchTerm = $input->getOption('search');
|
||||||
|
if ($searchTerm === null || $searchTerm === '') {
|
||||||
|
$this->renderUsersTable($output, $this->adminUserRepository->findAll());
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $this->searchUsers($searchTerm);
|
||||||
|
if (empty($users)) {
|
||||||
|
$this->io->warning(sprintf('No users found matching "%s"', $searchTerm));
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->success(sprintf('Found %d user(s) matching "%s"', count($users), $searchTerm));
|
||||||
|
$this->renderUsersTable($output, $users);
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<AdminUserInterface>
|
||||||
|
*/
|
||||||
|
private function searchUsers(string $searchTerm): array
|
||||||
|
{
|
||||||
|
$criteria = '%' . mb_strtolower($searchTerm) . '%';
|
||||||
|
|
||||||
|
return $this->entityManager->createQueryBuilder()
|
||||||
|
->from(AdminUserInterface::class, 'u')
|
||||||
|
->select('u')
|
||||||
|
->where('LOWER(u.email) LIKE :criteria
|
||||||
|
OR LOWER(u.username) LIKE :criteria
|
||||||
|
OR LOWER(u.firstName) LIKE :criteria
|
||||||
|
OR LOWER(u.lastName) LIKE :criteria')
|
||||||
|
->setParameter('criteria', $criteria)
|
||||||
|
->orderBy('u.createdAt', 'DESC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<AdminUserInterface> $adminUsers
|
||||||
|
*/
|
||||||
|
private function renderUsersTable(OutputInterface $output, array $adminUsers): void
|
||||||
|
{
|
||||||
|
$table = new Table($output);
|
||||||
|
$table->setHeaders([
|
||||||
|
'ID', 'E-Mail', 'Username', 'First name', 'Last name', 'Locale', 'Enabled', 'Roles',
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($adminUsers as $adminUser) {
|
||||||
|
$table->addRow([
|
||||||
|
$adminUser->getId(),
|
||||||
|
$adminUser->getEmail(),
|
||||||
|
$adminUser->getUsername(),
|
||||||
|
$adminUser->getFirstName() ?? '',
|
||||||
|
$adminUser->getLastName() ?? '',
|
||||||
|
$adminUser->getLocaleCode(),
|
||||||
|
$adminUser->isEnabled() ? '✔' : '✘',
|
||||||
|
$this->formatRoles($adminUser->getRoles()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string> $roles
|
||||||
|
*/
|
||||||
|
private function formatRoles(array $roles): string
|
||||||
|
{
|
||||||
|
if ($roles === []) {
|
||||||
|
return 'No roles';
|
||||||
|
}
|
||||||
|
|
||||||
|
$formatted = array_map(
|
||||||
|
static fn (string $role): string => str_replace('ROLE_', '', $role),
|
||||||
|
$roles,
|
||||||
|
);
|
||||||
|
|
||||||
|
return implode(",\n", $formatted);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue