├── .docker ├── nginx │ └── nginx.conf └── php │ └── php.ini ├── .editorconfig ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Makefile ├── README.md ├── behat.yml.dist ├── bin └── create_node_symlink.php ├── composer.json ├── docker-compose.yml ├── e2e-test-server.sh ├── easy-coding-standard.yml ├── ecs.php ├── etc └── build │ └── .gitignore ├── features ├── managing_shipping_methods_with_table_rate.feature ├── managing_table_rates.feature └── seeing_correct_shipping_fees_for_table_rate_based_method.feature ├── node_modules ├── phpspec.yml.dist ├── phpstan.neon ├── phpunit.xml.dist ├── psalm-baseline.xml ├── psalm.xml ├── roave-bc-check.yaml ├── spec ├── Calculator │ └── TableRateShippingCalculatorSpec.php ├── Checker │ └── TableRateShippingMethodEligibilityCheckerSpec.php ├── Entity │ └── ShippingTableRateSpec.php ├── EventSubscriber │ └── TableRateDeleteSubscriberSpec.php ├── Form │ └── EventSubscriber │ │ └── AddCurrencySubscriberSpec.php └── Resolver │ └── TableRateResolverSpec.php ├── src ├── Calculator │ └── TableRateShippingCalculator.php ├── Checker │ └── TableRateShippingMethodEligibilityChecker.php ├── DependencyInjection │ ├── Configuration.php │ └── WebgriffeSyliusTableRateShippingExtension.php ├── Entity │ └── ShippingTableRate.php ├── EventSubscriber │ └── TableRateDeleteSubscriber.php ├── Exception │ └── RateNotFoundException.php ├── Form │ ├── EventSubscriber │ │ └── AddCurrencySubscriber.php │ └── Type │ │ ├── Shipping │ │ └── Calculator │ │ │ ├── ChannelBasedTableRateConfigurationType.php │ │ │ └── TableRateConfigurationType.php │ │ ├── ShippingTableRateType.php │ │ └── WeightLimitToRateType.php ├── Menu │ └── AdminMenuListener.php ├── Resolver │ ├── TableRateResolver.php │ └── TableRateResolverInterface.php ├── Resources │ ├── config │ │ ├── admin_routing.yml │ │ ├── config.yml │ │ ├── doctrine │ │ │ └── ShippingTableRate.orm.yml │ │ ├── services.yml │ │ └── shop_routing.yml │ ├── translations │ │ ├── flashes.en.yml │ │ ├── flashes.fr.yml │ │ ├── messages.en.yml │ │ ├── messages.fr.yml │ │ ├── validators.en.yml │ │ └── validators.fr.yml │ └── views │ │ └── TableRateCrud │ │ ├── _form.html.twig │ │ ├── _weightLimitToRateTheme.html.twig │ │ ├── create.html.twig │ │ ├── index.html.twig │ │ └── update.html.twig └── WebgriffeSyliusTableRateShippingPlugin.php ├── symfony.lock └── tests ├── Application ├── .env ├── .env.test ├── .eslintrc.js ├── .gitignore ├── Kernel.php ├── assets │ ├── admin │ │ └── entry.js │ └── shop │ │ └── entry.js ├── bin │ └── console ├── composer.json ├── config │ ├── api_platform │ │ └── .gitignore │ ├── bootstrap.php │ ├── bundles.php │ ├── jwt │ │ ├── private.pem │ │ └── public.pem │ ├── packages │ │ ├── _sylius.yaml │ │ ├── api_platform.yaml │ │ ├── assets.yaml │ │ ├── dev │ │ │ ├── framework.yaml │ │ │ ├── jms_serializer.yaml │ │ │ ├── monolog.yaml │ │ │ ├── routing.yaml │ │ │ └── web_profiler.yaml │ │ ├── doctrine.yaml │ │ ├── doctrine_migrations.yaml │ │ ├── fos_rest.yaml │ │ ├── framework.yaml │ │ ├── jms_serializer.yaml │ │ ├── lexik_jwt_authentication.yaml │ │ ├── liip_imagine.yaml │ │ ├── mailer.yaml │ │ ├── prod │ │ │ ├── doctrine.yaml │ │ │ ├── jms_serializer.yaml │ │ │ └── monolog.yaml │ │ ├── routing.yaml │ │ ├── security.yaml │ │ ├── staging │ │ │ └── monolog.yaml │ │ ├── stof_doctrine_extensions.yaml │ │ ├── test │ │ │ ├── framework.yaml │ │ │ ├── mailer.yaml │ │ │ ├── monolog.yaml │ │ │ ├── security.yaml │ │ │ ├── sylius_theme.yaml │ │ │ ├── sylius_uploader.yaml │ │ │ └── web_profiler.yaml │ │ ├── test_cached │ │ │ ├── doctrine.yaml │ │ │ ├── fos_rest.yaml │ │ │ ├── framework.yaml │ │ │ ├── mailer.yaml │ │ │ ├── monolog.yaml │ │ │ ├── security.yaml │ │ │ ├── sylius_channel.yaml │ │ │ ├── sylius_theme.yaml │ │ │ ├── sylius_uploader.yaml │ │ │ └── twig.yaml │ │ ├── translation.yaml │ │ ├── twig.yaml │ │ ├── validator.yaml │ │ ├── webgriffe_sylius_table_rate_shipping_plugin.yaml │ │ └── webpack_encore.yaml │ ├── routes │ │ ├── dev │ │ │ └── web_profiler.yaml │ │ ├── liip_imagine.yaml │ │ ├── sylius_admin.yaml │ │ ├── sylius_api.yaml │ │ ├── sylius_shop.yaml │ │ ├── test │ │ │ ├── routing.yaml │ │ │ └── sylius_test_plugin.yaml │ │ ├── test_cached │ │ │ ├── routing.yaml │ │ │ └── sylius_test_plugin.yaml │ │ └── webgriffe_sylius_table_rate_shipping_plugin.yaml │ ├── secrets │ │ ├── dev │ │ │ └── .gitignore │ │ ├── prod │ │ │ └── .gitignore │ │ ├── test │ │ │ └── .gitignore │ │ └── test_cached │ │ │ └── .gitignore │ ├── serialization │ │ └── .gitignore │ ├── services.yaml │ ├── services_test.yaml │ └── services_test_cached.yaml ├── package.json ├── public │ ├── .htaccess │ ├── favicon.ico │ ├── index.php │ └── robots.txt ├── src │ └── Entity │ │ └── .gitignore ├── templates │ ├── .gitignore │ └── bundles │ │ ├── SyliusAdminBundle │ │ ├── Layout │ │ │ └── _logo.html.twig │ │ ├── Security │ │ │ └── _content.html.twig │ │ ├── _scripts.html.twig │ │ └── _styles.html.twig │ │ └── SyliusShopBundle │ │ ├── Homepage │ │ └── _banner.html.twig │ │ ├── Layout │ │ └── Header │ │ │ └── _logo.html.twig │ │ ├── _scripts.html.twig │ │ └── _styles.html.twig ├── translations │ └── .gitignore └── webpack.config.js └── Behat ├── Context ├── Setup │ ├── ProductContext.php │ └── ShippingTableRateContext.php ├── Transform │ └── NumberContext.php └── Ui │ ├── ManagingShippingMethodsWithTableRateContext.php │ ├── ManagingTableRatesContext.php │ └── ShippingTableRateContext.php ├── Page ├── ShippingMethod │ ├── UpdatePage.php │ └── UpdatePageInterface.php └── TableRate │ ├── CreatePage.php │ ├── CreatePageInterface.php │ ├── IndexPage.php │ ├── IndexPageInterface.php │ ├── UpdatePage.php │ └── UpdatePageInterface.php └── Resources ├── services.yml └── suites.yml /.docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | daemon off; 4 | pid /run/nginx.pid; 5 | 6 | include /etc/nginx/modules-enabled/*.conf; 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | server_tokens off; 17 | 18 | client_max_body_size 64m; 19 | sendfile on; 20 | tcp_nodelay on; 21 | tcp_nopush on; 22 | 23 | gzip_vary on; 24 | 25 | access_log /var/log/nginx/access.log; 26 | error_log /var/log/nginx/error.log; 27 | 28 | server { 29 | listen 80; 30 | 31 | root /app/tests/Application/public; 32 | index index.php; 33 | 34 | location / { 35 | try_files $uri /index.php$is_args$args; 36 | } 37 | 38 | location ~ \.php$ { 39 | include fastcgi_params; 40 | 41 | fastcgi_pass unix:/var/run/php8-fpm.sock; 42 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 43 | 44 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 45 | fastcgi_param DOCUMENT_ROOT $realpath_root; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.docker/php/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | memory_limit=512M 3 | 4 | [date] 5 | date.timezone=${PHP_DATE_TIMEZONE} 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | # Change these settings to your own preference 9 | indent_style = space 10 | indent_size = 4 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | 18 | [*.feature] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | [*.js] 23 | indent_style = space 24 | indent_size = 2 25 | 26 | [*.json] 27 | indent_style = space 28 | indent_size = 2 29 | 30 | [*.md] 31 | indent_style = space 32 | indent_size = 4 33 | trim_trailing_whitespace = false 34 | 35 | [*.neon] 36 | indent_style = space 37 | indent_size = 4 38 | 39 | [*.php] 40 | indent_style = space 41 | indent_size = 4 42 | 43 | [*.sh] 44 | indent_style = space 45 | indent_size = 4 46 | 47 | [*.{yaml,yml}] 48 | indent_style = space 49 | indent_size = 4 50 | trim_trailing_whitespace = false 51 | 52 | [.babelrc] 53 | indent_style = space 54 | indent_size = 2 55 | 56 | [.gitmodules] 57 | indent_style = tab 58 | indent_size = 4 59 | 60 | [.php_cs{,.dist}] 61 | indent_style = space 62 | indent_size = 4 63 | 64 | [composer.json] 65 | indent_style = space 66 | indent_size = 4 67 | 68 | [package.json] 69 | indent_style = space 70 | indent_size = 2 71 | 72 | [phpspec.yml{,.dist}] 73 | indent_style = space 74 | indent_size = 4 75 | 76 | [phpstan.neon] 77 | indent_style = space 78 | indent_size = 4 79 | 80 | [phpunit.xml{,.dist}] 81 | indent_style = space 82 | indent_size = 4 83 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - 'dependabot/**' 7 | pull_request: ~ 8 | release: 9 | types: [created] 10 | schedule: 11 | - 12 | cron: "0 1 * * 6" # Run at 1am every Saturday 13 | workflow_dispatch: ~ 14 | 15 | jobs: 16 | tests: 17 | runs-on: ubuntu-latest 18 | 19 | name: "Sylius ${{ matrix.sylius }}, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}" 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | php: [ "8.1", "8.2", "8.3" ] 25 | symfony: [ "5.4.*", "^6.4" ] 26 | sylius: ["1.13.3"] 27 | node: ["18.x"] 28 | mysql: ["8.0"] 29 | 30 | env: 31 | APP_ENV: test 32 | DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=${{ matrix.mysql }}" 33 | 34 | steps: 35 | - 36 | uses: actions/checkout@v2 37 | 38 | - 39 | name: Setup PHP 40 | uses: shivammathur/setup-php@v2 41 | with: 42 | php-version: "${{ matrix.php }}" 43 | extensions: intl 44 | tools: flex,symfony 45 | coverage: none 46 | 47 | - 48 | name: Setup Node 49 | uses: actions/setup-node@v4 50 | with: 51 | node-version: "${{ matrix.node }}" 52 | 53 | - 54 | name: Shutdown default MySQL 55 | run: sudo service mysql stop 56 | 57 | - 58 | name: Setup MySQL 59 | uses: mirromutth/mysql-action@v1.1 60 | with: 61 | mysql version: "${{ matrix.mysql }}" 62 | mysql root password: "root" 63 | 64 | - 65 | name: Output PHP version for Symfony CLI 66 | run: php -v | head -n 1 | awk '{ print $2 }' > .php-version 67 | 68 | - 69 | name: Install certificates 70 | run: symfony server:ca:install 71 | 72 | - 73 | name: Run Chrome Headless 74 | run: google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1 > /dev/null 2>&1 & 75 | 76 | - 77 | name: Run webserver 78 | run: (cd tests/Application && symfony server:start --port=8080 --dir=public --daemon) 79 | 80 | - 81 | name: Validate composer.json 82 | run: composer validate --ansi --strict 83 | 84 | - 85 | name: Get Composer cache directory 86 | id: composer-cache 87 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 88 | 89 | - 90 | name: Cache Composer 91 | uses: actions/cache@v4 92 | with: 93 | path: ${{ steps.composer-cache.outputs.dir }} 94 | key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json **/composer.lock') }} 95 | restore-keys: | 96 | ${{ runner.os }}-php-${{ matrix.php }}-composer- 97 | 98 | - 99 | name: Configure global composer 100 | run: | 101 | composer global config --no-plugins allow-plugins.symfony/flex true 102 | composer global require --no-progress --no-scripts --no-plugins "symfony/flex:^2.2.2" 103 | 104 | - 105 | name: Restrict Symfony version 106 | if: matrix.symfony != '' 107 | run: | 108 | composer config extra.symfony.require "${{ matrix.symfony }}" 109 | 110 | - 111 | name: Restrict Sylius version 112 | if: matrix.sylius != '' 113 | run: composer require "sylius/sylius:${{ matrix.sylius }}" --no-update --no-scripts --no-interaction 114 | 115 | - 116 | name: Install PHP dependencies 117 | run: composer install --no-interaction 118 | env: 119 | SYMFONY_REQUIRE: ${{ matrix.symfony }} 120 | 121 | - 122 | name: Get Yarn cache directory 123 | id: yarn-cache 124 | run: echo "::set-output name=dir::$(yarn cache dir)" 125 | 126 | - 127 | name: Cache Yarn 128 | uses: actions/cache@v4 129 | with: 130 | path: ${{ steps.yarn-cache.outputs.dir }} 131 | key: ${{ runner.os }}-node-${{ matrix.node }}-yarn-${{ hashFiles('**/package.json **/yarn.lock') }} 132 | restore-keys: | 133 | ${{ runner.os }}-node-${{ matrix.node }}-yarn- 134 | 135 | - 136 | name: Install JS dependencies 137 | run: (cd tests/Application && yarn install) 138 | 139 | - 140 | name: Prepare test application database 141 | run: | 142 | (cd tests/Application && bin/console doctrine:database:create -vvv) 143 | (cd tests/Application && bin/console doctrine:schema:create -vvv) 144 | 145 | - 146 | name: Prepare test application assets 147 | run: | 148 | (cd tests/Application && bin/console assets:install public -vvv) 149 | (cd tests/Application && yarn build:prod) 150 | 151 | - 152 | name: Prepare test application cache 153 | run: (cd tests/Application && bin/console cache:warmup -vvv) 154 | 155 | - 156 | name: Load fixtures in test application 157 | run: (cd tests/Application && bin/console sylius:fixtures:load -n) 158 | 159 | - 160 | name: Validate database schema 161 | run: (cd tests/Application && bin/console doctrine:schema:validate) 162 | 163 | - 164 | name: Run PHPStan 165 | run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/ 166 | 167 | - 168 | name: Run Psalm 169 | run: vendor/bin/psalm 170 | 171 | - 172 | name: Run PHPSpec 173 | run: vendor/bin/phpspec run --ansi -f progress --no-interaction 174 | 175 | - 176 | name: Run PHPUnit 177 | run: vendor/bin/phpunit --colors=always 178 | 179 | - 180 | name: Run Behat 181 | run: vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun 182 | 183 | - 184 | name: Upload Behat logs 185 | uses: actions/upload-artifact@v4 186 | if: failure() 187 | with: 188 | name: Behat logs 189 | path: etc/build/ 190 | if-no-files-found: ignore 191 | 192 | roave_bc_check: 193 | name: Roave BC Check 194 | runs-on: ubuntu-latest 195 | env: 196 | PHP_VERSION: 8.2 197 | steps: 198 | - name: Checkout code 199 | uses: actions/checkout@v3 200 | with: 201 | fetch-depth: 0 202 | 203 | - name: Setup PHP 204 | uses: shivammathur/setup-php@v2 205 | with: 206 | php-version: "${{ env.PHP_VERSION }}" 207 | extensions: intl 208 | tools: flex,symfony 209 | coverage: none 210 | 211 | - name: Install roave/backward-compatibility-check. 212 | run: composer require --dev roave/backward-compatibility-check --no-plugins 213 | 214 | - name: Run roave/backward-compatibility-check. 215 | run: vendor/bin/roave-backward-compatibility-check --format=github-actions 216 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /node_modules/ 3 | /composer.lock 4 | 5 | /etc/build/* 6 | !/etc/build/.gitignore 7 | 8 | /tests/Application/yarn.lock 9 | 10 | /.phpunit.result.cache 11 | /behat.yml 12 | /phpspec.yml 13 | /phpunit.xml 14 | 15 | # Symfony CLI https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project 16 | /.php-version 17 | /php.ini 18 | docker-compose.override.yml 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | phpunit: 2 | vendor/bin/phpunit 3 | 4 | phpspec: 5 | vendor/bin/phpspec run --ansi --no-interaction -f dot 6 | 7 | phpstan: 8 | vendor/bin/phpstan analyse 9 | 10 | psalm: 11 | vendor/bin/psalm 12 | 13 | behat-js: 14 | APP_ENV=test vendor/bin/behat --colors --strict --no-interaction -vvv -f progress 15 | 16 | install: 17 | composer install --no-interaction --no-scripts 18 | 19 | backend: 20 | tests/Application/bin/console sylius:install --no-interaction 21 | tests/Application/bin/console sylius:fixtures:load default --no-interaction 22 | 23 | frontend: 24 | (cd tests/Application && yarn install --pure-lockfile) 25 | (cd tests/Application && GULP_ENV=prod yarn build) 26 | 27 | behat: 28 | APP_ENV=test vendor/bin/behat --colors --strict --no-interaction -vvv -f progress 29 | 30 | init: install backend frontend 31 | 32 | ci: init phpstan psalm phpunit phpspec behat 33 | 34 | integration: init phpunit behat 35 | 36 | static: install phpspec phpstan psalm 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

Table Rate Shipping Plugin

8 | 9 |

10 |

This plugin allows to define shipping rates using weight tables.

11 |

Build Status

12 | 13 | ## Installation 14 | 15 | 1. Run `composer require --no-scripts webgriffe/sylius-table-rate-shipping-plugin`. 16 | 17 | 2. Add the plugin to the `config/bundles.php` file: 18 | 19 | ```php 20 | Webgriffe\SyliusTableRateShippingPlugin\WebgriffeSyliusTableRateShippingPlugin::class => ['all' => true], 21 | ``` 22 | 23 | 3. Add the plugin's config to by creating the file `config/packages/webgriffe_sylius_table_rate_shipping_plugin.yaml` with the following content: 24 | 25 | ```yaml 26 | imports: 27 | - { resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/config.yml" } 28 | ``` 29 | 30 | 4. Add the plugin's routing by creating the file `config/routes/webgriffe_sylius_table_rate_shipping_plugin.yaml` with the following content: 31 | 32 | ```yaml 33 | webgriffe_sylius_table_rate_shipping_plugin_shop: 34 | resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/shop_routing.yml" 35 | prefix: /{_locale} 36 | requirements: 37 | _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ 38 | 39 | webgriffe_sylius_table_rate_shipping_plugin_admin: 40 | resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/admin_routing.yml" 41 | prefix: /%sylius_admin.path_name% 42 | 43 | ``` 44 | 45 | 5. Finish the installation by updating the database schema and installing assets: 46 | 47 | ```bash 48 | bin/console cache:clear 49 | bin/console doctrine:migrations:diff 50 | bin/console doctrine:migrations:migrate 51 | bin/console assets:install 52 | bin/console sylius:theme:assets:install 53 | ``` 54 | 55 | ## Contributing 56 | 57 | To contribute you need to: 58 | 59 | 1. Clone this repository into you development environment and go to the plugin's root directory, 60 | 61 | 2. Then, from the plugin's root directory, run the following commands: 62 | 63 | ```bash 64 | composer install 65 | ``` 66 | 67 | 3. Copy `tests/Application/.env` in `tests/Application/.env.local` and set configuration specific for your development environment. 68 | 69 | 4. Then, from the plugin's root directory, run the following commands: 70 | 71 | ```bash 72 | (cd tests/Application && yarn install) 73 | (cd tests/Application && yarn build) 74 | (cd tests/Application && bin/console assets:install public) 75 | (cd tests/Application && bin/console doctrine:database:create) 76 | (cd tests/Application && bin/console doctrine:schema:create) 77 | (cd tests/Application && bin/console sylius:fixtures:load) 78 | (cd tests/Application && symfony server:start -d) # Requires Symfony CLI (https://symfony.com/download) 79 | ``` 80 | 81 | 5. Now at http://localhost:8080/ you have a full Sylius testing application which runs the plugin 82 | 83 | ### Testing 84 | 85 | After your changes you must ensure that the tests are still passing. 86 | 87 | First setup your test database: 88 | 89 | ```bash 90 | (cd tests/Application && bin/console -e test doctrine:database:create) 91 | (cd tests/Application && bin/console -e test doctrine:schema:create) 92 | ``` 93 | 94 | This plugin's test application already comes with a test configuration that uses SQLite as test database. 95 | If you don't want this you can create a `tests/Application/.env.test.local` with a different `DATABASE_URL`. 96 | 97 | The current CI suite runs the following tests: 98 | 99 | * Easy Coding Standard 100 | 101 | ```bash 102 | vendor/bin/ecs check src/ tests/Behat/ 103 | ``` 104 | 105 | * PHPStan 106 | 107 | ```bash 108 | vendor/bin/phpstan analyse -c phpstan.neon -l max src/ 109 | ``` 110 | 111 | * PHPUnit 112 | 113 | ```bash 114 | vendor/bin/phpunit 115 | ``` 116 | 117 | * PHPSpec 118 | 119 | ```bash 120 | vendor/bin/phpspec run 121 | ``` 122 | 123 | * Behat (without Javascript) 124 | 125 | ```bash 126 | vendor/bin/behat --tags="~@javascript" 127 | ``` 128 | 129 | * Behat (only Javascript) 130 | 131 | ```bash 132 | vendor/bin/behat --tags="@javascript" 133 | ``` 134 | 135 | To run them all with a single command run: 136 | 137 | ```bash 138 | composer suite 139 | ``` 140 | 141 | To run Behat's Javascript scenarios you need to setup Selenium and Chromedriver. Do the following: 142 | 143 | 1. Start Headless Chrome: 144 | 145 | ```bash 146 | google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1 147 | ``` 148 | 149 | 2. Install SSL certificates (only once needed) and run test application's webserver on `127.0.0.1:8080`: 150 | 151 | ```bash 152 | symfony server:ca:install 153 | APP_ENV=test symfony server:start --port=8080 --dir=tests/Application/public --daemon 154 | ``` 155 | 156 | License 157 | ------- 158 | This library is under the MIT license. See the complete license in the LICENSE file. 159 | 160 | Credits 161 | ------- 162 | Developed by [Webgriffe®](http://www.webgriffe.com/). 163 | -------------------------------------------------------------------------------- /behat.yml.dist: -------------------------------------------------------------------------------- 1 | imports: 2 | - vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml 3 | - tests/Behat/Resources/suites.yml 4 | 5 | default: 6 | formatters: 7 | pretty: 8 | verbose: true 9 | paths: false 10 | snippets: false 11 | 12 | extensions: 13 | DMore\ChromeExtension\Behat\ServiceContainer\ChromeExtension: ~ 14 | Robertfausk\Behat\PantherExtension: ~ 15 | 16 | FriendsOfBehat\MinkDebugExtension: 17 | directory: etc/build 18 | clean_start: false 19 | screenshot: true 20 | 21 | Behat\MinkExtension: 22 | files_path: "%paths.base%/vendor/sylius/sylius/src/Sylius/Behat/Resources/fixtures/" 23 | base_url: "https://127.0.0.1:8080/" 24 | default_session: symfony 25 | javascript_session: chromedriver 26 | sessions: 27 | symfony: 28 | symfony: ~ 29 | chromedriver: 30 | chrome: 31 | api_url: http://127.0.0.1:9222 32 | validate_certificate: false 33 | chrome_headless_second_session: 34 | chrome: 35 | api_url: http://127.0.0.1:9222 36 | validate_certificate: false 37 | panther: 38 | panther: 39 | manager_options: 40 | connection_timeout_in_ms: 5000 41 | request_timeout_in_ms: 120000 42 | chromedriver_arguments: 43 | - --log-path=etc/build/chromedriver.log 44 | - --verbose 45 | capabilities: 46 | acceptSslCerts: true 47 | acceptInsecureCerts: true 48 | unexpectedAlertBehaviour: accept 49 | show_auto: false 50 | 51 | FriendsOfBehat\SymfonyExtension: 52 | bootstrap: tests/Application/config/bootstrap.php 53 | kernel: 54 | class: Tests\Webgriffe\SyliusTableRateShippingPlugin\Application\Kernel 55 | 56 | FriendsOfBehat\VariadicExtension: ~ 57 | 58 | FriendsOfBehat\SuiteSettingsExtension: 59 | paths: 60 | - "features" 61 | 62 | SyliusLabs\SuiteTagsExtension: ~ 63 | -------------------------------------------------------------------------------- /bin/create_node_symlink.php: -------------------------------------------------------------------------------- 1 | `' . NODE_MODULES_FOLDER_NAME . '` already exists as a link or folder, keeping existing as may be intentional.' . PHP_EOL; 11 | exit(0); 12 | } else { 13 | echo '> Invalid symlink `' . NODE_MODULES_FOLDER_NAME . '` detected, recreating...' . PHP_EOL; 14 | if (!@unlink(NODE_MODULES_FOLDER_NAME)) { 15 | echo '> Could not delete file `' . NODE_MODULES_FOLDER_NAME . '`.' . PHP_EOL; 16 | exit(1); 17 | } 18 | } 19 | } 20 | 21 | /* try to create the symlink using PHP internals... */ 22 | $success = @symlink(PATH_TO_NODE_MODULES, NODE_MODULES_FOLDER_NAME); 23 | 24 | /* if case it has failed, but OS is Windows... */ 25 | if (!$success && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 26 | /* ...then try a different approach which does not require elevated permissions and folder to exist */ 27 | echo '> This system is running Windows, creation of links requires elevated privileges,' . PHP_EOL; 28 | echo '> and target path to exist. Fallback to NTFS Junction:' . PHP_EOL; 29 | exec(sprintf('mklink /J %s %s 2> NUL', NODE_MODULES_FOLDER_NAME, PATH_TO_NODE_MODULES), $output, $returnCode); 30 | $success = $returnCode === 0; 31 | if (!$success) { 32 | echo '> Failed o create the required symlink' . PHP_EOL; 33 | exit(2); 34 | } 35 | } 36 | 37 | $path = @readlink(NODE_MODULES_FOLDER_NAME); 38 | /* check if link points to the intended directory */ 39 | if ($path && realpath($path) === realpath(PATH_TO_NODE_MODULES)) { 40 | echo '> Successfully created the symlink.' . PHP_EOL; 41 | exit(0); 42 | } 43 | 44 | echo '> Failed to create the symlink to `' . NODE_MODULES_FOLDER_NAME . '`.' . PHP_EOL; 45 | exit(3); 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgriffe/sylius-table-rate-shipping-plugin", 3 | "type": "sylius-plugin", 4 | "description": "Provides table rate shipping calculator.", 5 | "keywords": [ 6 | "sylius", 7 | "sylius-plugin" 8 | ], 9 | "license": "MIT", 10 | "require": { 11 | "php": "^8.1", 12 | "sylius/sylius": "^1.13.3", 13 | "symfony/webpack-encore-bundle": "^1.15" 14 | }, 15 | "require-dev": { 16 | "behat/behat": "^3.6.1", 17 | "behat/mink-selenium2-driver": "^1.6", 18 | "dbrekelmans/bdi": "^1.1", 19 | "dmore/behat-chrome-extension": "^1.3", 20 | "dmore/chrome-mink-driver": "^2.7", 21 | "friends-of-behat/mink": "^1.8", 22 | "friends-of-behat/mink-browserkit-driver": "^1.4", 23 | "friends-of-behat/mink-debug-extension": "^2.0.0", 24 | "friends-of-behat/mink-extension": "^2.4", 25 | "friends-of-behat/page-object-extension": "^0.3", 26 | "friends-of-behat/suite-settings-extension": "^1.0", 27 | "friends-of-behat/symfony-extension": "^2.1", 28 | "friends-of-behat/variadic-extension": "^1.3", 29 | "phpspec/phpspec": "^7.2", 30 | "phpstan/extension-installer": "^1.0", 31 | "phpstan/phpstan": "^1.8.1", 32 | "phpstan/phpstan-doctrine": "1.3.16", 33 | "phpstan/phpstan-strict-rules": "^1.3.0", 34 | "phpstan/phpstan-webmozart-assert": "^1.2.0", 35 | "phpunit/phpunit": "^9.6 || ^10.5", 36 | "polishsymfonycommunity/symfony-mocker-container": "^1.0", 37 | "robertfausk/behat-panther-extension": "^1.1", 38 | "sylius-labs/coding-standard": "^4.2", 39 | "sylius-labs/suite-tags-extension": "^0.2", 40 | "symfony/browser-kit": "^5.4 || ^6.0", 41 | "symfony/debug-bundle": "^5.4 || ^6.0", 42 | "symfony/dotenv": "^5.4 || ^6.0", 43 | "symfony/flex": "^2.2.2", 44 | "symfony/intl": "^5.4 || ^6.0", 45 | "symfony/web-profiler-bundle": "^5.4 || ^6.0", 46 | "vimeo/psalm": "4.27.0" 47 | }, 48 | "config": { 49 | "sort-packages": true, 50 | "allow-plugins": { 51 | "dealerdirect/phpcodesniffer-composer-installer": false, 52 | "phpstan/extension-installer": true, 53 | "symfony/flex": true, 54 | "symfony/thanks": true 55 | } 56 | }, 57 | "extra": { 58 | "branch-alias": { 59 | "dev-master": "1.12-dev" 60 | } 61 | }, 62 | "autoload": { 63 | "psr-4": { 64 | "Webgriffe\\SyliusTableRateShippingPlugin\\": "src/", 65 | "Tests\\Webgriffe\\SyliusTableRateShippingPlugin\\": "tests/" 66 | } 67 | }, 68 | "autoload-dev": { 69 | "classmap": [ 70 | "tests/Application/Kernel.php" 71 | ] 72 | }, 73 | "scripts": { 74 | "post-install-cmd": [ 75 | "php bin/create_node_symlink.php" 76 | ], 77 | "post-update-cmd": [ 78 | "php bin/create_node_symlink.php" 79 | ], 80 | "post-create-project-cmd": [ 81 | "php bin/create_node_symlink.php" 82 | ], 83 | "ecs": "ecs check src/ tests/Behat/", 84 | "phpstan": "phpstan analyse -c phpstan.neon -l max src/", 85 | "phpunit": "phpunit", 86 | "phpspec": "phpspec run", 87 | "behat": "behat --strict -vvv --no-interaction || behat --strict -vvv --no-interaction --rerun", 88 | "suite": [ 89 | "@ecs", 90 | "@phpstan", 91 | "@phpunit", 92 | "@phpspec", 93 | "@behat" 94 | ], 95 | "auto-scripts": { 96 | "cache:clear": "symfony-cmd", 97 | "assets:install %PUBLIC_DIR%": "symfony-cmd", 98 | "security-checker security:check": "script" 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | image: sylius/standard:1.11-traditional-alpine 4 | environment: 5 | APP_ENV: "dev" 6 | DATABASE_URL: "mysql://root:mysql@mysql/sylius_%kernel.environment%?charset=utf8mb4" 7 | # DATABASE_URL: "pgsql://root:postgres@postgres/sylius_%kernel.environment%?charset=utf8" # When using postgres 8 | PHP_DATE_TIMEZONE: "Europe/Warsaw" 9 | volumes: 10 | - ./:/app:delegated 11 | - ./.docker/php/php.ini:/etc/php8/php.ini:delegated 12 | - ./.docker/nginx/nginx.conf:/etc/nginx/nginx.conf:delegated 13 | ports: 14 | - 80:80 15 | depends_on: 16 | - mysql 17 | networks: 18 | - sylius 19 | 20 | mysql: 21 | image: mysql:8.0 22 | platform: linux/amd64 23 | environment: 24 | MYSQL_ROOT_PASSWORD: mysql 25 | ports: 26 | - ${MYSQL_PORT:-3306}:3306 27 | networks: 28 | - sylius 29 | 30 | # postgres: 31 | # image: postgres:14-alpine 32 | # environment: 33 | # POSTGRES_USER: root 34 | # POSTGRES_PASSWORD: postgres 35 | # ports: 36 | # - ${POSTGRES_PORT:-5432}:5432 37 | # networks: 38 | # - sylius 39 | 40 | networks: 41 | sylius: 42 | driver: bridge 43 | -------------------------------------------------------------------------------- /e2e-test-server.sh: -------------------------------------------------------------------------------- 1 | # Make sure to set GOOGLE_CHROME env variable pointing to your Google Chrome binary. For example on macOS: 2 | # GOOGLE_CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ./e2e-test-server.sh 3 | 4 | trap 'kill %1; kill %2' SIGINT 5 | cd tests/Application && APP_ENV=test symfony server:start --port=8080 --dir=public & "${GOOGLE_CHROME}" --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --disable-gpu --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' --crash-dumps-dir=/tmp https://127.0.0.1 & tee 6 | -------------------------------------------------------------------------------- /easy-coding-standard.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: 'vendor/sylius-labs/coding-standard/easy-coding-standard.yml' } 3 | -------------------------------------------------------------------------------- /ecs.php: -------------------------------------------------------------------------------- 1 | paths([ 10 | __DIR__ . '/src', 11 | __DIR__ . '/tests/Behat', 12 | __DIR__ . '/ecs.php', 13 | ]); 14 | 15 | $ecsConfig->import('vendor/sylius-labs/coding-standard/ecs.php'); 16 | 17 | $ecsConfig->skip([ 18 | VisibilityRequiredFixer::class => ['*Spec.php'], 19 | ]); 20 | }; 21 | -------------------------------------------------------------------------------- /etc/build/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/etc/build/.gitignore -------------------------------------------------------------------------------- /features/managing_shipping_methods_with_table_rate.feature: -------------------------------------------------------------------------------- 1 | @managing_shipping_methods_with_table_rate 2 | Feature: Managing shipping methods with table rates 3 | In order to be able to effectively use already defined table rates 4 | As an Administrator 5 | I want to be able to assign table rates to shipping methods 6 | 7 | @ui @javascript 8 | Scenario: Assigning a table rate to a shipping method 9 | Given I am logged in as an administrator 10 | And the store operates on a single channel in "United States" 11 | And the store also operates on another channel named "Europe" in "EUR" currency 12 | And the store has a shipping table rate "United States Rates" for currency "USD" 13 | And the store has a shipping table rate "Europe Rates" for currency "EUR" 14 | When I want to create a new shipping method 15 | And I choose "Table rate" calculator 16 | Then I should be able to choose only the table rate "United States Rates" for the "United States" channel 17 | And I should be able to choose only the table rate "Europe Rates" for the "Europe" channel 18 | -------------------------------------------------------------------------------- /features/managing_table_rates.feature: -------------------------------------------------------------------------------- 1 | @managing_table_rates 2 | Feature: Managing table rates 3 | In order to define shipping table rates 4 | As an Administrator 5 | I want to browse, create, update and delete table rates 6 | 7 | Background: 8 | Given I am logged in as an administrator 9 | And the store operates on a single channel in "United States" 10 | 11 | @ui 12 | Scenario: Browsing an empty list of table rates 13 | When I am browsing the list of table rates 14 | Then I should see zero table rates in the list 15 | 16 | @ui 17 | Scenario: Browsing the list of table rates 18 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 19 | And the store has also a shipping table rate "West Coast Rates" for currency "USD" 20 | When I am browsing the list of table rates 21 | Then I should see 2 table rates in the list 22 | And I should see the "East Coast Rates" table rate in the list 23 | And I should see the "West Coast Rates" table rate in the list 24 | 25 | @ui @javascript 26 | Scenario: Adding a new table rate 27 | When I add a shipping table rate named "New Rates" for currency "USD" 28 | And I am browsing the list of table rates 29 | Then I should see 1 table rate in the list 30 | And I should see the "New Rates" table rate in the list 31 | 32 | @ui 33 | Scenario: Deleting a table rate 34 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 35 | When I am browsing the list of table rates 36 | Then I should see 1 table rate in the list 37 | When I delete the "East Coast Rates" table rate 38 | And I am browsing the list of table rates 39 | Then I should see zero table rates in the list 40 | 41 | @ui 42 | Scenario: Updating a table rate 43 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 44 | And this shipping table rate has a rate "$5.00" for shipments up to 1000 kg 45 | And I want to modify the "East Coast Rates" table rate 46 | When I change its name to "Edited Rates" 47 | And I save my changes 48 | Then I should be notified that it has been successfully edited 49 | And this shipping table rate name should be "Edited Rates" 50 | 51 | @ui @javascript 52 | Scenario: Adding weight rates into a table rate 53 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 54 | And I want to modify the "East Coast Rates" table rate 55 | When I add a new rate of "$5.00" for shipments up to 5 kg 56 | And I add a new rate of "$10.00" for shipments up to 20 kg 57 | And I save my changes 58 | Then I should be notified that it has been successfully edited 59 | And this shipping table rate should have 2 rates 60 | 61 | @ui @javascript 62 | Scenario: Validating a table rate 63 | When I try to add a new shipping table 64 | And I specify its code as "VALIDATE_ME" 65 | And I specify its currency as "USD" 66 | And I add a new rate of "$10.00" for shipments up to 20 kg 67 | But I do not specify its name 68 | And I try to add it 69 | Then I should be notified that name is required 70 | When I try to add a new shipping table 71 | And I specify its name as "Validate Me" 72 | And I specify its currency as "USD" 73 | And I add a new rate of "$10.00" for shipments up to 20 kg 74 | But I do not specify its code 75 | And I try to add it 76 | Then I should be notified that code is required 77 | When I try to add a new shipping table 78 | And I specify its code as "VALIDATE_ME" 79 | And I specify its name as "Validate Me" 80 | And I add a new rate of "$10.00" for shipments up to 20 kg 81 | But I do not specify its currency 82 | And I try to add it 83 | Then I should be notified that currency is required 84 | When I try to add a new shipping table 85 | And I specify its code as "VALIDATE_ME" 86 | And I specify its name as "Validate Me" 87 | And I specify its currency as "USD" 88 | But I do not specify any rate 89 | And I try to add it 90 | Then I should be notified that at least one rate is required 91 | 92 | @ui 93 | Scenario: Trying to change table rate code (even if its field is disabled) 94 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 95 | And this shipping table rate has a rate "$5.00" for shipments up to 1000 kg 96 | And I want to modify the "East Coast Rates" table rate 97 | Then the code field should be disabled 98 | When I change its code to "ANOTHER_CODE" 99 | And I save my changes 100 | Then the "East Coast Rates" table rate should still have code "EAST_COAST_RATES" 101 | 102 | @ui 103 | Scenario: Trying to change table rate currency (even if its field is disabled) 104 | Given the store also operates on another channel named "Europe" in "EUR" currency 105 | And the store has a shipping table rate "East Coast Rates" for currency "USD" 106 | And this shipping table rate has a rate "$5.00" for shipments up to 1000 kg 107 | And I want to modify the "East Coast Rates" table rate 108 | Then the currency field should be disabled 109 | When I change its currency to "EUR" 110 | And I save my changes 111 | Then the "East Coast Rates" table rate should still have "USD" currency 112 | 113 | @ui @javascript 114 | Scenario: Creating two table rates with the same code 115 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 116 | When I add a shipping table rate named "East Coast Rates" for currency "USD" 117 | Then I should be notified that code has to be unique 118 | 119 | @ui 120 | Scenario: Deleting a table rate which is already in use 121 | Given the store has a shipping table rate "East Coast Rates" for currency "USD" 122 | And the store has "East Coast Shipping" shipping method using "East Coast Rates" table rate for "United States" channel 123 | When I am browsing the list of table rates 124 | Then I should see 1 table rate in the list 125 | When I delete the "East Coast Rates" table rate 126 | Then I should be notified that the table rate couldn't be deleted because is already used by the "East Coast Shipping" shipping method 127 | And the "East Coast Rates" shipping table rate should still be there 128 | -------------------------------------------------------------------------------- /features/seeing_correct_shipping_fees_for_table_rate_based_method.feature: -------------------------------------------------------------------------------- 1 | @table_rate_shipping_fee 2 | Feature: Seeing correct shipping fees for table rate based shipping methods 3 | In order to get a proper margin on shipping cost 4 | As a Store Owner 5 | I want to charge different fees for different shipment weights 6 | 7 | Background: 8 | Given the store operates on a single channel in "United States" 9 | And the store has a product "Bottle of water" which weights 1 kg 10 | And the store has a shipping table rate "Weight-based" for currency "USD" 11 | And it has a rate "$5.00" for shipments up to 5 kg 12 | And it has a rate "$10.00" for shipments up to 20 kg 13 | And the store has "Basic" shipping method using "Weight-based" table rate for "United States" channel 14 | And I am a logged in customer 15 | 16 | @ui 17 | Scenario: Seeing correct shipping fee for the light shipment 18 | When I add product "Bottle of water" to the cart 19 | Then my cart shipping total should be "$5.00" 20 | 21 | @ui 22 | Scenario: Seeing correct shipping fee for the exact upper limit of the lighter shipment 23 | When I add 5 products "Bottle of water" to the cart 24 | Then my cart shipping total should be "$5.00" 25 | 26 | @ui 27 | Scenario: Seeing correct shipping fee for the heavier shipment 28 | When I add 15 products "Bottle of water" to the cart 29 | Then my cart shipping total should be "$10.00" 30 | 31 | @ui 32 | Scenario: Seeing no shipping methods for too heavy shipment 33 | When I add 25 products "Bottle of water" to the cart 34 | Then I should not see shipping total for my cart 35 | And I should have no shipping methods available to choose from 36 | 37 | @ui 38 | Scenario: Seeing no shipping methods for too heavy shipment when increasing product quantity from the shopping cart 39 | When I add product "Bottle of water" to the cart 40 | Then my cart shipping total should be "$5.00" 41 | When I change "Bottle of water" quantity to "25" 42 | Then my cart shipping total should be "$0.00" 43 | And I should have no shipping methods available to choose from 44 | -------------------------------------------------------------------------------- /node_modules: -------------------------------------------------------------------------------- 1 | tests/Application/node_modules -------------------------------------------------------------------------------- /phpspec.yml.dist: -------------------------------------------------------------------------------- 1 | suites: 2 | main: 3 | namespace: Webgriffe\SyliusTableRateShippingPlugin 4 | psr4_prefix: Webgriffe\SyliusTableRateShippingPlugin 5 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | reportUnmatchedIgnoredErrors: false 4 | paths: 5 | - src 6 | - tests/Behat 7 | 8 | excludePaths: 9 | # Makes PHPStan crash 10 | - 'src/DependencyInjection/Configuration.php' 11 | 12 | # Test dependencies 13 | - 'tests/Application/app/**.php' 14 | - 'tests/Application/src/**.php' 15 | 16 | ignoreErrors: 17 | - 18 | identifier: missingType.generics 19 | - 20 | identifier: missingType.iterableValue 21 | - '/Parameter #1 \$configuration of method Symfony\\Component\\DependencyInjection\\Extension\\Extension::processConfiguration\(\) expects Symfony\\Component\\Config\\Definition\\ConfigurationInterface, Symfony\\Component\\Config\\Definition\\ConfigurationInterface\|null given\./' 22 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | tests 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /psalm-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $shipment 6 | 7 | 8 | 9 | 10 | ShippingMethodEligibilityCheckerInterface 11 | ShippingMethodEligibilityCheckerInterface 12 | 13 | 14 | TableRateShippingMethodEligibilityChecker 15 | 16 | 17 | $method 18 | $subject 19 | 20 | 21 | 22 | 23 | $config 24 | 25 | 26 | $this->getConfiguration([], $container) 27 | 28 | 29 | 30 | 31 | 32 | $array['rate'] 33 | $array['weightLimit'] 34 | 35 | 36 | $array 37 | 38 | 39 | int 40 | 41 | 42 | $array['rate'] 43 | 44 | 45 | 46 | 47 | $channelConfiguration['table_rate'] 48 | 49 | 50 | $channelConfiguration 51 | 52 | 53 | 54 | 55 | $shippingTableRate->getCode() 56 | 57 | 58 | 59 | 60 | $resource 61 | 62 | 63 | $resource 64 | 65 | 66 | 67 | 68 | $currency 69 | 70 | 71 | 72 | 73 | $calculatorConfig[$channelCode][TableRateConfigurationType::TABLE_RATE_FIELD_NAME] 74 | 75 | 76 | $channel->getName() 77 | $channel->getName() 78 | $shippingMethod->getName() 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /roave-bc-check.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - '#\[BC\] SKIPPED: Roave\\BetterReflection\\Reflection\\ReflectionClass ".*?" could not be found in the located source#' 4 | -------------------------------------------------------------------------------- /spec/Calculator/TableRateShippingCalculatorSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($tableRateResolver); 21 | } 22 | 23 | function it_calculates_the_rate_based_on_the_table_rate( 24 | ShipmentInterface $shipment, 25 | TableRateResolverInterface $tableRateResolver, 26 | ShippingTableRate $tableRate 27 | ): void { 28 | $configuration = ['CHANNEL_CODE' => ['table_rate' => $tableRate]]; 29 | $shipment->getShippingWeight()->willReturn(15.5); 30 | $tableRateResolver->resolve($shipment, $configuration)->willReturn($tableRate); 31 | $tableRate->getRate(15.5)->willReturn(1000); 32 | 33 | $this 34 | ->calculate($shipment, $configuration) 35 | ->shouldReturn(1000) 36 | ; 37 | } 38 | 39 | function it_should_return_zero_if_no_rate_is_found_for_a_given_shipment( 40 | ShipmentInterface $shipment, 41 | TableRateResolverInterface $tableRateResolver, 42 | ShippingTableRate $tableRate 43 | ) { 44 | $configuration = ['CHANNEL_CODE' => ['table_rate' => $tableRate]]; 45 | $shipment->getShippingWeight()->willReturn(1000); 46 | $tableRateResolver->resolve($shipment, $configuration)->willReturn($tableRate); 47 | $tableRate->getRate(1000)->willThrow(RateNotFoundException::class); 48 | 49 | $this 50 | ->calculate($shipment, $configuration) 51 | ->shouldReturn(0) 52 | ; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /spec/Checker/TableRateShippingMethodEligibilityCheckerSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($eligibilityChecker, $tableRateResolver); 22 | } 23 | 24 | function it_returns_false_if_the_decorated_checker_returns_false( 25 | ShippingMethodEligibilityCheckerInterface $eligibilityChecker, 26 | ShipmentInterface $shipment, 27 | ShippingMethodInterface $shippingMethod 28 | ): void { 29 | $eligibilityChecker->isEligible($shipment, $shippingMethod)->willReturn(false); 30 | 31 | $this->isEligible($shipment, $shippingMethod)->shouldReturn(false); 32 | } 33 | 34 | function it_returns_true_if_the_decorated_checker_returns_true_and_other_calculator_is_used( 35 | ShippingMethodEligibilityCheckerInterface $eligibilityChecker, 36 | ShipmentInterface $shipment, 37 | ShippingMethodInterface $shippingMethod 38 | ): void { 39 | $eligibilityChecker->isEligible($shipment, $shippingMethod)->willReturn(true); 40 | 41 | $shippingMethod->getCalculator()->willReturn('ananas'); 42 | 43 | $this->isEligible($shipment, $shippingMethod)->shouldReturn(true); 44 | } 45 | 46 | function it_returns_true_if_the_decorated_checker_returns_true_and_a_rate_can_be_found( 47 | ShippingMethodEligibilityCheckerInterface $eligibilityChecker, 48 | ShipmentInterface $shipment, 49 | ShippingMethodInterface $shippingMethod, 50 | TableRateResolverInterface $tableRateResolver, 51 | ShippingTableRate $tableRate 52 | ): void { 53 | $eligibilityChecker->isEligible($shipment, $shippingMethod)->willReturn(true); 54 | $shippingMethod->getCalculator()->willReturn(TableRateShippingCalculator::TYPE); 55 | $shippingMethod->getConfiguration()->willReturn(['some' => 'config']); 56 | $shipment->getShippingWeight()->willReturn(15.5); 57 | $tableRateResolver->resolve($shipment, ['some' => 'config'])->willReturn($tableRate); 58 | $tableRate->getRate(15.5)->willReturn(1000); 59 | 60 | $this->isEligible($shipment, $shippingMethod)->shouldReturn(true); 61 | } 62 | 63 | function it_returns_false_if_the_decorated_checker_returns_true_and_a_rate_cannot_be_found( 64 | ShippingMethodEligibilityCheckerInterface $eligibilityChecker, 65 | ShipmentInterface $shipment, 66 | ShippingMethodInterface $shippingMethod, 67 | TableRateResolverInterface $tableRateResolver, 68 | ShippingTableRate $tableRate 69 | ): void { 70 | $eligibilityChecker->isEligible($shipment, $shippingMethod)->willReturn(true); 71 | $shippingMethod->getCalculator()->willReturn(TableRateShippingCalculator::TYPE); 72 | $shippingMethod->getConfiguration()->willReturn(['some' => 'config']); 73 | $shipment->getShippingWeight()->willReturn(15.5); 74 | $tableRateResolver->resolve($shipment, ['some' => 'config'])->willReturn($tableRate); 75 | $tableRate->getRate(15.5)->willThrow(RateNotFoundException::class); 76 | 77 | $this->isEligible($shipment, $shippingMethod)->shouldReturn(false); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /spec/Entity/ShippingTableRateSpec.php: -------------------------------------------------------------------------------- 1 | addRate(20.0, 10); 17 | $this->addRate(5.0, 5); 18 | $this->addRate(10.0, 7); 19 | 20 | $this->getRate(2.0)->shouldReturn(5); 21 | $this->getRate(5.0)->shouldReturn(5); 22 | $this->getRate(7.0)->shouldReturn(7); 23 | $this->getRate(10.0)->shouldReturn(7); 24 | $this->getRate(15.0)->shouldReturn(10); 25 | $this->getRate(20.0)->shouldReturn(10); 26 | $this->shouldThrow(RateNotFoundException::class)->during('getRate', [25.0]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spec/EventSubscriber/TableRateDeleteSubscriberSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($shippingMethodRepository); 23 | } 24 | 25 | public function it_is_initializable(): void 26 | { 27 | $this->shouldHaveType(TableRateDeleteSubscriber::class); 28 | } 29 | 30 | public function it_should_implement_event_subscriber_interface(): void 31 | { 32 | $this->shouldImplement(EventSubscriberInterface::class); 33 | } 34 | 35 | public function it_should_subscribe_to_table_rate_pre_delete_event(): void 36 | { 37 | self::getSubscribedEvents()->shouldReturn( 38 | ['webgriffe.shipping_table_rate.pre_delete' => 'onTableRatePreDelete'] 39 | ); 40 | } 41 | 42 | public function it_should_stop_event_if_table_rate_is_already_in_use( 43 | ResourceControllerEvent $event, 44 | ShippingMethodRepositoryInterface $shippingMethodRepository 45 | ): void { 46 | $tableRate = new ShippingTableRate(); 47 | $tableRate->setCode('TABLE_RATE'); 48 | $anotherTableRate = new ShippingTableRate(); 49 | $anotherTableRate->setCode('ANOTHER_RATE'); 50 | $shippingMethod1 = new ShippingMethod(); 51 | $shippingMethod1->setCode('METHOD1'); 52 | $shippingMethod1->setConfiguration(['WEB-US' => ['table_rate' => 'TABLE_RATE']]); 53 | $shippingMethod2 = new ShippingMethod(); 54 | $shippingMethod2->setCode('METHOD2'); 55 | $shippingMethod2->setConfiguration(['WEB-US' => ['table_rate' => 'ANOTHER_RATE']]); 56 | $shippingMethod3 = new ShippingMethod(); 57 | $shippingMethod3->setCode('METHOD3'); 58 | $shippingMethod3->setConfiguration( 59 | ['WEB-US' => ['table_rate' => 'ANOTHER_RATE'], 'WEB-EU' => ['table_rate' => 'TABLE_RATE']] 60 | ); 61 | $shippingMethodRepository 62 | ->findBy(['calculator' => TableRateShippingCalculator::TYPE]) 63 | ->willReturn([$shippingMethod1, $shippingMethod2, $shippingMethod3]) 64 | ; 65 | $event->getSubject()->willReturn($tableRate); 66 | $event 67 | ->stop( 68 | 'webgriffe_sylius_table_rate_plugin.ui.shipping_table_rate.already_used_by_shipping_methods', 69 | ResourceControllerEvent::TYPE_ERROR, 70 | ['%shipping_methods%' => 'METHOD1, METHOD3'], 71 | 400 72 | ) 73 | ->shouldBeCalled() 74 | ; 75 | 76 | $this->onTableRatePreDelete($event); 77 | } 78 | 79 | public function it_should_not_stop_event_if_table_rate_is_not_in_use( 80 | ShippingTableRate $shippingTableRate, 81 | ResourceControllerEvent $event, 82 | ShippingMethodRepositoryInterface $shippingMethodRepository 83 | ): void { 84 | $event->getSubject()->willReturn($shippingTableRate); 85 | $shippingMethodRepository 86 | ->findBy(['calculator' => TableRateShippingCalculator::TYPE]) 87 | ->willReturn([]) 88 | ; 89 | $event 90 | ->stop() 91 | ->shouldNotBeCalled() 92 | ; 93 | 94 | $this->onTableRatePreDelete($event); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /spec/Form/EventSubscriber/AddCurrencySubscriberSpec.php: -------------------------------------------------------------------------------- 1 | shouldImplement(EventSubscriberInterface::class); 19 | } 20 | 21 | function it_subscribes_to_event(): void 22 | { 23 | $this::getSubscribedEvents()->shouldReturn([FormEvents::PRE_SET_DATA => 'preSetData']); 24 | } 25 | 26 | function it_sets_currency_as_disabled_when_table_rate_is_not_new( 27 | FormEvent $event, 28 | ShippingTableRate $shippingTableRate, 29 | FormInterface $form 30 | ): void { 31 | $event->getData()->willReturn($shippingTableRate); 32 | $event->getForm()->willReturn($form); 33 | 34 | $shippingTableRate->getId()->willReturn(2); 35 | 36 | $form 37 | ->add('currency', Argument::type('string'), Argument::withEntry('disabled', true)) 38 | ->shouldBeCalled() 39 | ->willReturn($form) 40 | ; 41 | 42 | $this->preSetData($event); 43 | } 44 | 45 | function it_does_not_set_currency_as_disabled_when_table_rate_is_new( 46 | FormEvent $event, 47 | ShippingTableRate $shippingTableRate, 48 | FormInterface $form 49 | ): void { 50 | $event->getData()->willReturn($shippingTableRate); 51 | $event->getForm()->willReturn($form); 52 | 53 | $shippingTableRate->getId()->willReturn(null); 54 | 55 | $form 56 | ->add('currency', Argument::type('string'), Argument::withEntry('disabled', false)) 57 | ->shouldBeCalled() 58 | ->willReturn($form) 59 | ; 60 | 61 | $this->preSetData($event); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spec/Resolver/TableRateResolverSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($tableRateRepository); 22 | } 23 | 24 | public function it_should_implement_table_rate_resolver_interface(): void 25 | { 26 | $this->shouldImplement(TableRateResolverInterface::class); 27 | } 28 | 29 | public function it_should_resolve_table_rate_from_shipment( 30 | ShipmentInterface $shipment, 31 | OrderInterface $order, 32 | ChannelInterface $channel, 33 | RepositoryInterface $tableRateRepository, 34 | ShippingTableRate $tableRate 35 | ): void { 36 | $shipment->getOrder()->willReturn($order); 37 | $order->getChannel()->willReturn($channel); 38 | $channel->getCode()->willReturn('CHANNEL_CODE'); 39 | $tableRate->getCode()->willReturn('TABLE_RATE_CODE'); 40 | $tableRateRepository->findOneBy(['code' => 'TABLE_RATE_CODE'])->willReturn($tableRate); 41 | $calculatorConfig = ['CHANNEL_CODE' => ['table_rate' => 'TABLE_RATE_CODE']]; 42 | 43 | $this->resolve($shipment, $calculatorConfig)->shouldReturn($tableRate); 44 | } 45 | 46 | public function it_throws_a_missing_channel_configuration_exception_if_the_order_channel_is_not_configured( 47 | ShipmentInterface $shipment, 48 | OrderInterface $order, 49 | ChannelInterface $channel, 50 | ShippingMethodInterface $shippingMethod, 51 | ShippingTableRate $shippingTableRate 52 | ): void { 53 | $shipment->getOrder()->willReturn($order); 54 | $order->getChannel()->willReturn($channel); 55 | $channel->getCode()->willReturn('ANOTHER_CHANNEL_CODE'); 56 | $channel->getName()->willReturn('Another channel'); 57 | 58 | $shipment->getMethod()->willReturn($shippingMethod); 59 | $shippingMethod->getName()->willReturn('Table rate based'); 60 | $shippingTableRate->getCode()->willReturn('TABLE_RATE_CODE'); 61 | 62 | $this 63 | ->shouldThrow( 64 | new MissingChannelConfigurationException( 65 | 'Shipping method "Table rate based" has no configuration for channel "Another channel".' 66 | ) 67 | ) 68 | ->during('resolve', [$shipment, ['CHANNEL_CODE' => ['table_rate' => $shippingTableRate]]]) 69 | ; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Calculator/TableRateShippingCalculator.php: -------------------------------------------------------------------------------- 1 | tableRateResolver->resolve($shipment, $configuration); 28 | 29 | try { 30 | return $tableRate->getRate($shipment->getShippingWeight()); 31 | } catch (RateNotFoundException $e) { 32 | return 0; 33 | } 34 | } 35 | 36 | public function getType(): string 37 | { 38 | return self::TYPE; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Checker/TableRateShippingMethodEligibilityChecker.php: -------------------------------------------------------------------------------- 1 | eligibilityChecker->isEligible($subject, $method)) { 29 | return false; 30 | } 31 | 32 | if ($method->getCalculator() !== TableRateShippingCalculator::TYPE) { 33 | return true; 34 | } 35 | 36 | Assert::isInstanceOf($subject, ShipmentInterface::class); 37 | 38 | $weight = $subject->getShippingWeight(); 39 | /** @noinspection PhpParamsInspection */ 40 | $tableRate = $this->tableRateResolver->resolve($subject, $method->getConfiguration()); 41 | 42 | try { 43 | $tableRate->getRate($weight); 44 | } catch (RateNotFoundException $e) { 45 | return false; 46 | } 47 | 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | processConfiguration($this->getConfiguration([], $container), $configs); 18 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 19 | 20 | $loader->load('services.yml'); 21 | } 22 | 23 | public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface 24 | { 25 | return new Configuration(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Entity/ShippingTableRate.php: -------------------------------------------------------------------------------- 1 | id; 56 | } 57 | 58 | public function getCode(): ?string 59 | { 60 | return $this->code; 61 | } 62 | 63 | public function setCode(?string $code): void 64 | { 65 | $this->code = $code; 66 | } 67 | 68 | public function getName(): ?string 69 | { 70 | return $this->name; 71 | } 72 | 73 | public function setName(?string $name): void 74 | { 75 | $this->name = $name; 76 | } 77 | 78 | public function getCurrency(): ?CurrencyInterface 79 | { 80 | return $this->currency; 81 | } 82 | 83 | public function setCurrency(?CurrencyInterface $currency): void 84 | { 85 | $this->currency = $currency; 86 | } 87 | 88 | public function addRate(float $weightLimit, int $rate): void 89 | { 90 | $this->weightLimitToRate[] = ['weightLimit' => $weightLimit, 'rate' => $rate]; 91 | } 92 | 93 | public function getRate(float $weight): int 94 | { 95 | usort($this->weightLimitToRate, function (array $a, array $b): int { 96 | return $a['weightLimit'] <=> $b['weightLimit']; 97 | }); 98 | 99 | foreach ($this->weightLimitToRate as $array) { 100 | if ($weight <= $array['weightLimit']) { 101 | return $array['rate']; 102 | } 103 | } 104 | 105 | throw new RateNotFoundException($this, $weight); 106 | } 107 | 108 | public function getRatesCount(): int 109 | { 110 | return count($this->weightLimitToRate); 111 | } 112 | 113 | /** 114 | * @internal 115 | */ 116 | public function getWeightLimitToRate(): array 117 | { 118 | return $this->weightLimitToRate; 119 | } 120 | 121 | /** 122 | * @internal 123 | */ 124 | public function setWeightLimitToRate(array $weightLimitToRate): void 125 | { 126 | $this->weightLimitToRate = $weightLimitToRate; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/EventSubscriber/TableRateDeleteSubscriber.php: -------------------------------------------------------------------------------- 1 | 'onTableRatePreDelete']; 24 | } 25 | 26 | public function onTableRatePreDelete(ResourceControllerEvent $event): void 27 | { 28 | $shippingTableRate = $event->getSubject(); 29 | Assert::isInstanceOf($shippingTableRate, ShippingTableRate::class); 30 | 31 | $shippingMethods = $this->shippingMethodRepository->findBy(['calculator' => TableRateShippingCalculator::TYPE]); 32 | $foundMethods = []; 33 | /** @var ShippingMethod $shippingMethod */ 34 | foreach ($shippingMethods as $shippingMethod) { 35 | foreach ($shippingMethod->getConfiguration() as $channelConfiguration) { 36 | /** @var string|null $channelTableRateCode */ 37 | $channelTableRateCode = $channelConfiguration['table_rate'] ?? null; 38 | if ($channelTableRateCode !== null && $channelTableRateCode === $shippingTableRate->getCode()) { 39 | $foundMethods[] = $shippingMethod->getCode(); 40 | } 41 | } 42 | } 43 | 44 | if (count($foundMethods) > 0) { 45 | $event->stop( 46 | 'webgriffe_sylius_table_rate_plugin.ui.shipping_table_rate.already_used_by_shipping_methods', 47 | ResourceControllerEvent::TYPE_ERROR, 48 | ['%shipping_methods%' => implode(', ', $foundMethods)], 49 | 400, 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Exception/RateNotFoundException.php: -------------------------------------------------------------------------------- 1 | getCode(), 21 | $weight, 22 | ); 23 | parent::__construct($message, $code, $previous); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Form/EventSubscriber/AddCurrencySubscriber.php: -------------------------------------------------------------------------------- 1 | 'preSetData']; 19 | } 20 | 21 | public function preSetData(FormEvent $event): void 22 | { 23 | $messagesNamespace = 'webgriffe_sylius_table_rate_plugin.ui.shipping_table_rate.'; 24 | $form = $event->getForm(); 25 | $resource = $event->getData(); 26 | Assert::nullOrIsInstanceOf($resource, ResourceInterface::class); 27 | 28 | $form->add( 29 | 'currency', 30 | CurrencyChoiceType::class, 31 | [ 32 | 'label' => $messagesNamespace . 'currency.label', 33 | 'required' => true, 34 | 'placeholder' => $messagesNamespace . 'currency.placeholder', 35 | 'disabled' => $this->shouldCurrencyBeDisabled($resource), 36 | ], 37 | ); 38 | } 39 | 40 | private function shouldCurrencyBeDisabled(?ResourceInterface $resource): bool 41 | { 42 | if (null === $resource) { 43 | return false; 44 | } 45 | 46 | return $resource->getId() !== null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Form/Type/Shipping/Calculator/ChannelBasedTableRateConfigurationType.php: -------------------------------------------------------------------------------- 1 | setDefaults( 17 | [ 18 | 'entry_type' => TableRateConfigurationType::class, 19 | 'entry_options' => function (ChannelInterface $channel): array { 20 | return ['label' => $channel->getName(), 'currency' => $channel->getBaseCurrency()]; 21 | }, 22 | ], 23 | ); 24 | } 25 | 26 | public function getParent(): string 27 | { 28 | return ChannelCollectionType::class; 29 | } 30 | 31 | public function getBlockPrefix(): string 32 | { 33 | return 'webgriffe_sylius_table_rate_shipping_plugin_calculator_channel_based_table_rate'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Form/Type/Shipping/Calculator/TableRateConfigurationType.php: -------------------------------------------------------------------------------- 1 | add( 34 | self::TABLE_RATE_FIELD_NAME, 35 | EntityType::class, 36 | [ 37 | 'label' => $messagesNamespace . 'table_rate.label', 38 | 'placeholder' => $messagesNamespace . 'table_rate.placeholder', 39 | 'class' => ShippingTableRate::class, 40 | 'query_builder' => function (EntityRepository $entityRepository) use ($currency): QueryBuilder { 41 | return $entityRepository 42 | ->createQueryBuilder('tr') 43 | ->where('tr.currency = :currency') 44 | ->setParameter('currency', $currency) 45 | ; 46 | }, 47 | 'choice_label' => 'name', 48 | 'choice_value' => 'code', 49 | 'constraints' => [new NotBlank(['groups' => ['sylius']])], 50 | ], 51 | )->setDataMapper($this); 52 | } 53 | 54 | public function configureOptions(OptionsResolver $resolver): void 55 | { 56 | $resolver 57 | ->setDefaults(['data_class' => null]) 58 | ->setRequired('currency') 59 | ; 60 | } 61 | 62 | public function getBlockPrefix(): string 63 | { 64 | return 'webgriffe_sylius_table_rate_shipping_plugin_calculator_table_rate'; 65 | } 66 | 67 | /** 68 | * @psalm-suppress MoreSpecificImplementedParamType 69 | * @psalm-suppress PossiblyInvalidArgument 70 | * @psalm-suppress UnnecessaryVarAnnotation 71 | */ 72 | public function mapDataToForms(mixed $viewData, \Traversable $forms): void 73 | { 74 | // there is no data yet, so nothing to prepopulate 75 | if (null === $viewData) { 76 | return; 77 | } 78 | 79 | // invalid data type 80 | if (!is_array($viewData)) { 81 | throw new UnexpectedTypeException($viewData, 'array'); 82 | } 83 | 84 | /** @var FormInterface[] $forms */ 85 | $forms = iterator_to_array($forms); 86 | 87 | if (!array_key_exists(self::TABLE_RATE_FIELD_NAME, $viewData)) { 88 | return; 89 | } 90 | 91 | // initialize form field values 92 | $forms['table_rate']->setData($this->tableRateRepository->findOneBy(['code' => $viewData[self::TABLE_RATE_FIELD_NAME]])); 93 | } 94 | 95 | /** 96 | * @psalm-suppress MoreSpecificImplementedParamType 97 | * @psalm-suppress PossiblyInvalidArgument 98 | * @psalm-suppress UnnecessaryVarAnnotation 99 | */ 100 | public function mapFormsToData(\Traversable $forms, mixed &$viewData): void 101 | { 102 | /** @var FormInterface[] $forms */ 103 | $forms = iterator_to_array($forms); 104 | 105 | // as data is passed by reference, overriding it will change it in 106 | // the form object as well 107 | // beware of type inconsistency, see caution below 108 | $tableRateSelected = $forms[self::TABLE_RATE_FIELD_NAME]->getData(); 109 | if (!$tableRateSelected instanceof ShippingTableRate) { 110 | $viewData = []; 111 | 112 | return; 113 | } 114 | 115 | $viewData = [self::TABLE_RATE_FIELD_NAME => $tableRateSelected->getCode()]; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Form/Type/ShippingTableRateType.php: -------------------------------------------------------------------------------- 1 | addEventSubscriber(new AddCodeFormSubscriber()) 21 | ->addEventSubscriber(new AddCurrencySubscriber()) 22 | ->add('name', TextType::class, ['label' => $messagesNamespace . 'name']) 23 | ->add( 24 | 'weightLimitToRate', 25 | CollectionType::class, 26 | [ 27 | 'label' => $messagesNamespace . 'weightLimitToRate.label', 28 | 'allow_add' => true, 29 | 'allow_delete' => true, 30 | 'entry_type' => WeightLimitToRateType::class, 31 | ], 32 | ) 33 | ; 34 | } 35 | 36 | public function getBlockPrefix(): string 37 | { 38 | return 'webgriffe_sylius_table_rate_plugin_shipping_table_rate'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Form/Type/WeightLimitToRateType.php: -------------------------------------------------------------------------------- 1 | add( 20 | 'weightLimit', 21 | NumberType::class, 22 | [ 23 | 'label' => $messagesNamespace . 'weightLimit', 24 | 'scale' => 2, 25 | 'required' => true, 26 | 'constraints' => [new NotBlank(['groups' => 'sylius'])], 27 | ], 28 | ) 29 | ->add( 30 | 'rate', 31 | MoneyType::class, 32 | [ 33 | 'label' => $messagesNamespace . 'rate', 34 | 'required' => true, 35 | 'constraints' => [new NotBlank(['groups' => 'sylius'])], 36 | ], 37 | ) 38 | ; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Menu/AdminMenuListener.php: -------------------------------------------------------------------------------- 1 | getMenu(); 14 | $configuration = $menu->getChild('configuration'); 15 | if (null === $configuration) { 16 | return; 17 | } 18 | $configuration 19 | ->addChild( 20 | 'webgriffe-sylius-table-rate-plugin-table-rates', 21 | ['route' => 'webgriffe_admin_shipping_table_rate_index'], 22 | ) 23 | ->setLabel('webgriffe.ui.shipping_table_rates') 24 | ->setLabelAttribute('icon', 'pallet') 25 | ; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Resolver/TableRateResolver.php: -------------------------------------------------------------------------------- 1 | $tableRateRepository 21 | */ 22 | public function __construct(private RepositoryInterface $tableRateRepository) 23 | { 24 | } 25 | 26 | public function resolve(ShipmentInterface $shipment, array $calculatorConfig): ShippingTableRate 27 | { 28 | $order = $shipment->getOrder(); 29 | if (null === $order) { 30 | throw new \RuntimeException('Cannot resolve a table rate, there\'s no order for this shipment.'); 31 | } 32 | $channel = $order->getChannel(); 33 | if (null === $channel) { 34 | throw new \RuntimeException( 35 | 'Cannot resolve a table rate, there\'s no channel for this shipment\'s order.', 36 | ); 37 | } 38 | $channelCode = $channel->getCode(); 39 | 40 | if (!isset($calculatorConfig[$channelCode])) { 41 | $shippingMethod = $shipment->getMethod(); 42 | if (null === $shippingMethod) { 43 | throw new MissingChannelConfigurationException( 44 | sprintf( 45 | 'This shipment has no configuration for channel "%s".', 46 | $channel->getName(), 47 | ), 48 | ); 49 | } 50 | 51 | throw new MissingChannelConfigurationException( 52 | sprintf( 53 | 'Shipping method "%s" has no configuration for channel "%s".', 54 | $shippingMethod->getName(), 55 | $channel->getName(), 56 | ), 57 | ); 58 | } 59 | 60 | /** @var string $tableRateCode */ 61 | $tableRateCode = $calculatorConfig[$channelCode][TableRateConfigurationType::TABLE_RATE_FIELD_NAME]; 62 | $tableRate = $this->tableRateRepository->findOneBy(['code' => $tableRateCode]); 63 | Assert::isInstanceOf($tableRate, ShippingTableRate::class); 64 | 65 | return $tableRate; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Resolver/TableRateResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 |
17 | 18 | {{ error(form.vars.errors) }} 19 | 20 | {% if prototypes|default is iterable %} 21 | {% for key, subPrototype in prototypes %} 22 | 25 | {% endfor %} 26 | {% endif %} 27 | 28 |
29 | {% for child in form %} 30 | {{ _self.collectionItem(child, allow_delete, button_delete_label, loop.index0) }} 31 | {% endfor %} 32 |
33 | 34 | {% if prototype is defined and allow_add %} 35 | 36 | 37 | {{ button_add_label|trans }} 38 | 39 | {% endif %} 40 |
41 | {% endblock %} 42 | 43 | {% macro collectionItem(form, allow_delete, button_delete_label, index) %} 44 |
45 | {{ form_widget(form) }} 46 | {% if allow_delete %} 47 |
48 | 50 | 51 | {{ button_delete_label|trans }} 52 | 53 |
54 | {% endif %} 55 |
56 | {% endmacro %} 57 | 58 | {% block _webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate_entry_widget %} 59 | {%- if form is rootform -%} 60 | {{ form_errors(form) }} 61 | {%- endif -%} 62 | {{- block('form_rows') -}} 63 | {{- form_rest(form) -}} 64 | {% endblock %} 65 | 66 | {% block _webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate_entry_weightLimit_row %} 67 |
68 | {{ block('form_row') }} 69 |
70 | {% endblock %} 71 | {% block _webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate_entry_rate_row %} 72 |
73 | {{ block('form_row') }} 74 |
75 | {% endblock %} 76 | -------------------------------------------------------------------------------- /src/Resources/views/TableRateCrud/create.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@SyliusAdmin/Crud/create.html.twig' %} 2 | 3 | {% form_theme form.weightLimitToRate '@WebgriffeSyliusTableRateShippingPlugin/TableRateCrud/_weightLimitToRateTheme.html.twig' %} 4 | 5 | -------------------------------------------------------------------------------- /src/Resources/views/TableRateCrud/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@SyliusAdmin/Crud/index.html.twig' %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/TableRateCrud/update.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@SyliusAdmin/Crud/update.html.twig' %} 2 | 3 | {% form_theme form.weightLimitToRate '@WebgriffeSyliusTableRateShippingPlugin/TableRateCrud/_weightLimitToRateTheme.html.twig' %} 4 | -------------------------------------------------------------------------------- /src/WebgriffeSyliusTableRateShippingPlugin.php: -------------------------------------------------------------------------------- 1 | symfony/framework-bundle ### 6 | APP_ENV=dev 7 | APP_DEBUG=1 8 | APP_SECRET=EDITME 9 | ###< symfony/framework-bundle ### 10 | 11 | ###> doctrine/doctrine-bundle ### 12 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 13 | # For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 14 | # Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls 15 | DATABASE_URL=mysql://root@127.0.0.1/sylius_table_rate_shipping_%kernel.environment%?serverVersion=5.5 16 | ###< doctrine/doctrine-bundle ### 17 | 18 | ###> lexik/jwt-authentication-bundle ### 19 | JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem 20 | JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem 21 | JWT_PASSPHRASE=acme_plugin_development 22 | ###< lexik/jwt-authentication-bundle ### 23 | 24 | ###> symfony/mailer ### 25 | MAILER_DSN=null://null 26 | ###< symfony/mailer ### 27 | 28 | ###> symfony/messenger ### 29 | # Choose one of the transports below 30 | # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages 31 | MESSENGER_TRANSPORT_DSN=doctrine://default 32 | # MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages 33 | ###< symfony/messenger ### 34 | -------------------------------------------------------------------------------- /tests/Application/.env.test: -------------------------------------------------------------------------------- 1 | APP_SECRET='ch4mb3r0f5ecr3ts' 2 | 3 | KERNEL_CLASS='Tests\Webgriffe\SyliusTableRateShippingPlugin\Application\Kernel' 4 | -------------------------------------------------------------------------------- /tests/Application/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'airbnb-base', 3 | env: { 4 | node: true, 5 | }, 6 | rules: { 7 | 'object-shorthand': ['error', 'always', { 8 | avoidQuotes: true, 9 | avoidExplicitReturnArrows: true, 10 | }], 11 | 'function-paren-newline': ['error', 'consistent'], 12 | 'max-len': ['warn', 120, 2, { 13 | ignoreUrls: true, 14 | ignoreComments: false, 15 | ignoreRegExpLiterals: true, 16 | ignoreStrings: true, 17 | ignoreTemplateLiterals: true, 18 | }], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /tests/Application/.gitignore: -------------------------------------------------------------------------------- 1 | /public/assets 2 | /public/build 3 | /public/css 4 | /public/js 5 | /public/media/* 6 | !/public/media/image/ 7 | /public/media/image/* 8 | !/public/media/image/.gitignore 9 | 10 | /node_modules 11 | 12 | ###> symfony/framework-bundle ### 13 | /.env.*.local 14 | /.env.local 15 | /.env.local.php 16 | /public/bundles 17 | /var/ 18 | /vendor/ 19 | ###< symfony/framework-bundle ### 20 | 21 | ###> symfony/web-server-bundle ### 22 | /.web-server-pid 23 | ###< symfony/web-server-bundle ### 24 | -------------------------------------------------------------------------------- /tests/Application/Kernel.php: -------------------------------------------------------------------------------- 1 | getProjectDir() . '/var/cache/' . $this->environment; 23 | } 24 | 25 | public function getLogDir(): string 26 | { 27 | return $this->getProjectDir() . '/var/log'; 28 | } 29 | 30 | public function registerBundles(): iterable 31 | { 32 | foreach ($this->getConfigurationDirectories() as $confDir) { 33 | $bundlesFile = $confDir . '/bundles.php'; 34 | if (false === is_file($bundlesFile)) { 35 | continue; 36 | } 37 | yield from $this->registerBundlesFromFile($bundlesFile); 38 | } 39 | } 40 | 41 | protected function configureRoutes(RoutingConfigurator $routes): void 42 | { 43 | foreach ($this->getConfigurationDirectories() as $confDir) { 44 | $this->loadRoutesConfiguration($routes, $confDir); 45 | } 46 | } 47 | 48 | protected function getContainerBaseClass(): string 49 | { 50 | if ($this->isTestEnvironment() && class_exists(MockerContainer::class)) { 51 | return MockerContainer::class; 52 | } 53 | 54 | return parent::getContainerBaseClass(); 55 | } 56 | 57 | private function isTestEnvironment(): bool 58 | { 59 | return 0 === strpos($this->getEnvironment(), 'test'); 60 | } 61 | 62 | private function loadRoutesConfiguration(RoutingConfigurator $routes, string $confDir): void 63 | { 64 | $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS); 65 | $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS); 66 | $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS); 67 | } 68 | 69 | /** 70 | * @return BundleInterface[] 71 | */ 72 | private function registerBundlesFromFile(string $bundlesFile): iterable 73 | { 74 | $contents = require $bundlesFile; 75 | foreach ($contents as $class => $envs) { 76 | if (isset($envs['all']) || isset($envs[$this->environment])) { 77 | yield new $class(); 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * @return string[] 84 | */ 85 | private function getConfigurationDirectories(): iterable 86 | { 87 | yield $this->getProjectDir() . '/config'; 88 | $syliusConfigDir = $this->getProjectDir() . '/config/sylius/' . SyliusKernel::MAJOR_VERSION . '.' . SyliusKernel::MINOR_VERSION; 89 | if (is_dir($syliusConfigDir)) { 90 | yield $syliusConfigDir; 91 | } 92 | $symfonyConfigDir = $this->getProjectDir() . '/config/symfony/' . BaseKernel::MAJOR_VERSION . '.' . BaseKernel::MINOR_VERSION; 93 | if (is_dir($symfonyConfigDir)) { 94 | yield $symfonyConfigDir; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/Application/assets/admin/entry.js: -------------------------------------------------------------------------------- 1 | import 'sylius/bundle/AdminBundle/Resources/private/entry'; 2 | -------------------------------------------------------------------------------- /tests/Application/assets/shop/entry.js: -------------------------------------------------------------------------------- 1 | import 'sylius/bundle/ShopBundle/Resources/private/entry'; 2 | -------------------------------------------------------------------------------- /tests/Application/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 19 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 20 | } 21 | 22 | if ($input->hasParameterOption('--no-debug', true)) { 23 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 24 | } 25 | 26 | require dirname(__DIR__).'/config/bootstrap.php'; 27 | 28 | if ($_SERVER['APP_DEBUG']) { 29 | umask(0000); 30 | 31 | if (class_exists(Debug::class)) { 32 | Debug::enable(); 33 | } 34 | } 35 | 36 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 37 | $application = new Application($kernel); 38 | $application->run($input); 39 | -------------------------------------------------------------------------------- /tests/Application/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sylius/plugin-skeleton-test-application", 3 | "description": "Sylius application for plugin testing purposes (composer.json needed for project dir resolving)", 4 | "license": "MIT" 5 | } 6 | -------------------------------------------------------------------------------- /tests/Application/config/api_platform/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/api_platform/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 11 | if (is_array($env = @include dirname(__DIR__) . '/.env.local.php')) { 12 | $_SERVER += $env; 13 | $_ENV += $env; 14 | } elseif (!class_exists(Dotenv::class)) { 15 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); 16 | } elseif (method_exists(Dotenv::class, 'bootEnv')) { 17 | (new Dotenv())->bootEnv(dirname(__DIR__) . '/.env'); 18 | 19 | return; 20 | } else { 21 | // load all the .env files 22 | (new Dotenv(true))->loadEnv(dirname(__DIR__) . '/.env'); 23 | } 24 | 25 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 26 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 27 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 28 | -------------------------------------------------------------------------------- /tests/Application/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 6 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 7 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 9 | Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], 10 | Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], 11 | Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], 12 | Sylius\Bundle\LocaleBundle\SyliusLocaleBundle::class => ['all' => true], 13 | Sylius\Bundle\ProductBundle\SyliusProductBundle::class => ['all' => true], 14 | Sylius\Bundle\ChannelBundle\SyliusChannelBundle::class => ['all' => true], 15 | Sylius\Bundle\AttributeBundle\SyliusAttributeBundle::class => ['all' => true], 16 | Sylius\Bundle\TaxationBundle\SyliusTaxationBundle::class => ['all' => true], 17 | Sylius\Bundle\ShippingBundle\SyliusShippingBundle::class => ['all' => true], 18 | Sylius\Bundle\PaymentBundle\SyliusPaymentBundle::class => ['all' => true], 19 | Sylius\Bundle\MailerBundle\SyliusMailerBundle::class => ['all' => true], 20 | Sylius\Bundle\PromotionBundle\SyliusPromotionBundle::class => ['all' => true], 21 | Sylius\Bundle\AddressingBundle\SyliusAddressingBundle::class => ['all' => true], 22 | Sylius\Bundle\InventoryBundle\SyliusInventoryBundle::class => ['all' => true], 23 | Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle::class => ['all' => true], 24 | Sylius\Bundle\UserBundle\SyliusUserBundle::class => ['all' => true], 25 | Sylius\Bundle\CustomerBundle\SyliusCustomerBundle::class => ['all' => true], 26 | Sylius\Bundle\UiBundle\SyliusUiBundle::class => ['all' => true], 27 | Sylius\Bundle\ReviewBundle\SyliusReviewBundle::class => ['all' => true], 28 | Sylius\Bundle\CoreBundle\SyliusCoreBundle::class => ['all' => true], 29 | Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true], 30 | Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], 31 | winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true], 32 | Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true], 33 | Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle::class => ['all' => true], 34 | JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], 35 | FOS\RestBundle\FOSRestBundle::class => ['all' => true], 36 | Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], 37 | Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], 38 | Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], 39 | Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], 40 | Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], 41 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 42 | Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], 43 | Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], 44 | Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], 45 | Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], 46 | Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], 47 | Webgriffe\SyliusTableRateShippingPlugin\WebgriffeSyliusTableRateShippingPlugin::class => ['all' => true], 48 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], 49 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], 50 | FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], 51 | Sylius\Behat\Application\SyliusTestPlugin\SyliusTestPlugin::class => ['test' => true, 'test_cached' => true], 52 | ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 53 | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], 54 | Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true], 55 | SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true], 56 | BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], 57 | SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], 58 | Sylius\Calendar\SyliusCalendarBundle::class => ['all' => true], 59 | Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], 60 | League\FlysystemBundle\FlysystemBundle::class => ['all' => true], 61 | ]; 62 | if (class_exists(Sylius\Abstraction\StateMachine\SyliusStateMachineAbstractionBundle::class)) { 63 | $bundles[Sylius\Abstraction\StateMachine\SyliusStateMachineAbstractionBundle::class] = ['all' => true]; 64 | } 65 | 66 | return $bundles; 67 | -------------------------------------------------------------------------------- /tests/Application/config/jwt/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIDbthk+aF5EACAggA 3 | MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBA3DYfh2mXByUxFNke/Wf5SBIIJ 4 | UBckIgXeXBWPLQAAq07pN8uNFMUcUirFuEvbmxVe1PupCCAqriNxi1DqeSu/M7c1 5 | h66y0BqKZu/0G9SVTg63iCKDEiRAM3hLyD2CsjYg8h2LAaqQ9dFYGV0cHRhCXagZ 6 | Sdt9YTfn2rarRbxauMSt0z9zwCaiUrBU4JwSM3g+tD7W0lxAm9TeaqBZek5DIX+j 7 | 3Gom5tPYQe8jvfGMGdMPuanoEwH4WbWzGcqypWriy4JwaggwKCQ4ituWfa9kqMMC 8 | 8HRmBBDg0gtafmQP910RZh18JL2ewF5Pl7GDsLtOj5gNLNuAiQxDCcYRnD4/Cdsl 9 | bH91btmGX1nUVIFViUTW93eBsjBgdgqOMRVxUKkSSX6CmIZWlE3AazgwSbvOvNrN 10 | JGa8X21UwfuS/JHLmfRmgdti0YxRjJkBYLPpcd3ILsi+MMhSHy0uycAM/dB80Q1B 11 | vkW1UXGbCw/PzA5yHrzULzAl69E3Tt5nTVMIIcBGxw2rf+ej+AVjsuOl7etwecdC 12 | gnA90ViNlGOACLVnhsjd4WVF9Oircosf0UYoblwcT6gw1GSVF9pWuu7k5hy/7Pt/ 13 | o1BvonUgz/4VHG+K58qvtnlto+JE0XWzPvukNUyggtekTLyoQCI3ZKge6ui3qLax 14 | N6whHpzFnFVF3GJAisTk5naHFawHNvH7t85pmc+UnjNUUmyl9RStl9LMYDSBKNlR 15 | LzPlJK27E5SLhhyJCni4+UYjH6PdlJuKXJ0365fufJ+5ajHRatwt039xLnK0W+oa 16 | L35NxCuXrn8YxOgJIomt7IrkV3AuxoWxcx4lRFoM0WCdn9SWZVtfFFiyX/Xr1qDg 17 | dUysw3/bePEkOKr5JWx09hT0OKDpkwLFo2Ljtvjln4EMXYEvvVqFciKw0kqF73Dw 18 | NyoSubwR4qs6FQclKW1TAP6UW4B6ffq1iagKOCTZ5bBtsPBZk8UGCJb57q4fUj4P 19 | nJy0hnSdlOH4Am+US4HF4ayOGuaV1Be1taurdJnt5cNnUYRah0wg4nG+wVdG5HJk 20 | f4dJ4nih9d6WA/8LfxdpB7NCwdR+KK6lky+GgLSdhmIT9lzjj2GDsU4lBf29TkBn 21 | lyt98/LWGrgCQgZAQ/obxLT8CZtY+tNejGoMppY+ub8DIaLBFID+fcz13kgA9x7a 22 | TeVB8RPok+S3yHXP9a4WSFe9DGjjN+m7EnRtte7MEjyMoekXVnT04gNbTMoGAjNb 23 | lrR4g3ICygZtsoGSB2VEu7o3azAspXNBMOuJfRCuC0LDXcjH3TbvjX0da5wHBoK9 24 | clRxu+CDo9A849HMkmSje8wED7ysZnkvSX0OdPjXahVd4t1tDRI6jSlzFo9fGcjp 25 | S8Ikm9iMrHXaWcDdtcq4C63CjSynIBr4mNIxe/f2e9nynm3AIv+aOan891RWHqrd 26 | DdpSSPShtzATI9PbB+b+S0Gw58Y8fpO7yoZ87VW1BMpadmFZ87YY78jdB7BwInNI 27 | JqtnivinM6qCsvbdMoGinUyL6PUcfQGiEAibouKr3zNRDC4aesBZZmj7w0dnf+HK 28 | YC905aR0cddlc6DBo/ed3o9krMcZ6oY/vruemPTc5G7Cg3t4H3mInRgURw22X1wo 29 | FsioU1yOdkK+MYxvmGsQvQuSJhp7h1Uz37t/olkPRafZgy2nEtw6DQO0Dm4UfSsD 30 | nysq6dn1WeZPkOipGBRgQmY1FTRzwPoCxi7+/EuHhD8hr962rHOglSuNqPG89J8r 31 | wdbTDr8kgXj2A9p+jI3TVKEX+h6FEhrCHW9SHUqATOZ7RiNL6hKld9j0U4D9gQwZ 32 | dflA0TxpVsHXm7pd1idkr46jIFgw7HA89Erm0Ty7RolfHkqlRca805AVmsKkviIz 33 | sbF5uv4WzIE3ViO8P1KMUhCyElm72mpyNTXBhkxkup9hJ4fQieaN6pET6dQ2xyjs 34 | SBIvQoXI0JQKpespcyAdoh88ULQjRUXEOaNFfN7q+itTcocwmPZfzW2nXORJT2p8 35 | SXLqSE73nYZdqzSYFq1hLcnlubJ7yPBYYG1fI0IydjSGKfnjtB0DReR32OToRZ7m 36 | laduZ8O+IaBUY4Sp6QdYcVbGGpG/wsPmTQyScc/O2bfSI7AiPnL9EnwebI9sPSWQ 37 | R0t0QMXZOSSqNY6jkYjsOCxeekRIdY6havo2Y52Ywti0QNrkT4BQ+175VVTmRMdy 38 | LNaMFeEq6ehSEdaHaozvjHvP50HQT43tCK+RJiL+Gf9FqawoQRt693yO5LFbQsuw 39 | QsUSMi41txpINMa+HEc2K5FvGoPr7FmajLK7X2fr+3c/yZ4fahoMKEAVFWl5kRYx 40 | Fe1smlw1Vxl/qNQ32LFWsBIK+XnYBteYmlpVyYrTgXyjnp1rK2zz0118DPFuYiAP 41 | O0r6nnBz0NbwnSKb7S4CjxBKDvDbWTzP35Q5L/vySnO2zRbM64Gw7sjeLiJittWS 42 | gQfbFpEk9k8KVndKM4H50Jp0WznmYpm1Tman8hUOiCvmq0qdI3bJ5Bnj0K+q2zFV 43 | +noGpMFdq1+8WaUFLQFGCPM+yJgCqDgT1RAgfsGcomckGcmenDtHaTbcSFabEdpM 44 | Tsa2qLdg/Kju+7JyGrkmobXl/azuyjYTHfRvSZrvO5WUDFzhChrJpIL4nA3ZGRlS 45 | gvy+OzyyBh4sRyHwLItwUwE81aya3W4llAkhQ7OycmqniJgjtJzLwnxv2RQsB8bF 46 | pyoqQdKVxkqHdbUFeh9igI4ffRAK+8xDER5J+RUoZ4mO8qJebxar54XTb6I/Lepc 47 | g8ITX8bJ/GH+M6JdP7tLCikDTSGS+i1ReMQXE5XuEajYOVbzQdyWU5jleZIx0f6X 48 | mTa4WvMEGNyNxKZZXsy9FAaBkZqrNzEv8k0uFgFMNWQcMMtiqbei86yACdqe+jiW 49 | HqHv8wfoBHR+eIARub2itOJ/cI+oKv96d4it4FqQ9Lml8RUFFZj7Hrd6EjDb6Nq4 50 | P9ti7eku/xZvS0saBNChvv44GhP6FZJS0i/gidVffLna7Wua98tPZEAXp57k+XUL 51 | PzsRJ4a+hFuQjkyXFoz/v8YuUdyCFUSVVr9ArVu0v4+4euFWpQLav5sXv0Gh9X58 52 | Ek1KIf7Z/tZAJnSjTjFuSbDX/AoTMTxpRBKKnFW6zY0Nw2pjTVMtTVDkv9xkBpBK 53 | wod7FPD5f0T7y9YOARVZnBxVRSkkcYpEJFy5pLNeadg9 54 | -----END ENCRYPTED PRIVATE KEY----- 55 | -------------------------------------------------------------------------------- /tests/Application/config/jwt/public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6QkmF/Xi5nAYb8Kzr7qC 3 | d63V2K+d/nCXbpDUKKDPJAqOtTlMoQSuJRLNnhhp7z1i/Cp4Bhifr20Pu2dq8JYg 4 | 6pRT4ctqvYb/MXxAaPZc3EcBC0S6AhgKO/fDvR3LcqYqGJmQQOXZvxTsgqongdvV 5 | 4XbqFBMMgngyayoBk0VKTaI/s+LQhIce+1QaxbAI0+/zbR0hZ1hWT73orJi3do+1 6 | TBzQol+V7WGa8LlJfmgM56qO3BmVkeTDMBc27pGp6g3+Oufk/l29jEGJlUT9yu7Q 7 | BRhaQTWNVASa2aD+AKjVBzJh53O2zD8slAbjF1M9U7bbWN28Sv+xC/dUz0q9HnPu 8 | RsY2tnwryqTyYn/Hf2xyP3/KvjJ6oslAwemu5JirdJkO7KVQAthWG42gLuhZg3ks 9 | cSZhCLZH7nO2UDsf+2ZZgdbhpYZwR4gDRfNt7GKWXnWZOz9Uw1yVCPgylyZRZwg8 10 | l0y9aABdj3379I22icrwpMZbAgkyxNSV6UNJuxZksLUoP3i9OvXYgPYU9E4tU/Ul 11 | Dm/T1rGSReGoPkU1YQnI50bq7p1byIoUu2scTflvpTVI5a7zULkS1tg60xk7vBRC 12 | aBc7nr4UEtA235N6uLtcGxH11WBMwsKX69sSU0sQdC4Sk25zXM2gc8R1XV9K3qz2 13 | wQorQRlCwrkG44VRDgbFH+8CAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /tests/Application/config/packages/_sylius.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } 3 | 4 | - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } 5 | 6 | - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } 7 | 8 | - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } 9 | 10 | parameters: 11 | sylius_core.public_dir: '%kernel.project_dir%/public' 12 | 13 | sylius_shop: 14 | product_grid: 15 | include_all_descendants: true 16 | 17 | sylius_api: 18 | enabled: true 19 | -------------------------------------------------------------------------------- /tests/Application/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | mapping: 3 | paths: 4 | - '%kernel.project_dir%/../../vendor/sylius/sylius/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources' 5 | - '%kernel.project_dir%/config/api_platform' 6 | - '%kernel.project_dir%/src/Entity' 7 | patch_formats: 8 | json: ['application/merge-patch+json'] 9 | swagger: 10 | versions: [3] 11 | -------------------------------------------------------------------------------- /tests/Application/config/packages/assets.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | assets: 3 | packages: 4 | shop: 5 | json_manifest_path: '%kernel.project_dir%/public/build/shop/manifest.json' 6 | admin: 7 | json_manifest_path: '%kernel.project_dir%/public/build/admin/manifest.json' 8 | -------------------------------------------------------------------------------- /tests/Application/config/packages/dev/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | profiler: { only_exceptions: false } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/dev/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | json_serialization: 4 | options: 5 | - JSON_PRETTY_PRINT 6 | - JSON_UNESCAPED_SLASHES 7 | - JSON_PRESERVE_ZERO_FRACTION 8 | json_deserialization: 9 | options: 10 | - JSON_PRETTY_PRINT 11 | - JSON_UNESCAPED_SLASHES 12 | - JSON_PRESERVE_ZERO_FRACTION 13 | -------------------------------------------------------------------------------- /tests/Application/config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | firephp: 8 | type: firephp 9 | level: info 10 | -------------------------------------------------------------------------------- /tests/Application/config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | # Adds a fallback DATABASE_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(DATABASE_URL): '' 7 | 8 | doctrine: 9 | dbal: 10 | driver: 'pdo_mysql' 11 | server_version: '5.7' 12 | charset: UTF8 13 | 14 | url: '%env(resolve:DATABASE_URL)%' 15 | -------------------------------------------------------------------------------- /tests/Application/config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | storage: 3 | table_storage: 4 | table_name: sylius_migrations 5 | -------------------------------------------------------------------------------- /tests/Application/config/packages/fos_rest.yaml: -------------------------------------------------------------------------------- 1 | fos_rest: 2 | exception: true 3 | view: 4 | formats: 5 | json: true 6 | xml: true 7 | empty_content: 204 8 | format_listener: 9 | rules: 10 | - { path: '^/api/v1/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } 11 | - { path: '^/', stop: true } 12 | -------------------------------------------------------------------------------- /tests/Application/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | form: true 4 | csrf_protection: true 5 | session: 6 | handler_id: ~ 7 | serializer: 8 | mapping: 9 | paths: [ '%kernel.project_dir%/config/serialization' ] 10 | -------------------------------------------------------------------------------- /tests/Application/config/packages/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | xml_serialization: 4 | format_output: '%kernel.debug%' 5 | -------------------------------------------------------------------------------- /tests/Application/config/packages/lexik_jwt_authentication.yaml: -------------------------------------------------------------------------------- 1 | lexik_jwt_authentication: 2 | secret_key: '%env(resolve:JWT_SECRET_KEY)%' 3 | public_key: '%env(resolve:JWT_PUBLIC_KEY)%' 4 | pass_phrase: '%env(JWT_PASSPHRASE)%' 5 | -------------------------------------------------------------------------------- /tests/Application/config/packages/liip_imagine.yaml: -------------------------------------------------------------------------------- 1 | liip_imagine: 2 | resolvers: 3 | default: 4 | web_path: 5 | web_root: "%kernel.project_dir%/public" 6 | cache_prefix: "media/cache" 7 | -------------------------------------------------------------------------------- /tests/Application/config/packages/mailer.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | mailer: 3 | dsn: '%env(MAILER_DSN)%' 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | metadata_cache_driver: 4 | type: service 5 | id: doctrine.system_cache_provider 6 | query_cache_driver: 7 | type: service 8 | id: doctrine.system_cache_provider 9 | result_cache_driver: 10 | type: service 11 | id: doctrine.result_cache_provider 12 | 13 | services: 14 | doctrine.result_cache_provider: 15 | class: Symfony\Component\Cache\DoctrineProvider 16 | public: false 17 | arguments: 18 | - '@doctrine.result_cache_pool' 19 | doctrine.system_cache_provider: 20 | class: Symfony\Component\Cache\DoctrineProvider 21 | public: false 22 | arguments: 23 | - '@doctrine.system_cache_pool' 24 | 25 | framework: 26 | cache: 27 | pools: 28 | doctrine.result_cache_pool: 29 | adapter: cache.app 30 | doctrine.system_cache_pool: 31 | adapter: cache.system 32 | -------------------------------------------------------------------------------- /tests/Application/config/packages/prod/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | json_serialization: 4 | options: 5 | - JSON_UNESCAPED_SLASHES 6 | - JSON_PRESERVE_ZERO_FRACTION 7 | json_deserialization: 8 | options: 9 | - JSON_UNESCAPED_SLASHES 10 | - JSON_PRESERVE_ZERO_FRACTION 11 | -------------------------------------------------------------------------------- /tests/Application/config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | nested: 8 | type: stream 9 | path: "%kernel.logs_dir%/%kernel.environment%.log" 10 | level: debug 11 | -------------------------------------------------------------------------------- /tests/Application/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | enable_authenticator_manager: true 3 | providers: 4 | sylius_admin_user_provider: 5 | id: sylius.admin_user_provider.email_or_name_based 6 | sylius_api_admin_user_provider: 7 | id: sylius.admin_user_provider.email_or_name_based 8 | sylius_shop_user_provider: 9 | id: sylius.shop_user_provider.email_or_name_based 10 | sylius_api_shop_user_provider: 11 | id: sylius.shop_user_provider.email_or_name_based 12 | 13 | password_hashers: 14 | Sylius\Component\User\Model\UserInterface: argon2i 15 | firewalls: 16 | admin: 17 | switch_user: true 18 | context: admin 19 | pattern: "%sylius.security.admin_regex%" 20 | provider: sylius_admin_user_provider 21 | form_login: 22 | provider: sylius_admin_user_provider 23 | login_path: sylius_admin_login 24 | check_path: sylius_admin_login_check 25 | failure_path: sylius_admin_login 26 | default_target_path: sylius_admin_dashboard 27 | use_forward: false 28 | use_referer: true 29 | enable_csrf: true 30 | csrf_parameter: _csrf_admin_security_token 31 | csrf_token_id: admin_authenticate 32 | remember_me: 33 | secret: "%env(APP_SECRET)%" 34 | path: "/%sylius_admin.path_name%" 35 | name: APP_ADMIN_REMEMBER_ME 36 | lifetime: 31536000 37 | remember_me_parameter: _remember_me 38 | logout: 39 | path: sylius_admin_logout 40 | target: sylius_admin_login 41 | 42 | new_api_admin_user: 43 | pattern: "%sylius.security.new_api_admin_regex%/.*" 44 | provider: sylius_api_admin_user_provider 45 | stateless: true 46 | entry_point: jwt 47 | json_login: 48 | check_path: "%sylius.security.new_api_admin_route%/authentication-token" 49 | username_path: email 50 | password_path: password 51 | success_handler: lexik_jwt_authentication.handler.authentication_success 52 | failure_handler: lexik_jwt_authentication.handler.authentication_failure 53 | jwt: true 54 | 55 | new_api_shop_user: 56 | pattern: "%sylius.security.new_api_shop_regex%/.*" 57 | provider: sylius_api_shop_user_provider 58 | stateless: true 59 | entry_point: jwt 60 | json_login: 61 | check_path: "%sylius.security.new_api_shop_route%/authentication-token" 62 | username_path: email 63 | password_path: password 64 | success_handler: lexik_jwt_authentication.handler.authentication_success 65 | failure_handler: lexik_jwt_authentication.handler.authentication_failure 66 | jwt: true 67 | 68 | shop: 69 | switch_user: { role: ROLE_ALLOWED_TO_SWITCH } 70 | context: shop 71 | pattern: "%sylius.security.shop_regex%" 72 | provider: sylius_shop_user_provider 73 | form_login: 74 | success_handler: sylius.authentication.success_handler 75 | failure_handler: sylius.authentication.failure_handler 76 | provider: sylius_shop_user_provider 77 | login_path: sylius_shop_login 78 | check_path: sylius_shop_login_check 79 | failure_path: sylius_shop_login 80 | default_target_path: sylius_shop_homepage 81 | use_forward: false 82 | use_referer: true 83 | enable_csrf: true 84 | csrf_parameter: _csrf_shop_security_token 85 | csrf_token_id: shop_authenticate 86 | remember_me: 87 | secret: "%env(APP_SECRET)%" 88 | name: APP_SHOP_REMEMBER_ME 89 | lifetime: 31536000 90 | remember_me_parameter: _remember_me 91 | logout: 92 | path: sylius_shop_logout 93 | target: sylius_shop_homepage 94 | invalidate_session: false 95 | 96 | dev: 97 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 98 | security: false 99 | 100 | image_resolver: 101 | pattern: ^/media/cache/resolve 102 | security: false 103 | 104 | access_control: 105 | - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } 106 | - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } 107 | - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } 108 | - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } 109 | 110 | - { path: "%sylius.security.admin_regex%/login", role: PUBLIC_ACCESS } 111 | - { path: "%sylius.security.shop_regex%/login", role: PUBLIC_ACCESS } 112 | 113 | - { path: "%sylius.security.shop_regex%/register", role: PUBLIC_ACCESS } 114 | - { path: "%sylius.security.shop_regex%/verify", role: PUBLIC_ACCESS } 115 | 116 | - { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS } 117 | - { path: "%sylius.security.shop_regex%/account", role: ROLE_USER } 118 | 119 | - { path: "%sylius.security.new_api_admin_route%/reset-password-requests", role: PUBLIC_ACCESS } 120 | - { path: "%sylius.security.new_api_admin_regex%/.*", role: ROLE_API_ACCESS } 121 | - { path: "%sylius.security.new_api_admin_route%/authentication-token", role: PUBLIC_ACCESS } 122 | - { path: "%sylius.security.new_api_user_account_regex%/.*", role: ROLE_USER } 123 | - { path: "%sylius.security.new_api_shop_route%/authentication-token", role: PUBLIC_ACCESS } 124 | - { path: "%sylius.security.new_api_shop_regex%/.*", role: PUBLIC_ACCESS } 125 | -------------------------------------------------------------------------------- /tests/Application/config/packages/staging/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | nested: 8 | type: stream 9 | path: "%kernel.logs_dir%/%kernel.environment%.log" 10 | level: debug 11 | -------------------------------------------------------------------------------- /tests/Application/config/packages/stof_doctrine_extensions.yaml: -------------------------------------------------------------------------------- 1 | # Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html 2 | # See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/ 3 | stof_doctrine_extensions: 4 | default_locale: '%locale%' 5 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | session: 4 | storage_factory_id: session.storage.factory.mock_file 5 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/mailer.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | pools: 4 | test.mailer_pool: 5 | adapter: cache.adapter.filesystem 6 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: error 7 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | password_hashers: 3 | Sylius\Component\User\Model\UserInterface: 4 | algorithm: argon2i 5 | time_cost: 3 6 | memory_cost: 10 7 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/sylius_theme.yaml: -------------------------------------------------------------------------------- 1 | sylius_theme: 2 | sources: 3 | test: ~ 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/sylius_uploader.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | Sylius\Component\Core\Generator\ImagePathGeneratorInterface: 3 | class: Sylius\Behat\Service\Generator\UploadedImagePathGenerator 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: false 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { collect: false } 7 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | entity_managers: 4 | default: 5 | result_cache_driver: 6 | type: memcached 7 | host: localhost 8 | port: 11211 9 | query_cache_driver: 10 | type: memcached 11 | host: localhost 12 | port: 11211 13 | metadata_cache_driver: 14 | type: memcached 15 | host: localhost 16 | port: 11211 17 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/fos_rest.yaml: -------------------------------------------------------------------------------- 1 | fos_rest: 2 | exception: 3 | debug: true 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/framework.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../test/framework.yaml } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/mailer.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "../test/mailer.yaml" } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: error 7 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/security.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../test/security.yaml } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/sylius_channel.yaml: -------------------------------------------------------------------------------- 1 | sylius_channel: 2 | debug: true 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/sylius_theme.yaml: -------------------------------------------------------------------------------- 1 | sylius_theme: 2 | sources: 3 | test: ~ 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/sylius_uploader.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "../test/sylius_uploader.yaml" } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/test_cached/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | strict_variables: true 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: '%locale%' 3 | translator: 4 | paths: 5 | - '%kernel.project_dir%/translations' 6 | fallbacks: 7 | - '%locale%' 8 | - 'en' 9 | -------------------------------------------------------------------------------- /tests/Application/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | paths: ['%kernel.project_dir%/templates'] 3 | debug: '%kernel.debug%' 4 | strict_variables: '%kernel.debug%' 5 | 6 | services: 7 | _defaults: 8 | public: false 9 | autowire: true 10 | autoconfigure: true 11 | 12 | Twig\Extra\Intl\IntlExtension: ~ 13 | -------------------------------------------------------------------------------- /tests/Application/config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | enable_annotations: true 4 | -------------------------------------------------------------------------------- /tests/Application/config/packages/webgriffe_sylius_table_rate_shipping_plugin.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/config.yml" } 3 | -------------------------------------------------------------------------------- /tests/Application/config/packages/webpack_encore.yaml: -------------------------------------------------------------------------------- 1 | webpack_encore: 2 | output_path: '%kernel.project_dir%/public/build/default' 3 | builds: 4 | shop: '%kernel.project_dir%/public/build/shop' 5 | admin: '%kernel.project_dir%/public/build/admin' 6 | -------------------------------------------------------------------------------- /tests/Application/config/routes/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | _wdt: 2 | resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" 3 | prefix: /_wdt 4 | 5 | _profiler: 6 | resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" 7 | prefix: /_profiler 8 | -------------------------------------------------------------------------------- /tests/Application/config/routes/liip_imagine.yaml: -------------------------------------------------------------------------------- 1 | _liip_imagine: 2 | resource: "@LiipImagineBundle/Resources/config/routing.yaml" 3 | -------------------------------------------------------------------------------- /tests/Application/config/routes/sylius_admin.yaml: -------------------------------------------------------------------------------- 1 | sylius_admin: 2 | resource: "@SyliusAdminBundle/Resources/config/routing.yml" 3 | prefix: /admin 4 | -------------------------------------------------------------------------------- /tests/Application/config/routes/sylius_api.yaml: -------------------------------------------------------------------------------- 1 | sylius_api: 2 | resource: "@SyliusApiBundle/Resources/config/routing.yml" 3 | prefix: "%sylius.security.new_api_route%" 4 | -------------------------------------------------------------------------------- /tests/Application/config/routes/sylius_shop.yaml: -------------------------------------------------------------------------------- 1 | sylius_shop: 2 | resource: "@SyliusShopBundle/Resources/config/routing.yml" 3 | prefix: /{_locale} 4 | requirements: 5 | _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ 6 | 7 | sylius_shop_payum: 8 | resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" 9 | 10 | sylius_shop_default_locale: 11 | path: / 12 | methods: [GET] 13 | defaults: 14 | _controller: sylius.controller.shop.locale_switch::switchAction 15 | -------------------------------------------------------------------------------- /tests/Application/config/routes/test/routing.yaml: -------------------------------------------------------------------------------- 1 | sylius_test_plugin_main: 2 | path: /test/main 3 | controller: FrameworkBundle:Template:template 4 | defaults: 5 | template: "@SyliusTestPlugin/main.html.twig" 6 | -------------------------------------------------------------------------------- /tests/Application/config/routes/test/sylius_test_plugin.yaml: -------------------------------------------------------------------------------- 1 | sylius_test_plugin_main: 2 | path: /test/main 3 | controller: FrameworkBundle:Template:template 4 | defaults: 5 | template: "@SyliusTestPlugin/main.html.twig" 6 | -------------------------------------------------------------------------------- /tests/Application/config/routes/test_cached/routing.yaml: -------------------------------------------------------------------------------- 1 | sylius_test_plugin_main: 2 | path: /test/main 3 | controller: FrameworkBundle:Template:template 4 | defaults: 5 | template: "@SyliusTestPlugin/main.html.twig" 6 | -------------------------------------------------------------------------------- /tests/Application/config/routes/test_cached/sylius_test_plugin.yaml: -------------------------------------------------------------------------------- 1 | sylius_test_plugin_main: 2 | path: /test/main 3 | controller: FrameworkBundle:Template:template 4 | defaults: 5 | template: "@SyliusTestPlugin/main.html.twig" 6 | -------------------------------------------------------------------------------- /tests/Application/config/routes/webgriffe_sylius_table_rate_shipping_plugin.yaml: -------------------------------------------------------------------------------- 1 | webgriffe_sylius_table_rate_shipping_plugin_shop: 2 | resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/shop_routing.yml" 3 | prefix: /{_locale} 4 | requirements: 5 | _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ 6 | 7 | webgriffe_sylius_table_rate_shipping_plugin_admina: 8 | resource: "@WebgriffeSyliusTableRateShippingPlugin/Resources/config/admin_routing.yml" 9 | prefix: /admin 10 | -------------------------------------------------------------------------------- /tests/Application/config/secrets/dev/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/secrets/dev/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/secrets/prod/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/secrets/prod/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/secrets/test/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/secrets/test/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/secrets/test_cached/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/secrets/test_cached/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/serialization/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/config/serialization/.gitignore -------------------------------------------------------------------------------- /tests/Application/config/services.yaml: -------------------------------------------------------------------------------- 1 | # Put parameters here that don't need to change on each machine where the app is deployed 2 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 3 | parameters: 4 | locale: en_US 5 | -------------------------------------------------------------------------------- /tests/Application/config/services_test.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "../../Behat/Resources/services.yml" } 3 | - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } 4 | 5 | # workaround needed for strange "test.client.history" problem 6 | # see https://github.com/FriendsOfBehat/SymfonyExtension/issues/88 7 | services: 8 | Symfony\Component\BrowserKit\AbstractBrowser: '@test.client' 9 | -------------------------------------------------------------------------------- /tests/Application/config/services_test_cached.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: "services_test.yaml" } 3 | -------------------------------------------------------------------------------- /tests/Application/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@babel/polyfill": "^7.0.0", 4 | "chart.js": "^3.7.1", 5 | "jquery": "^3.5.0", 6 | "jquery.dirtyforms": "^2.0.0", 7 | "lightbox2": "^2.9.0", 8 | "semantic-ui-css": "^2.2.0", 9 | "slick-carousel": "^1.8.1" 10 | }, 11 | "devDependencies": { 12 | "@babel/core": "^7.0.0", 13 | "@babel/plugin-external-helpers": "^7.0.0", 14 | "@babel/plugin-proposal-object-rest-spread": "^7.18.9", 15 | "@babel/preset-env": "^7.18.10", 16 | "@babel/register": "^7.0.0", 17 | "@rollup/plugin-babel": "^5.3.1", 18 | "@rollup/plugin-commonjs": "^22.0.2", 19 | "@rollup/plugin-inject": "^4.0.4", 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@semantic-ui-react/css-patch": "^1.1.2", 22 | "@symfony/webpack-encore": "^3.1.0", 23 | "babel-plugin-fast-async": "^6.1.2", 24 | "babel-plugin-module-resolver": "^4.1.0", 25 | "dedent": "^0.7.0", 26 | "eslint": "^8.23.0", 27 | "eslint-config-airbnb-base": "^15.0.0", 28 | "eslint-import-resolver-babel-module": "^5.3.1", 29 | "eslint-plugin-import": "^2.26.0", 30 | "fast-async": "^6.3.8", 31 | "gulp": "^4.0.2", 32 | "gulp-chug": "^0.5.1", 33 | "gulp-concat": "^2.6.1", 34 | "gulp-debug": "^4.0.0", 35 | "gulp-if": "^3.0.0", 36 | "gulp-livereload": "^4.0.2", 37 | "gulp-order": "^1.2.0", 38 | "gulp-sass": "^5.1.0", 39 | "gulp-sourcemaps": "^3.0.0", 40 | "gulp-uglifycss": "^1.1.0", 41 | "merge-stream": "^2.0.0", 42 | "rollup": "^2.79.0", 43 | "rollup-plugin-terser": "^7.0.2", 44 | "sass": "^1.54.8", 45 | "sass-loader": "^13.0.0", 46 | "upath": "^2.0.1", 47 | "yargs": "^17.5.1" 48 | }, 49 | "engines": { 50 | "node": "^14 || ^16 || ^18" 51 | }, 52 | "engineStrict": true, 53 | "scripts": { 54 | "watch": "encore dev --watch", 55 | "build": "encore dev", 56 | "build:prod": "encore production", 57 | "gulp": "gulp build", 58 | "lint": "yarn lint:js", 59 | "lint:js": "eslint gulpfile.babel.js src/Sylius/Bundle/AdminBundle/gulpfile.babel.js src/Sylius/Bundle/ShopBundle/gulpfile.babel.js src/Sylius/Bundle/UiBundle/Resources/private/js src/Sylius/Bundle/AdminBundle/Resources/private/js src/Sylius/Bundle/ShopBundle/Resources/private/js", 60 | "postinstall": "semantic-ui-css-patch" 61 | }, 62 | "repository": { 63 | "type": "git", 64 | "url": "git+https://github.com/Sylius/Sylius.git" 65 | }, 66 | "author": "Paweł Jędrzejewski", 67 | "license": "MIT" 68 | } 69 | -------------------------------------------------------------------------------- /tests/Application/public/.htaccess: -------------------------------------------------------------------------------- 1 | DirectoryIndex app.php 2 | 3 | 4 | RewriteEngine On 5 | 6 | RewriteCond %{HTTP:Authorization} ^(.*) 7 | RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] 8 | 9 | RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ 10 | RewriteRule ^(.*) - [E=BASE:%1] 11 | 12 | RewriteCond %{ENV:REDIRECT_STATUS} ^$ 13 | RewriteRule ^index\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] 14 | 15 | RewriteCond %{REQUEST_FILENAME} -f 16 | RewriteRule .? - [L] 17 | 18 | RewriteRule .? %{ENV:BASE}/index.php [L] 19 | 20 | 21 | 22 | 23 | RedirectMatch 302 ^/$ /index.php/ 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/Application/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/public/favicon.ico -------------------------------------------------------------------------------- /tests/Application/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 28 | $response->send(); 29 | $kernel->terminate($request, $response); 30 | -------------------------------------------------------------------------------- /tests/Application/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | -------------------------------------------------------------------------------- /tests/Application/src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/src/Entity/.gitignore -------------------------------------------------------------------------------- /tests/Application/templates/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/templates/.gitignore -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusAdminBundle/Security/_content.html.twig: -------------------------------------------------------------------------------- 1 | {% include '@SyliusUi/Security/_login.html.twig' 2 | with { 3 | 'action': path('sylius_admin_login_check'), 4 | 'paths': {'logo': asset('build/admin/images/logo.png', 'admin')} 5 | } 6 | %} 7 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig: -------------------------------------------------------------------------------- 1 | {{ encore_entry_script_tags('admin-entry', null, 'admin') }} 2 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig: -------------------------------------------------------------------------------- 1 | {{ encore_entry_link_tags('admin-entry', null, 'admin') }} 2 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusShopBundle/Homepage/_banner.html.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ 'sylius.homepage.banner_content'|trans }} 4 |
5 |
6 |
{{ 'sylius.homepage.banner_content'|trans }}
7 | {{ 'sylius.homepage.banner_button'|trans }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig: -------------------------------------------------------------------------------- 1 |
2 | 3 | Sylius logo 4 | 5 |
6 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig: -------------------------------------------------------------------------------- 1 | {{ encore_entry_script_tags('shop-entry', null, 'shop') }} 2 | -------------------------------------------------------------------------------- /tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig: -------------------------------------------------------------------------------- 1 | {{ encore_entry_link_tags('shop-entry', null, 'shop') }} 2 | -------------------------------------------------------------------------------- /tests/Application/translations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgriffe/WebgriffeSyliusTableRateShippingPlugin/f94f6c1b985927d11fdf38972365f920a8d1d6f5/tests/Application/translations/.gitignore -------------------------------------------------------------------------------- /tests/Application/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const Encore = require('@symfony/webpack-encore'); 3 | 4 | const syliusBundles = path.resolve(__dirname, '../../vendor/sylius/sylius/src/Sylius/Bundle/'); 5 | const uiBundleScripts = path.resolve(syliusBundles, 'UiBundle/Resources/private/js/'); 6 | const uiBundleResources = path.resolve(syliusBundles, 'UiBundle/Resources/private/'); 7 | 8 | // Shop config 9 | Encore 10 | .setOutputPath('public/build/shop/') 11 | .setPublicPath('/build/shop') 12 | .addEntry('shop-entry', './assets/shop/entry.js') 13 | .disableSingleRuntimeChunk() 14 | .cleanupOutputBeforeBuild() 15 | .enableSourceMaps(!Encore.isProduction()) 16 | .enableVersioning(Encore.isProduction()) 17 | .enableSassLoader(); 18 | 19 | const shopConfig = Encore.getWebpackConfig(); 20 | 21 | shopConfig.resolve.alias['sylius/ui'] = uiBundleScripts; 22 | shopConfig.resolve.alias['sylius/ui-resources'] = uiBundleResources; 23 | shopConfig.resolve.alias['sylius/bundle'] = syliusBundles; 24 | shopConfig.resolve.alias['chart.js/dist/Chart.min'] = path.resolve(__dirname, 'node_modules/chart.js/dist/chart.min.js'); 25 | shopConfig.name = 'shop'; 26 | 27 | Encore.reset(); 28 | 29 | // Admin config 30 | Encore 31 | .setOutputPath('public/build/admin/') 32 | .setPublicPath('/build/admin') 33 | .addEntry('admin-entry', './assets/admin/entry.js') 34 | .disableSingleRuntimeChunk() 35 | .cleanupOutputBeforeBuild() 36 | .enableSourceMaps(!Encore.isProduction()) 37 | .enableVersioning(Encore.isProduction()) 38 | .enableSassLoader(); 39 | 40 | const adminConfig = Encore.getWebpackConfig(); 41 | 42 | adminConfig.resolve.alias['sylius/ui'] = uiBundleScripts; 43 | adminConfig.resolve.alias['sylius/ui-resources'] = uiBundleResources; 44 | adminConfig.resolve.alias['sylius/bundle'] = syliusBundles; 45 | adminConfig.resolve.alias['chart.js/dist/Chart.min'] = path.resolve(__dirname, 'node_modules/chart.js/dist/chart.min.js'); 46 | adminConfig.externals = Object.assign({}, adminConfig.externals, { window: 'window', document: 'document' }); 47 | adminConfig.name = 'admin'; 48 | 49 | module.exports = [shopConfig, adminConfig]; 50 | -------------------------------------------------------------------------------- /tests/Behat/Context/Setup/ProductContext.php: -------------------------------------------------------------------------------- 1 | productExampleFactory->create([ 38 | 'name' => $productName, 39 | 'enabled' => true, 40 | 'main_taxon' => null, 41 | ]); 42 | 43 | /** @var ProductVariantInterface $productVariant */ 44 | $productVariant = $product->getVariants()->first(); 45 | 46 | $productVariant->setWeight((float) $weight); 47 | 48 | $this->productRepository->add($product); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Behat/Context/Setup/ShippingTableRateContext.php: -------------------------------------------------------------------------------- 1 | shippingTableRateRepository->findBy(['name' => $name]); 40 | 41 | Assert::count( 42 | $shippingTableRates, 43 | 1, 44 | sprintf('%d shipping table rates has been found with name "%s".', count($shippingTableRates), $name), 45 | ); 46 | 47 | return $shippingTableRates[0]; 48 | } 49 | 50 | /** 51 | * @Given the store has (also) a shipping table rate :name for currency :currency 52 | */ 53 | public function theStoreHasShippingTableRateForCurrency(string $name, CurrencyInterface $currency): void 54 | { 55 | /** @var ShippingTableRate $shippingTableRate */ 56 | $shippingTableRate = $this->shippingTableRateFactory->createNew(); 57 | $shippingTableRate->setName($name); 58 | $shippingTableRate->setCode(StringInflector::nameToUppercaseCode($name)); 59 | $shippingTableRate->setCurrency($currency); 60 | 61 | $this->shippingTableRateRepository->add($shippingTableRate); 62 | 63 | $this->sharedStorage->set('shipping_table_rate', $shippingTableRate); 64 | } 65 | 66 | /** 67 | * @Given /^(this shipping table rate) has a rate ("[^"]+") for shipments up to (\d+) kg$/ 68 | * @Given /^(it) has a rate ("[^"]+") for shipments up to (\d+) kg$/ 69 | */ 70 | public function thisShippingTableRateHasRateForShipmentsUpToKg(ShippingTableRate $shippingTableRate, int $rate, int $weightLimit): void 71 | { 72 | $shippingTableRate->addRate($weightLimit, $rate); 73 | 74 | $this->shippingTableRateManager->flush(); 75 | } 76 | 77 | /** 78 | * @Given the store has :shippingMethodName shipping method using :shippingTableRate table rate for :channel channel 79 | */ 80 | public function theStoreHasShippingMethodUsingTableRateForChannel( 81 | string $shippingMethodName, 82 | ShippingTableRate $shippingTableRate, 83 | ChannelInterface $channel, 84 | ): void { 85 | /** @var ShippingMethodInterface $shippingMethod */ 86 | $shippingMethod = $this->shippingMethodExampleFactory->create([ 87 | 'name' => $shippingMethodName, 88 | 'enabled' => true, 89 | 'zone' => $this->getShippingZone(), 90 | 'calculator' => [ 91 | 'type' => TableRateShippingCalculator::TYPE, 92 | 'configuration' => [$channel->getCode() => [TableRateShippingCalculator::TYPE => $shippingTableRate->getCode()]], 93 | ], 94 | 'channels' => [$channel], 95 | ]); 96 | 97 | $this->shippingMethodRepository->add($shippingMethod); 98 | } 99 | 100 | private function getShippingZone(): ZoneInterface 101 | { 102 | if ($this->sharedStorage->has('shipping_zone')) { 103 | return $this->sharedStorage->get('shipping_zone'); 104 | } 105 | 106 | return $this->sharedStorage->get('zone'); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/Behat/Context/Transform/NumberContext.php: -------------------------------------------------------------------------------- 1 | 0, 13 | 'one' => 1, 14 | 'two' => 2, 15 | 'three' => 3, 16 | ]; 17 | 18 | /** 19 | * @Transform :number 20 | */ 21 | public function transformNumber(string $number): int 22 | { 23 | if (is_numeric($number)) { 24 | return (int) $number; 25 | } 26 | if (!\array_key_exists($number, self::STRING_TO_INT)) { 27 | throw new \RuntimeException("Can't transform the number '$number' to integer. It's to big."); 28 | } 29 | 30 | return self::STRING_TO_INT[$number]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Behat/Context/Ui/ManagingShippingMethodsWithTableRateContext.php: -------------------------------------------------------------------------------- 1 | updatePage->getTableRateOptions($channel->getCode()); 29 | Assert::count($options, 1); 30 | Assert::eq($options[0]->getAttribute('value'), $shippingTableRate->getCode()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Behat/Context/Ui/ManagingTableRatesContext.php: -------------------------------------------------------------------------------- 1 | indexPage->open(); 35 | } 36 | 37 | /** 38 | * @Then I should see :number table rate(s) in the list 39 | */ 40 | public function iShouldSeeZeroTableRatesInTheList(int $number = 0) 41 | { 42 | Assert::same($number, $this->indexPage->countItems()); 43 | } 44 | 45 | /** 46 | * @Then I should see the :shippingTableRate table rate in the list 47 | */ 48 | public function iShouldSeeTheTableRateInTheList(ShippingTableRate $shippingTableRate) 49 | { 50 | Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $shippingTableRate->getName()])); 51 | } 52 | 53 | /** 54 | * @When I add a shipping table rate named :name for currency :currency 55 | */ 56 | public function iAddAShippingTableRateNamedForCurrency(string $name, CurrencyInterface $currency) 57 | { 58 | $this->createPage->open(); 59 | $this->createPage->fillCode(StringInflector::nameToUppercaseCode($name)); 60 | $this->createPage->fillName($name); 61 | $this->createPage->fillCurrency($currency); 62 | $this->createPage->addRate(random_int(1, 10), random_int(500, 1000)); 63 | $this->createPage->create(); 64 | } 65 | 66 | /** 67 | * @When I delete the :shippingTableRate table rate 68 | */ 69 | public function iDeleteTheTableRate(ShippingTableRate $shippingTableRate) 70 | { 71 | $this->indexPage->deleteResourceOnPage(['name' => $shippingTableRate->getName()]); 72 | } 73 | 74 | /** 75 | * @Given I want to modify the :shippingTableRate table rate 76 | */ 77 | public function iWantToModifyTheTableRate(ShippingTableRate $shippingTableRate): void 78 | { 79 | $this->updatePage->open(['id' => $shippingTableRate->getId()]); 80 | } 81 | 82 | /** 83 | * @When I save my changes 84 | */ 85 | public function iSaveMyChanges() 86 | { 87 | $this->updatePage->saveChanges(); 88 | } 89 | 90 | /** 91 | * @When I change its code to :code 92 | */ 93 | public function iChangeItsCodeTo(string $code) 94 | { 95 | $this->createPage->fillCode($code); 96 | } 97 | 98 | /** 99 | * @When I change its name to :name 100 | */ 101 | public function iChangeItsNameTo(string $name) 102 | { 103 | $this->createPage->fillName($name); 104 | } 105 | 106 | /** 107 | * @Then /^(this shipping table rate) name should be "([^"]+)"$/ 108 | */ 109 | public function thisShippingTableRateNameShouldBe(ShippingTableRate $shippingTableRate, string $code) 110 | { 111 | $this->updatePage->open(['id' => $shippingTableRate->getId()]); 112 | $this->updatePage->hasResourceValues(['name' => $code]); 113 | } 114 | 115 | /** 116 | * @When /^I add a new rate of ("[^"]+") for shipments up to (\d+) kg$/ 117 | */ 118 | public function iAddANewRateOfForShipmentsUpToKg(int $rate, int $weightLimit): void 119 | { 120 | $this->updatePage->addRate($rate, $weightLimit); 121 | } 122 | 123 | /** 124 | * @Then /^(this shipping table rate) should have (\d+) rates$/ 125 | */ 126 | public function thisShippingTableRateShouldHaveRates(ShippingTableRate $shippingTableRate, int $count) 127 | { 128 | $this->indexPage->open(); 129 | 130 | Assert::eq($this->indexPage->getTableRateRatesCount($shippingTableRate), $count); 131 | } 132 | 133 | /** 134 | * @When I try to add a new shipping table 135 | */ 136 | public function iTryToAddANewShippingTable() 137 | { 138 | $this->createPage->open(); 139 | } 140 | 141 | /** 142 | * @When I specify its code as :code 143 | */ 144 | public function iSpecifyItsCodeAs(string $code) 145 | { 146 | $this->createPage->fillCode($code); 147 | } 148 | 149 | /** 150 | * @When I specify its currency as :currency 151 | */ 152 | public function iSpecifyItsCurrencyAs(CurrencyInterface $currency) 153 | { 154 | $this->createPage->fillCurrency($currency); 155 | } 156 | 157 | /** 158 | * @When I do not specify its name 159 | */ 160 | public function iDoNotSpecifyItsName() 161 | { 162 | $this->createPage->fillName(''); 163 | } 164 | 165 | /** 166 | * @When I try to add it 167 | */ 168 | public function iTryToAddIt() 169 | { 170 | $this->createPage->create(); 171 | } 172 | 173 | /** 174 | * @Then I should be notified that :element is required 175 | */ 176 | public function iShouldBeNotifiedThatIsRequired($element) 177 | { 178 | Assert::same($this->createPage->getValidationMessage($element), 'This value should not be blank.'); 179 | } 180 | 181 | /** 182 | * @When I specify its name as :name 183 | */ 184 | public function iSpecifyItsNameAs(string $name) 185 | { 186 | $this->createPage->fillName($name); 187 | } 188 | 189 | /** 190 | * @When I do not specify its code 191 | */ 192 | public function iDoNotSpecifyItsCode() 193 | { 194 | $this->createPage->fillCode(''); 195 | } 196 | 197 | /** 198 | * @When I do not specify its currency 199 | */ 200 | public function iDoNotSpecifyItsCurrency() 201 | { 202 | $this->createPage->fillCurrency(null); 203 | } 204 | 205 | /** 206 | * @When I do not specify any rate 207 | */ 208 | public function iDoNotSpecifyAnyRate() 209 | { 210 | // Simply we don't add the rate 211 | } 212 | 213 | /** 214 | * @Then I should be notified that at least one rate is required 215 | */ 216 | public function iShouldBeNotifiedThatAtLeastOneRateIsRequired() 217 | { 218 | Assert::same( 219 | $this->createPage->getFormValidationMessage(), 220 | 'You should specify at least one rate for this table rate.', 221 | ); 222 | } 223 | 224 | /** 225 | * @Then the code field should be disabled 226 | */ 227 | public function theCodeFieldShouldBeDisabled() 228 | { 229 | Assert::true($this->updatePage->isCodeDisabled()); 230 | } 231 | 232 | /** 233 | * @Then the :shippingTableRate table rate should still have code :code 234 | */ 235 | public function theShippingTableRateShouldStillHaveCode(ShippingTableRate $shippingTableRate, string $code) 236 | { 237 | Assert::eq($shippingTableRate->getCode(), $code); 238 | } 239 | 240 | /** 241 | * @Then the currency field should be disabled 242 | */ 243 | public function theCurrencyFieldShouldBeDisabled() 244 | { 245 | Assert::true($this->updatePage->isCurrencyDisabled()); 246 | } 247 | 248 | /** 249 | * @When I change its currency to :currency 250 | */ 251 | public function iChangeItsCurrencyTo(CurrencyInterface $currency) 252 | { 253 | $this->createPage->fillCurrency($currency); 254 | } 255 | 256 | /** 257 | * @Then the :shippingTableRate table rate should still have :currency currency 258 | */ 259 | public function theTableRateShouldStillHaveCurrency( 260 | ShippingTableRate $shippingTableRate, 261 | CurrencyInterface $currency, 262 | ) { 263 | Assert::same($shippingTableRate->getCurrency()->getCode(), $currency->getCode()); 264 | } 265 | 266 | /** 267 | * @Then I should be notified that code has to be unique 268 | */ 269 | public function iShouldBeNotifiedThatCodeHasToBeUnique() 270 | { 271 | $this->createPage->getValidationMessage( 272 | 'code', 273 | 'There\'s another shipping table rate with the same code. The code has to be unique.', 274 | ); 275 | } 276 | 277 | /** 278 | * @Then I should be notified that the table rate couldn't be deleted because is already used by the :shippingMethod shipping method 279 | */ 280 | public function iShouldBeNotifiedThatTheTableRateCouldntBeDeletedBecauseIsAlreadyUsedByTheShippingMethod( 281 | ShippingMethod $shippingMethod, 282 | ) { 283 | Assert::contains( 284 | $this->indexPage->getValidationMessage(), 285 | 'The table rate cannot be deleted because is currently used by the following shipping methods: ' . 286 | $shippingMethod->getCode(), 287 | ); 288 | } 289 | 290 | /** 291 | * @Then the :shippingTableRate shipping table rate should still be there 292 | */ 293 | public function theShippingTableRateShouldStillBeThere(ShippingTableRate $shippingTableRate) 294 | { 295 | Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $shippingTableRate->getName()])); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /tests/Behat/Context/Ui/ShippingTableRateContext.php: -------------------------------------------------------------------------------- 1 | addressPage->open(); 29 | $this->addressPage->specifyBillingAddress($this->createDefaultAddress()); 30 | $this->addressPage->nextStep(); 31 | 32 | $this->selectShippingPage->verify(); 33 | Assert::true($this->selectShippingPage->hasNoShippingMethodsMessage()); 34 | } 35 | 36 | private function createDefaultAddress(): AddressInterface 37 | { 38 | /** @var AddressInterface $address */ 39 | $address = $this->addressFactory->createNew(); 40 | $address->setFirstName('John'); 41 | $address->setLastName('Doe'); 42 | $address->setCountryCode('US'); 43 | $address->setCity('North Bridget'); 44 | $address->setPostcode('93-554'); 45 | $address->setStreet('0635 Myron Hollow Apt. 711'); 46 | $address->setPhoneNumber('321123456'); 47 | 48 | return $address; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Behat/Page/ShippingMethod/UpdatePage.php: -------------------------------------------------------------------------------- 1 | getElement('table_rate', ['%channelCode%' => $channelCode])->findAll( 14 | 'css', 15 | 'option[value!=""]', 16 | ); 17 | } 18 | 19 | protected function getDefinedElements(): array 20 | { 21 | return array_merge( 22 | parent::getDefinedElements(), 23 | ['table_rate' => '#sylius_shipping_method_configuration_%channelCode%_table_rate'], 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Behat/Page/ShippingMethod/UpdatePageInterface.php: -------------------------------------------------------------------------------- 1 | 'form[name="webgriffe_sylius_table_rate_plugin_shipping_table_rate"]', 16 | 'code' => '#webgriffe_sylius_table_rate_plugin_shipping_table_rate_code', 17 | 'name' => '#webgriffe_sylius_table_rate_plugin_shipping_table_rate_name', 18 | 'currency' => '#webgriffe_sylius_table_rate_plugin_shipping_table_rate_currency', 19 | 'weightLimitToRate' => '#webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate', 20 | ]; 21 | } 22 | 23 | protected function getDefinedElements(): array 24 | { 25 | return array_merge( 26 | parent::getDefinedElements(), 27 | self::getCreateUpdatePageDefinedElements(), 28 | ); 29 | } 30 | 31 | public function fillCode(string $code) 32 | { 33 | $this->getDocument()->fillField('Code', $code); 34 | } 35 | 36 | public function fillName(string $name) 37 | { 38 | $this->getDocument()->fillField('Name', $name); 39 | } 40 | 41 | public function fillCurrency(?CurrencyInterface $currency) 42 | { 43 | $this->getDocument()->selectFieldOption('Currency', $currency ? $currency->getCode() : ''); 44 | } 45 | 46 | public function getFormValidationMessage(): string 47 | { 48 | return trim($this->getElement('form')->find('css', '.sylius-validation-error')->getText()); 49 | } 50 | 51 | public function addRate(int $rate, int $weightLimit) 52 | { 53 | $weightLimitToRateField = $this->getDocument()->findById( 54 | 'webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate', 55 | ); 56 | $addRateButton = $weightLimitToRateField->findLink('Add'); 57 | $addRateButton->click(); 58 | $item = $weightLimitToRateField->find('css', '[data-form-collection=item]:last-child'); 59 | $item->fillField('Weight limit', $weightLimit); 60 | $item->fillField('Rate', $rate / 100); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Behat/Page/TableRate/CreatePageInterface.php: -------------------------------------------------------------------------------- 1 | getTableAccessor(); 15 | $table = $this->getElement('table'); 16 | 17 | $row = $tableAccessor->getRowWithFields($table, ['code' => $shippingTableRate->getCode()]); 18 | $rates = $tableAccessor->getFieldFromRow($table, $row, 'rates_count'); 19 | 20 | return (int) $rates->getText(); 21 | } 22 | 23 | public function getValidationMessage(): string 24 | { 25 | return $this->getDocument()->find('css', '.sylius-flash-message.negative')->getText(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Behat/Page/TableRate/IndexPageInterface.php: -------------------------------------------------------------------------------- 1 | getDocument()->findById( 26 | 'webgriffe_sylius_table_rate_plugin_shipping_table_rate_weightLimitToRate', 27 | ); 28 | $addRateButton = $weightLimitToRateField->findLink('Add'); 29 | $addRateButton->click(); 30 | $item = $weightLimitToRateField->find('css', '[data-form-collection=item]:last-child'); 31 | $item->fillField('Weight limit', $weightLimit); 32 | $item->fillField('Rate', $rate / 100); 33 | } 34 | 35 | /** 36 | * @throws \Behat\Mink\Exception\ElementNotFoundException 37 | */ 38 | protected function getCodeElement(): NodeElement 39 | { 40 | return $this->getElement('code'); 41 | } 42 | 43 | /** 44 | * @throws \Behat\Mink\Exception\ElementNotFoundException 45 | */ 46 | public function isCurrencyDisabled(): bool 47 | { 48 | return $this->getElement('currency')->getAttribute('disabled') === 'disabled'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Behat/Page/TableRate/UpdatePageInterface.php: -------------------------------------------------------------------------------- 1 |