Skip to content

Commit

Permalink
Merge pull request #60 from jacquesbh/fixtures
Browse files Browse the repository at this point in the history
Implement fixtures
  • Loading branch information
etienne-monsieurbiz authored Jul 26, 2023
2 parents e109255 + 7e23bc8 commit 0452d8d
Show file tree
Hide file tree
Showing 17 changed files with 654 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ jobs:

- run: make install

- run: make sylius.fixtures.local

- run: make test.composer

- run: make test.phpcs
Expand Down
13 changes: 7 additions & 6 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@
'binary_operator_spaces' => true,
'blank_line_after_opening_tag' => true,
'blank_line_after_namespace' => true,
'blank_lines_before_namespace' => true,
'blank_line_before_statement' => true,
'braces' => [
'allow_single_line_closure' => true,
],
'cast_spaces' => true,
'class_attributes_separation' => true,
'class_definition' => [
Expand Down Expand Up @@ -88,7 +86,6 @@
'fully_qualified_strict_types' => true,
'function_declaration' => true,
'function_to_constant' => true,
'function_typehint_space' => true,
'general_phpdoc_tag_rename' => true,
'global_namespace_import' => [
'import_classes' => true,
Expand All @@ -115,6 +112,7 @@
'lowercase_static_reference' => true,
'magic_constant_casing' => true,
'method_argument_space' => true,
'modernize_strpos' => false,
'modernize_types_casting' => true,
'multiline_comment_opening_closing' => true,
'multiline_whitespace_before_semicolons' => [
Expand Down Expand Up @@ -181,6 +179,7 @@
'no_whitespace_in_blank_line' => true,
'non_printable_character' => true,
'normalize_index_brace' => true,
'nullable_type_declaration_for_default_null_value' => false,
'object_operator_without_whitespace' => true,
'ordered_imports' => [
'imports_order' => [
Expand Down Expand Up @@ -215,7 +214,9 @@
'phpdoc_order' => true,
'phpdoc_return_self_reference' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_separation' => ['groups' => [
['ORM\\*'], ['Assert\\*'],
]],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_tag_type' => true,
'phpdoc_to_comment' => false,
Expand All @@ -235,7 +236,6 @@
'self_accessor' => true,
'short_scalar_cast' => true,
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
'single_class_element_per_statement' => true,
'single_import_per_statement' => true,
'single_line_after_imports' => true,
Expand All @@ -252,6 +252,7 @@
'elements' => ['arrays'],
],
'trim_array_spaces' => true,
'type_declaration_spaces' => true,
'unary_operator_spaces' => true,
'visibility_required' => [
'elements' => [
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ test.twig: ## Validate Twig templates
### SYLIUS
### ¯¯¯¯¯¯

sylius: dependencies sylius.database sylius.fixtures sylius.assets ## Install Sylius
sylius: dependencies sylius.database sylius.fixtures sylius.assets messenger.setup ## Install Sylius
.PHONY: sylius

sylius.database: ## Setup the database
Expand All @@ -153,11 +153,17 @@ sylius.database: ## Setup the database
sylius.fixtures: ## Run the fixtures
${CONSOLE} sylius:fixtures:load -n default

sylius.fixtures.local: ## Run the local fixtures (for testing purpose)
${CONSOLE} sylius:fixtures:load -n local

sylius.assets: ## Install all assets with symlinks
${CONSOLE} assets:install --symlink
${CONSOLE} sylius:install:assets
${CONSOLE} sylius:theme:assets:install --symlink

messenger.setup: ## Setup Messenger transports
${CONSOLE} messenger:setup-transports

###
### PLATFORM
### ¯¯¯¯¯¯¯¯
Expand Down
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<h1 align="center">Settings for Sylius</h1>

[![Settings Plugin license](https://img.shields.io/github/license/monsieurbiz/SyliusSettingsPlugin?public)](https://github.com/monsieurbiz/SyliusSettingsPlugin/blob/master/LICENSE.txt)
[![Tests Status](https://img.shields.io/github/workflow/status/monsieurbiz/SyliusSettingsPlugin/Tests?logo=github)](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions?query=workflow%3ATests)
[![Security Status](https://img.shields.io/github/workflow/status/monsieurbiz/SyliusSettingsPlugin/Security?label=security&logo=github)](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions?query=workflow%3ASecurity)
[![Tests](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions/workflows/tests.yaml/badge.svg?branch=master&event=push)](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions/workflows/tests.yaml)
[![Security](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions/workflows/security.yaml/badge.svg?branch=master&event=push)](https://github.com/monsieurbiz/SyliusSettingsPlugin/actions/workflows/security.yaml)

This plugin gives you the ability to have Plugins oriented settings in your favorite e-commerce platform, Sylius.

Expand Down Expand Up @@ -101,6 +101,38 @@ use MonsieurBiz\SyliusSettingsPlugin\Provider\SettingsProviderInterface;
}
```

### Use fixtures

We've implemented a fixtures loader to help you to create your settings if you need to have different settings for your
tests or project (by channel, by locale…).

You need to create a yaml file with your fixtures, like explained in the documentation of Sylius.
You can find our own example in the source code, section `sylius_fixtures`: [configuration file](dist/config/packages/monsieurbiz_settings_plugin_custom.yaml).

It's also possible to run test fixtures with a local suite in development: `make sylius.fixtures.local`.

By default, a fixture will replace the value of a setting if it already exists.
If you want to keep a value as it is in the database when running this fixture, you can use the flag `ignore_if_exists: true` for each element that you want to be kept.

### Use CLI

You can use a CLI command to set a value for a setting directly from the console:
`$ ./bin/console monsieurbiz:settings:set {alias} {path} {value} --channel="FASHION_WEB" --locale="en_US" --type="text"`


Examples:
```bash
$ ./bin/console monsieurbiz:settings:set app.default demo_message 'fashion message' --channel="FASHION_WEB" --locale="en_US"
$ ./bin/console monsieurbiz:settings:set app.default demo_json '{"foo":"baz"}' --channel="FASHION_WEB" --locale="en_US" --type="json"
$ ./bin/console monsieurbiz:settings:set app.default demo_datetime '2023-07-24 01:02:03' --channel="FASHION_WEB" --locale="en_US" --type="datetime"
$ ./bin/console monsieurbiz:settings:set app.default enabled 0
```
The options channel and locale can be omitted if you want to set the value for a global scope.
If a value exists for the given scope the type can be omitted as it will be the same as the existing one unless you want to change the type.
For a new value you need to specify the type.

⚠️ When specifying the type, be sure to know what you are doing as it should be coherent with the Form Type of the field.

## Contributing

You can find a way to run the plugin without effort in the file [DEVELOPMENT.md](./DEVELOPMENT.md).
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,18 @@
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
},
"symfony": {
"allow-contrib": "true"
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"symfony/thanks": true,
"ergebnis/composer-normalize": true,
"symfony/flex": true
"symfony/flex": true,
"php-http/discovery": true
}
}
}
80 changes: 80 additions & 0 deletions dist/config/packages/monsieurbiz_settings_plugin_custom.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,83 @@ sylius_ui:
blocks:
demo_message:
template: '/views/message.html.twig'

sylius_fixtures:
suites:
default:
fixtures:
monsieurbiz_settings:
options:
custom:
-
alias: app.default
path: demo_message
channel: FASHION_WEB
locale: en_US
type: text
value: My amazing very fashion message
-
alias: app.default
path: demo_message
# no channel because we want the default
locale: fr_FR
type: text
value: Mon message vraiment très fashion
-
alias: app.default
path: demo_message
# no channel because we want the default
# no locale because we want the default
type: text
value: My very default message

local:
listeners:
logger: ~
fixtures:
monsieurbiz_settings:
options:
custom:
-
alias: app.default
path: test_bool
type: boolean
value: true
ignore_if_exists: true
-
alias: app.default
path: test_bool2
type: boolean
value: false
-
alias: app.default
path: test_json
type: json
value: |
{"foo":"bar"}
-
alias: app.default
path: test_json_array
type: json
value: {"foo":"baz"}
-
alias: app.default
path: test_int
type: integer
value: 42
-
alias: app.default
path: test_float
type: float
value: 13.37
-
alias: app.default
path: test_date
type: date
value: 2023-07-24
-
alias: app.default
path: test_datetime
type: datetime
value: 2023-07-24 01:02:03

137 changes: 137 additions & 0 deletions src/Command/SetSettingsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

/*
* This file is part of Monsieur Biz' Settings plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusSettingsPlugin\Command;

use Doctrine\ORM\EntityManagerInterface;
use Exception;
use MonsieurBiz\SyliusSettingsPlugin\Exception\SettingsException;
use MonsieurBiz\SyliusSettingsPlugin\Formatter\SettingsFormatterInterface;
use MonsieurBiz\SyliusSettingsPlugin\Provider\SettingProviderInterface;
use MonsieurBiz\SyliusSettingsPlugin\Settings\RegistryInterface;
use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface;
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class SetSettingsCommand extends Command
{
private const ARGUMENT_ALIAS = 'alias';

private const ARGUMENT_PATH = 'path';

private const OPTION_CHANNEL = 'channel';

private const OPTION_LOCALE = 'locale';

private const OPTION_TYPE = 'type';

private const ARGUMENT_VALUE = 'value';

private RegistryInterface $settingsRegistry;

private ChannelRepositoryInterface $channelRepository;

private EntityManagerInterface $settingManager;

private SettingsFormatterInterface $settingsFormatter;

private SettingProviderInterface $settingProvider;

protected static $defaultName = 'monsieurbiz:settings:set';

public function __construct(
RegistryInterface $settingsRegistry,
ChannelRepositoryInterface $channelRepository,
EntityManagerInterface $settingManager,
SettingsFormatterInterface $settingsFormatter,
SettingProviderInterface $settingProvider,
string $name = null
) {
$this->settingsRegistry = $settingsRegistry;
$this->channelRepository = $channelRepository;
$this->settingManager = $settingManager;
$this->settingsFormatter = $settingsFormatter;
$this->settingProvider = $settingProvider;
parent::__construct($name);
}

protected function configure(): void
{
$this
->setDescription('Set a settings value for a given path')
->setHelp('This command allows you to set a settings value for a given path')
->addArgument(self::ARGUMENT_ALIAS, InputArgument::REQUIRED, 'Alias of the settings like {vendor}.{plugin} from the setting definition')
->addArgument(self::ARGUMENT_PATH, InputArgument::REQUIRED, 'Path of the settings')
->addArgument(self::ARGUMENT_VALUE, InputArgument::REQUIRED, 'Value of the settings')
->addOption(self::OPTION_TYPE, 't', InputOption::VALUE_OPTIONAL, 'Type of the settings', null)
->addOption(self::OPTION_CHANNEL, 'c', InputOption::VALUE_OPTIONAL, 'Channel code')
->addOption(self::OPTION_LOCALE, 'l', InputOption::VALUE_OPTIONAL, 'Locale code')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
/** @var string $alias */
$alias = $input->getArgument(self::ARGUMENT_ALIAS);
/** @var string $path */
$path = $input->getArgument(self::ARGUMENT_PATH);
$channelCode = $input->getOption(self::OPTION_CHANNEL);
/** @var ?string $locale */
$locale = $input->getOption(self::OPTION_LOCALE);

$channel = null;
if (null !== $channelCode) {
/** @var ?ChannelInterface $channel */
$channel = $this->channelRepository->findOneBy(['code' => $channelCode]);
}

/** @var ?SettingsInterface $settings */
$settings = $this->settingsRegistry->getByAlias($alias);

if (null === $settings) {
throw new SettingsException(sprintf('The alias "%s" is not valid.', $alias));
}

['vendor' => $vendor, 'plugin' => $plugin] = $settings->getAliasAsArray();
$setting = $this->settingProvider->getSettingOrCreateNew($vendor, $plugin, $path, $locale, $channel);

/** @var string $type */
$type = $input->getOption(self::OPTION_TYPE) ?? $setting->getStorageType();
$this->settingProvider->validateType($type);

$value = $input->getArgument(self::ARGUMENT_VALUE);

$this->settingProvider->resetExistingValue($setting);
$setting->setStorageType($type);
/** @phpstan-ignore-next-line */
$setting->setValue($this->settingsFormatter->formatValue($type, $value));

$this->settingManager->persist($setting);
$this->settingManager->flush();
} catch (Exception $e) {
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));

return Command::FAILURE;
}

$output->writeln(sprintf('<info>%s</info>', 'The setting has been saved'));

return Command::SUCCESS;
}
}
Loading

0 comments on commit 0452d8d

Please sign in to comment.