├── .gitignore ├── Exception ├── SettingsException.php ├── UnknownSettingException.php ├── UnknownSerializerException.php └── WrongScopeException.php ├── .editorconfig ├── phpstan.neon.dist ├── Entity ├── SettingsOwnerInterface.php └── Setting.php ├── Resources ├── config │ ├── routing.yml │ └── services.yml ├── translations │ ├── settings.en.yml │ ├── settings.es.yml │ ├── settings.it.yml │ ├── settings.ru.yml │ ├── settings.uk.yml │ ├── settings.sv.yml │ ├── settings.nl.yml │ ├── settings.de.yml │ └── settings.fr.yml ├── views │ ├── Settings │ │ └── manage.html.twig │ └── layout.html.twig └── doc │ ├── faq.md │ ├── customization.md │ ├── i18n.md │ ├── scopes.md │ ├── installation.md │ ├── advanced-configuration.md │ └── general-usage.md ├── Tests ├── Resources │ └── app │ │ └── config │ │ └── config.yml ├── Serializer │ └── CustomSerializer.php ├── ServiceTest.php ├── SerializerTest.php ├── Functional │ └── ServiceInstantiationTest.php ├── AbstractTest.php ├── CachedSettingsManagerTest.php └── SettingsManagerTest.php ├── DmishhSettingsBundle.php ├── Serializer ├── SerializerInterface.php ├── PhpSerializer.php ├── JsonSerializer.php └── SerializerFactory.php ├── .php_cs ├── Makefile ├── Twig └── SettingsExtension.php ├── LICENSE ├── phpunit.xml.dist ├── Manager ├── SettingsManagerInterface.php ├── CachedSettingsManager.php └── SettingsManager.php ├── UPGRADE.md ├── CHANGELOG.md ├── composer.json ├── .github └── workflows │ └── main.yml ├── README.md ├── DependencyInjection ├── DmishhSettingsExtension.php └── Configuration.php ├── Form └── Type │ └── SettingsType.php ├── Controller └── SettingsController.php └── phpstan-baseline.neon /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | phpunit.xml 4 | .php_cs.cache 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /Exception/SettingsException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class DmishhSettingsBundle extends Bundle 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /Exception/UnknownSettingException.php: -------------------------------------------------------------------------------- 1 | setRules(array( 5 | '@Symfony' => true, 6 | '@Symfony:risky' => true, 7 | 'no_superfluous_phpdoc_tags' => true, 8 | )) 9 | ->setRiskyAllowed(true) 10 | ->setFinder( 11 | PhpCsFixer\Finder::create() 12 | ->in(__DIR__) 13 | ->exclude('vendor') 14 | ->name('*.php') 15 | ) 16 | ; 17 | -------------------------------------------------------------------------------- /Resources/translations/settings.nl.yml: -------------------------------------------------------------------------------- 1 | settings: Instellingen 2 | save: Opslaan 3 | settings_updated: Instellingen zijn succesvolg opgeslagen! 4 | not_allowed_to_edit_global_settings: Je bent niet toegestaan om globale instellingen te wijzigen. 5 | must_be_logged_in_to_edit_own_settings: Je dient ingelogd te zijn om je instellingen te wijzigen. 6 | not_allowed_to_edit_own_settings: Je bent niet toegestaan om je instellingen te wijzigen. 7 | -------------------------------------------------------------------------------- /Resources/views/Settings/manage.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'DmishhSettingsBundle::layout.html.twig' %} 2 | 3 | {% block content %} 4 |

{% trans from 'settings' %}settings{% endtrans %}

5 | 6 | {{ form_start(settings_form) }} 7 | {{ form_widget(settings_form) }} 8 | 9 | {{ form_end(settings_form) }} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /Resources/translations/settings.de.yml: -------------------------------------------------------------------------------- 1 | settings: Einstellungen 2 | save: Speichern 3 | settings_updated: Einstellungen wurden erfolgreich aktualisiert! 4 | not_allowed_to_edit_global_settings: Sie dürfen nicht bearbeiten globale einstellungen 5 | must_be_logged_in_to_edit_own_settings: Sie müssen eingeloggt sein, um ihre einstellungen zu bearbeiten 6 | not_allowed_to_edit_own_settings: Sie sind nicht zulässig, um ihre einstellungen zu bearbeiten 7 | -------------------------------------------------------------------------------- /Resources/translations/settings.fr.yml: -------------------------------------------------------------------------------- 1 | settings: Les paramètres de 2 | save: Enregistrer 3 | settings_updated: Les paramètres ont été correctement mis à jour! 4 | not_allowed_to_edit_global_settings: Vous n'êtes pas autorisé à modifier des paramètres globaux 5 | must_be_logged_in_to_edit_own_settings: Vous devez être connecté afin de modifier vos paramètres 6 | not_allowed_to_edit_own_settings: Vous n'êtes pas autorisé à modifier vos paramètres 7 | -------------------------------------------------------------------------------- /Tests/Serializer/CustomSerializer.php: -------------------------------------------------------------------------------- 1 | phpstan-baseline.neon 9 | 10 | test: 11 | composer update --prefer-dist --no-interaction ${COMPOSER_PARAMS} 12 | vendor/bin/phpunit 13 | 14 | test-lowest: 15 | COMPOSER_PARAMS='--prefer-lowest' $(MAKE) test 16 | 17 | codestyle: 18 | docker run --rm -v $(DIR):/project -w /project $(QA_IMAGE) php-cs-fixer fix 19 | -------------------------------------------------------------------------------- /Resources/views/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% for flashMessage in app.session.flashbag.get('error') %} 10 |
{{ flashMessage }}
11 | {% endfor %} 12 | {% for flashMessage in app.session.flashbag.get('success') %} 13 |
{{ flashMessage }}
14 | {% endfor %} 15 | {% block content %}{% endblock %} 16 | 17 | 18 | -------------------------------------------------------------------------------- /Resources/doc/faq.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * [General usage](general-usage.md) 5 | * [Scopes](scopes.md) 6 | * [Advanced configuration](advanced-configuration.md) 7 | * [I18n](i18n.md) 8 | * [Customization](customization.md) 9 | * **FAQ** 10 | 11 | ## FAQ 12 | 13 | **? How to add optional setting?** 14 | 15 | Add `required: false` to setting validation options 16 | 17 | ```yaml 18 | dmishh_settings: 19 | settings: 20 | my_first_setting: 21 | options: 22 | required: false 23 | ``` 24 | 25 | **? How to add an `array` setting?** 26 | 27 | TODO 28 | 29 | **? How to inject `settings_manager` into form?** 30 | 31 | TODO 32 | -------------------------------------------------------------------------------- /Twig/SettingsExtension.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class SettingsExtension extends AbstractExtension 15 | { 16 | private $settingsManager; 17 | 18 | public function __construct(SettingsManagerInterface $settingsManager) 19 | { 20 | $this->settingsManager = $settingsManager; 21 | } 22 | 23 | public function getFunctions() 24 | { 25 | return [ 26 | new TwigFunction('get_setting', [$this->settingsManager, 'get']), 27 | new TwigFunction('get_all_settings', [$this->settingsManager, 'all']), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Resources/doc/customization.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * [General usage](general-usage.md) 5 | * [Scopes](scopes.md) 6 | * [Advanced configuration](advanced-configuration.md) 7 | * [I18n](i18n.md) 8 | * **Customization** 9 | * [FAQ](faq.md) 10 | 11 | ## Customization 12 | 13 | #### Overriding layout via bundle inheritance 14 | 15 | Make use of Symfony Bundle Inheritance to expose the bundle via your own template. See the Symfony Cookbook for an 16 | article about how to override parts of a bundle. 17 | 18 | * [How to Use Bundle Inheritance to Override Parts of a Bundle »](http://symfony.com/doc/current/cookbook/bundles/inheritance.html#overriding-resources-templates-routing-etc) 19 | 20 | #### Overriding template 21 | 22 | The template the bundle controller will use can be overwritten by changing the configuration parameter. 23 | 24 | ```yaml 25 | dmishh_settings: 26 | template: DmishhSettingsBundle:Settings:manage.html.twig # change to your own 27 | ``` 28 | 29 | 30 | #### Overriding controller 31 | 32 | TODO 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | © 2013 Dmitriy Scherbina 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | ./Tests 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ./ 27 | 28 | ./Resources 29 | ./Tests 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Serializer/SerializerFactory.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class ServiceTest extends AbstractExtensionTestCase 15 | { 16 | protected function getContainerExtensions(): array 17 | { 18 | return [ 19 | new DmishhSettingsExtension(), 20 | ]; 21 | } 22 | 23 | public function testAlias() 24 | { 25 | $this->load(); 26 | $this->assertContainerBuilderHasAlias(SettingsManagerInterface::class, SettingsManager::class); 27 | } 28 | 29 | /** 30 | * If we provide a cache_service we should use the CachedSettingsManager as default. 31 | */ 32 | public function testCacheServiceAlias() 33 | { 34 | $this->load(['cache_service' => 'cache']); 35 | $this->assertContainerBuilderHasAlias(SettingsManagerInterface::class, CachedSettingsManager::class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | Dmishh\SettingsBundle\Manager\SettingsManagerInterface: '@Dmishh\SettingsBundle\Manager\SettingsManager' 3 | 4 | Dmishh\SettingsBundle\Manager\SettingsManager: 5 | arguments: [ '@doctrine.orm.entity_manager', '@Dmishh\SettingsBundle\Serializer\SerializerInterface', ~ ] 6 | 7 | Dmishh\SettingsBundle\Manager\CachedSettingsManager: 8 | arguments: [ '@Dmishh\SettingsBundle\Manager\SettingsManager', ~, ~ ] 9 | 10 | Dmishh\SettingsBundle\Serializer\SerializerInterface: 11 | class: Dmishh\SettingsBundle\Serializer\PhpSerializer 12 | factory: Dmishh\SettingsBundle\Serializer\SerializerFactory::create 13 | arguments: ['%settings_manager.serialization%'] 14 | 15 | Dmishh\SettingsBundle\Form\Type\SettingsType: 16 | arguments: [ ~ ] 17 | tags: 18 | - { name: form.type } 19 | 20 | Dmishh\SettingsBundle\Twig\SettingsExtension: 21 | arguments: [ '@Dmishh\SettingsBundle\Manager\SettingsManagerInterface' ] 22 | tags: 23 | - { name: twig.extension } 24 | 25 | Dmishh\SettingsBundle\Controller\SettingsController: 26 | arguments: 27 | - '@translator' 28 | - '@Dmishh\SettingsBundle\Manager\SettingsManagerInterface' 29 | - ~ # template 30 | - ~ # manage own settings 31 | - ~ # security role 32 | -------------------------------------------------------------------------------- /Resources/doc/i18n.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * [General usage](general-usage.md) 5 | * [Scopes](scopes.md) 6 | * [Advanced configuration](advanced-configuration.md) 7 | * **I18n** 8 | * [Customization](customization.md) 9 | * [FAQ](faq.md) 10 | 11 | ## I18n 12 | 13 | #### Define custom settings names 14 | 15 | 1. Create _yml_ or _xliff_ file for domain _settings_ (example: _settings.en.yml_) in any of your bundles or directly in _app/Resources_ (note: your bundle must be activated after _DmishhSettingsBundle_ in _AppKernel.php_) 16 | 1. Add your settings translations like in the following example for _yml_ format: 17 | 18 | ```yaml 19 | labels: 20 | my_custom_setting: My Custom Label 21 | profile_update_interval: Profile update interval 22 | ``` 23 | 24 | Clear your cache with ```app/console cache:clear``` 25 | 26 | #### Provide translations for choice type 27 | 28 | 1. Create, if not yet, _yml_ or _xliff_ file for domain _settings_ (example: _settings.en.yml_) in any of your bundles or directly in _app/Resources_ (note: your bundle must be activated after _DmishhSettingsBundle_ in _AppKernel.php_) 29 | 1. Add your choices translations like in the following example for _yml_ format (add _choice postfix to your setting's name): 30 | 31 | ```yaml 32 | labels: 33 | gender: Gender 34 | gender_choices: 35 | m: Male 36 | f: Female 37 | ``` 38 | 39 | Clear your cache with ```app/console cache:clear``` 40 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrade 2 | 3 | ## Upgrade from 1.0.x to 2.0.0 4 | 5 | Your User entity should implement the `Dmishh\SettingsBundle\Entity\SettingsOwnerInterface` interface. In order to maintain 6 | your user setting you need to implement the `getSettingIdentifier` to return the username. 7 | 8 | ``` php 9 | class MyUser implements SettingsOwnerInterface 10 | { 11 | // .. 12 | 13 | public function getSettingIdentifier() 14 | { 15 | return $this->getUsername(); 16 | } 17 | } 18 | ``` 19 | 20 | You do also need to update your database tables. 21 | 22 | * Via [DoctrineMigrationsBundle](http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html):** 23 | 24 | ```bash 25 | php app/console doctrine:migrations:diff 26 | php app/console doctrine:migrations:migrate 27 | ``` 28 | 29 | * Using Doctrine schema update tool 30 | 31 | ```bash 32 | php app/console doctrine:schema:update --force 33 | ``` 34 | 35 | * The following queries should be executed: 36 | 37 | ``` sql 38 | DROP INDEX name_user_name_idx ON dmishh_settings; 39 | ALTER TABLE dmishh_settings CHANGE username ownerId VARCHAR(255) DEFAULT NULL; 40 | CREATE INDEX name_owner_id_idx ON dmishh_settings (name, ownerId); 41 | ``` 42 | 43 | ### Namespace changed 44 | 45 | The "\Bundle" part of the namespace has been removed between `2.0.0-beta1` and `2.0.0-beta2`. The use statements 46 | and `AppKernel` bundle declaration should be changed: 47 | 48 | * Old: `Dmishh\Bundle\SettingsBundle` 49 | * New: `Dmishh\SettingsBundle` 50 | -------------------------------------------------------------------------------- /Tests/SerializerTest.php: -------------------------------------------------------------------------------- 1 | '123', 123, 5.0]; 10 | 11 | public function testPhpSerializer() 12 | { 13 | $serializer = SerializerFactory::create('php'); 14 | $this->assertEquals(serialize(self::$testData), $serializer->serialize(self::$testData)); 15 | $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); 16 | } 17 | 18 | public function testJsonSerializer() 19 | { 20 | $serializer = SerializerFactory::create('json'); 21 | $this->assertEquals(json_encode(self::$testData), $serializer->serialize(self::$testData)); 22 | $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); 23 | } 24 | 25 | public function testCustomSerializer() 26 | { 27 | $serializer = SerializerFactory::create('Dmishh\SettingsBundle\Tests\Serializer\CustomSerializer'); 28 | $this->assertEquals(self::$testData, $serializer->unserialize($serializer->serialize(self::$testData))); 29 | } 30 | 31 | public function testUnknownSerializer() 32 | { 33 | $this->expectException('\Dmishh\SettingsBundle\Exception\UnknownSerializerException'); 34 | $serializer = SerializerFactory::create('unknown_serializer'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | #### Version 3.0.0-beta3 4 | 5 | * Run tests using Github Actions, and test lowest set of dependencies #121 6 | * Support PHP8.0 #117 7 | * Fix PHPUnit deprecations #120 8 | 9 | #### Version 3.0.0-beta2 10 | 11 | * Documentation and bugfixes 12 | 13 | #### Version 3.0.0-beta1 14 | 15 | * Added support for Symfony 4 & 5 16 | * Added FCQN as service names 17 | * Added PHP 7 type hints 18 | * Dropped support for Symfony < 3.4 19 | * Dropped support for PHP < 7.2 20 | 21 | #### Version 2.0.0-dev 22 | 23 | * Added optional caching 24 | * New interface for your entity. We are no longer using `UserInterface`. Use `SettingsOwnerInterface` instead. 25 | * Changed behavior of `SettingsManager::all`. It will not return global config if the user/local values are missing 26 | * Added possibility to add default value as third parameter on `SettingsManager::get` 27 | * Updated namespace to `Dmishh\SettingsBundle` instead of `Dmishh\Bundle\SettingsBundle` 28 | * Updated the configuration. This break BC but makes sure the configuration is not as "deep". [#31](https://github.com/dmishh/SettingsBundle/issues/31) 29 | * Bump PHP to `^5.5.9` and Symfony to `^2.7|^3.0` [#50](https://github.com/dmishh/SettingsBundle/issues/50) 30 | 31 | #### Version 1.0.2-1.0.7 (9 Mar 2015) 32 | * Minor code improvements and bug fixes 33 | * System messages translations to en, it, es, fr, de, ru, uk, sv languages 34 | 35 | #### Version 1.0.1 (13 May 2014) 36 | * Ability to choose serialization mechanism (php or json) 37 | * Ability to add constraints to validation 38 | 39 | #### Version 1.0.0 (3 Apr 2014) 40 | * First stable version 41 | -------------------------------------------------------------------------------- /Entity/Setting.php: -------------------------------------------------------------------------------- 1 | id; 46 | } 47 | 48 | public function setName(?string $name) 49 | { 50 | $this->name = $name; 51 | } 52 | 53 | public function getName(): ?string 54 | { 55 | return $this->name; 56 | } 57 | 58 | public function setValue(?string $value): void 59 | { 60 | $this->value = $value; 61 | } 62 | 63 | public function getValue(): ?string 64 | { 65 | return $this->value; 66 | } 67 | 68 | public function getOwnerId(): ?string 69 | { 70 | return $this->ownerId; 71 | } 72 | 73 | public function setOwnerId(?string $ownerId) 74 | { 75 | $this->ownerId = $ownerId; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/Functional/ServiceInstantiationTest.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new PublicServicePass('|Dmishh.*|')); 25 | } 26 | 27 | public function testInitBundle() 28 | { 29 | $kernel = $this->createKernel(); 30 | $kernel->addConfigFile(\dirname(__DIR__).'/Resources/app/config/config.yml'); 31 | $kernel->addBundle(DoctrineBundle::class); 32 | $this->bootKernel(); 33 | $container = $this->getContainer(); 34 | 35 | // Test if you services exists 36 | self::assertTrue($container->has(SerializerInterface::class)); 37 | $service = $container->get(SerializerInterface::class); 38 | self::assertInstanceOf(PhpSerializer::class, $service); 39 | 40 | $service = $container->get(SettingsManagerInterface::class); 41 | self::assertInstanceOf(SettingsManager::class, $service); 42 | } 43 | 44 | protected function getBundleClass() 45 | { 46 | return DmishhSettingsBundle::class; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dmishh/settings-bundle", 3 | "description": "Database centric Symfony configuration management. Global and per-user settings supported.", 4 | "keywords": ["symfony", "bundle", "config", "configuration", "settings"], 5 | "homepage": "https://github.com/dmishh/SettingsBundle", 6 | "type": "symfony-bundle", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Dmitriy Scherbina", 11 | "homepage": "http://dmishh.com" 12 | }, 13 | { 14 | "name": "Tobias Nyholm", 15 | "homepage": "https://github.com/Nyholm" 16 | }, 17 | { 18 | "name": "Richard van Laak", 19 | "homepage": "https://github.com/rvanlaak" 20 | } 21 | ], 22 | "require": { 23 | "php": "^7.2|^8.0", 24 | "psr/cache": "^1.0", 25 | "symfony/framework-bundle": "^3.4 || ^4.3 || ^5.0", 26 | "symfony/form": "^3.4 || ^4.3 || ^5.0", 27 | "doctrine/orm": "^2.6.3|^2.7" 28 | }, 29 | "require-dev": { 30 | "phpunit/phpunit": "^8.5", 31 | "mockery/mockery": "^1.3", 32 | "doctrine/doctrine-bundle": "^1.12 || ^2.0", 33 | "polishsymfonycommunity/symfony-mocker-container": "^1.0", 34 | "matthiasnoback/symfony-dependency-injection-test": "^4.1", 35 | "nyholm/symfony-bundle-test": "^1.6", 36 | "symfony/translation": "^4.4 || ^5.0", 37 | "symfony/security-core": "^4.4 || ^5.0", 38 | "twig/twig": "^2.0 || ^3.0" 39 | }, 40 | "autoload": { 41 | "psr-4": { "Dmishh\\SettingsBundle\\": "" } 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "3.0.x-dev" 46 | } 47 | }, 48 | "suggest": { 49 | "cache/adapter-bundle": "This bundle will help you to add your PSR-6 cache implementations as Symfony services" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/AbstractTest.php: -------------------------------------------------------------------------------- 1 | em = $this->createEntityManager(); 22 | $this->generateSchema(); 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | protected function tearDown(): void 29 | { 30 | $this->em->close(); 31 | } 32 | 33 | protected function createEntityManager() 34 | { 35 | $config = new Configuration(); 36 | $config->setProxyDir(sys_get_temp_dir()); 37 | $config->setProxyNamespace('EntityProxy'); 38 | $config->setAutoGenerateProxyClasses(true); 39 | 40 | AnnotationRegistry::registerFile( 41 | __DIR__. 42 | '/../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php' 43 | ); 44 | $driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver( 45 | new \Doctrine\Common\Annotations\AnnotationReader(), 46 | [__DIR__.'/../Entity'] 47 | ); 48 | $config->setMetadataDriverImpl($driver); 49 | 50 | $conn = [ 51 | 'driver' => 'pdo_sqlite', 52 | 'memory' => true, 53 | ]; 54 | 55 | $em = \Doctrine\ORM\EntityManager::create($conn, $config); 56 | 57 | return $em; 58 | } 59 | 60 | protected function generateSchema() 61 | { 62 | $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); 63 | 64 | if (!empty($metadatas)) { 65 | $tool = new \Doctrine\ORM\Tools\SchemaTool($this->em); 66 | $tool->dropSchema($metadatas); 67 | $tool->createSchema($metadatas); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Resources/doc/scopes.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * [General usage](general-usage.md) 5 | * **Scopes** 6 | * [Advanced configuration](advanced-configuration.md) 7 | * [I18n](i18n.md) 8 | * [Customization](customization.md) 9 | * [FAQ](faq.md) 10 | 11 | ## Understanding scopes 12 | 13 | Bundle provides settings separation into 3 scopes: `ALL`, `GLOBAL` and `USER`. 14 | 15 | * GLOBAL and USER scopes are totally independent. 16 | * ALL scope provides you to inherit global settings when user setting with the same name is not setted. 17 | 18 | Examples must give more clearance: 19 | 20 | ```php 21 | set('all_scope_setting', 'value'); 25 | $settingsMaanger->get('all_scope_setting'); // => 'value' 26 | $settingsMaanger->get('all_scope_setting', $this->getUser()); // => 'value' 27 | $settingsMaanger->set('all_scope_setting', 'user_value', $this->getUser()); 28 | $settingsMaanger->get('all_scope_setting', $this->getUser()); // => 'user_value' 29 | 30 | // Example #1 with GLOBAL and USER scopes 31 | $settingsMaanger->set('global_scope_setting', 'value'); 32 | $settingsMaanger->get('global_scope_setting'); // => 'value' 33 | $settingsMaanger->get('global_scope_setting', $this->getUser()); // => WrongScopeException 34 | $settingsMaanger->set('global_scope_setting', 'value', $this->getUser()); // => WrongScopeException 35 | 36 | // Example #2 with GLOBAL and USER scopes 37 | $settingsMaanger->set('user_scope_setting', 'value', $this->getUser()); 38 | $settingsMaanger->get('user_scope_setting', $this->getUser()); // => 'value' 39 | $settingsMaanger->get('user_scope_setting'); // => WrongScopeException 40 | $settingsMaanger->set('user_scope_setting', 'value'); // => WrongScopeException 41 | ``` 42 | 43 | #### Configuring scope 44 | 45 | You may configure a scope to each of your settings. You can use ALL (default), GLOBAL or USER scope. 46 | 47 | ```yaml 48 | dmishh_settings: 49 | settings: 50 | my_first_user_setting: 51 | scope: user # all, global 52 | ``` 53 | -------------------------------------------------------------------------------- /Resources/doc/installation.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * **Installation** 4 | * [General usage](general-usage.md) 5 | * [Scopes](scopes.md) 6 | * [Advanced configuration](advanced-configuration.md) 7 | * [I18n](i18n.md) 8 | * [Customization](customization.md) 9 | * [FAQ](faq.md) 10 | 11 | ## Installation (using Composer) 12 | 13 | * Add the following to your `composer.json` file: 14 | 15 | ```js 16 | // composer.json 17 | { 18 | "require": { 19 | // ... 20 | "dmishh/settings-bundle": "2.0.*@dev" 21 | } 22 | } 23 | ``` 24 | 25 | * Update dependencies, run from command line: 26 | 27 | ```bash 28 | php composer.phar update 29 | ``` 30 | 31 | * Register the bundle in your ``AppKernel.php`` file: 32 | 33 | ```php 34 | http://YOUR-PROJECT-URL/app_dev.php/settings/global and start managing your settings! 75 | 76 | **Note:** If you're using Symfony 3, please see the instructions in [Advanced configuration](advanced-configuration.md). 77 | 78 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ pull_request ] 4 | 5 | jobs: 6 | static-code-analysis: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Run PHPStan 11 | uses: docker://jakzal/phpqa 12 | with: 13 | args: phpstan analyze 14 | test: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | php: [ '7.2', '7.4', '8.0' ] 19 | symfony: [ '4.4.*', '5.3.*' ] 20 | name: Test on Symfony ${{ matrix.symfony }} with PHP ${{ matrix.php }} 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: ${{ matrix.php }} 26 | coverage: none # disable xdebug, pcov 27 | - run: composer require symfony/framework-bundle:${{ matrix.symfony }} --no-update 28 | - run: composer require symfony/form:${{ matrix.symfony }} --no-update 29 | - run: composer install 30 | - run: make test 31 | test-lowest: 32 | runs-on: ubuntu-latest 33 | strategy: 34 | matrix: 35 | php: [ '7.2', '7.3' ] 36 | symfony: [ '3.4.*', '4.4.*' ] 37 | name: Test lowest on Symfony ${{ matrix.symfony }} with PHP ${{ matrix.php }} 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: shivammathur/setup-php@v2 41 | with: 42 | php-version: ${{ matrix.php }} 43 | coverage: none # disable xdebug, pcov 44 | - run: composer require symfony/framework-bundle:${{ matrix.symfony }} --no-update 45 | - run: composer require symfony/form:${{ matrix.symfony }} --no-update 46 | - run: composer install 47 | - run: make test-lowest 48 | php-cs-fixer: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v1 52 | - name: Run PHP-CS-Fixer 53 | uses: docker://jakzal/phpqa 54 | with: 55 | args: php-cs-fixer fix --dry-run 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SettingsBundle 2 | ============== 3 | 4 | Bundle for storing configuration with Symfony in database using Doctrine ORM. 5 | 6 | 👀 This bundle was previously known as `dmissh/settings-bundle`, and the Packagist installation instruction will stay as is. 7 | 8 | ## Features 9 | 10 | * Easy-to-use (Twig extension, container service) 11 | * Settings scopes per user, global or all 12 | * Settings validation by using the Symfony Form Component 13 | * 2 serialization mechanisms: PHP `serialize()` and JSON (+ you can write your own) 14 | * Settings caching (PSR-6) 15 | * Fast and extensible 16 | 17 | ## Quick usage examples 18 | 19 | Symfony controller: 20 | 21 | ```php 22 | // Global settings 23 | $settingsManager->set('name', 'foo'); 24 | $settingsManager->get('name'); // returns 'foo' 25 | 26 | // User settings 27 | $settingsManager->get('name', $user); // returns global 'foo' 28 | $settingsManager->set('name', 'bar', $user); 29 | $settingsManager->get('name', $user); // returns 'bar' 30 | ``` 31 | 32 | Twig template: 33 | 34 | ```twig 35 | {# Global setting #} 36 | {{ get_setting('some_setting') }} {# => 'value' #} 37 | 38 | {# User setting #} 39 | {{ get_setting('some_user_setting', app.user) }} {# => 'value' #} 40 | ``` 41 | 42 | See the [general usage](/Resources/doc/general-usage.md) documentation for more examples. 43 | 44 | ## Documentation 45 | 46 | * [Installation](/Resources/doc/installation.md) 47 | * [General usage](/Resources/doc/general-usage.md) 48 | * [Scopes](/Resources/doc/scopes.md) 49 | * [Advanced configuration](/Resources/doc/advanced-configuration.md) 50 | * [I18n](/Resources/doc/i18n.md) 51 | * [Customization](/Resources/doc/customization.md) 52 | * [FAQ](/Resources/doc/faq.md) 53 | 54 | ## Changelog, Roadmap and contribution 55 | 56 | Please, do not hesitate to [report bugs](https://github.com/dmishh/SettingsBundle/issues) or send 57 | [pull requests](https://github.com/dmishh/SettingsBundle/pulls). It will help to motivate me to support 58 | library better than anything else :) 59 | 60 | See [CHANGELOG.md](CHANGELOG.md) for all major changes. 61 | 62 | ### Upgrade from 1.0.* 63 | 64 | Make sure to read the [UPGRADE.md](UPGRADE.md) to successfully migrate your application. 65 | 66 | ## License 67 | 68 | The MIT License. For the full text of license, please, see [LICENSE](/LICENSE) 69 | -------------------------------------------------------------------------------- /DependencyInjection/DmishhSettingsExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 30 | 31 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 32 | $loader->load('services.yml'); 33 | 34 | $container->setParameter('settings_manager.serialization', $config['serialization']); 35 | 36 | // Configure the correct storage 37 | if (null === $config['cache_service']) { 38 | $container->removeDefinition(CachedSettingsManager::class); 39 | } else { 40 | $container->getDefinition(CachedSettingsManager::class) 41 | ->replaceArgument(1, new Reference($config['cache_service'])) 42 | ->replaceArgument(2, $config['cache_lifetime']); 43 | 44 | // set an alias to make sure the cached settings manager is the default 45 | $container->setAlias(SettingsManagerInterface::class, CachedSettingsManager::class); 46 | } 47 | 48 | $container->getDefinition(SettingsManager::class) 49 | ->replaceArgument(2, $config['settings']); 50 | 51 | $container->getDefinition(SettingsType::class) 52 | ->replaceArgument(0, $config['settings']); 53 | 54 | $container->getDefinition(SettingsController::class) 55 | ->replaceArgument(2, $config['template']) 56 | ->replaceArgument(3, $config['security']['users_can_manage_own_settings']) 57 | ->replaceArgument(4, $config['security']['manage_global_settings_role']); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Form/Type/SettingsType.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Artem Zhuravlov 15 | */ 16 | class SettingsType extends AbstractType 17 | { 18 | protected $settingsConfiguration; 19 | 20 | public function __construct(array $settingsConfiguration) 21 | { 22 | $this->settingsConfiguration = $settingsConfiguration; 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function buildForm(FormBuilderInterface $builder, array $options) 29 | { 30 | foreach ($this->settingsConfiguration as $name => $configuration) { 31 | // If setting's value exists in data and setting isn't disabled 32 | if (\array_key_exists($name, $options['data']) && !\in_array($name, $options['disabled_settings'])) { 33 | $fieldType = $configuration['type']; 34 | $fieldOptions = $configuration['options']; 35 | $fieldOptions['constraints'] = $configuration['constraints']; 36 | 37 | // Validator constraints 38 | if (!empty($fieldOptions['constraints']) && \is_array($fieldOptions['constraints'])) { 39 | $constraints = []; 40 | foreach ($fieldOptions['constraints'] as $class => $constraintOptions) { 41 | if (class_exists($class)) { 42 | $constraints[] = new $class($constraintOptions); 43 | } else { 44 | throw new SettingsException(sprintf('Constraint class "%s" not found', $class)); 45 | } 46 | } 47 | 48 | $fieldOptions['constraints'] = $constraints; 49 | } 50 | 51 | // Label I18n 52 | $fieldOptions['label'] = 'labels.'.$name; 53 | $fieldOptions['translation_domain'] = 'settings'; 54 | 55 | // Choices I18n 56 | if (!empty($fieldOptions['choices'])) { 57 | $fieldOptions['choices'] = array_flip( 58 | array_map( 59 | function ($label) use ($fieldOptions) { 60 | return $fieldOptions['label'].'_choices.'.$label; 61 | }, 62 | array_combine($fieldOptions['choices'], $fieldOptions['choices']) 63 | ) 64 | ); 65 | } 66 | $builder->add($name, $fieldType, $fieldOptions); 67 | } 68 | } 69 | } 70 | 71 | public function configureOptions(OptionsResolver $resolver) 72 | { 73 | $resolver->setDefaults( 74 | [ 75 | 'disabled_settings' => [], 76 | ] 77 | ); 78 | } 79 | 80 | public function getBlockPrefix() 81 | { 82 | return 'settings_management'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Resources/doc/advanced-configuration.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * [General usage](general-usage.md) 5 | * [Scopes](scopes.md) 6 | * **Advanced configuration** 7 | * [I18n](i18n.md) 8 | * [Customization](customization.md) 9 | * [FAQ](faq.md) 10 | 11 | ## Advanced configuration 12 | 13 | Full list of options: 14 | 15 | ```yaml 16 | dmishh_settings: 17 | template: DmishhSettingsBundle:Settings:manage.html.twig 18 | cache_service: null 19 | cache_lifetime: 3600 20 | security: 21 | manage_global_settings_role: ROLE_USER 22 | users_can_manage_own_settings: true 23 | serialization: php # database serialization mechanism (php|json) 24 | settings: 25 | my_first_setting: 26 | scope: all # global or user 27 | type: number # any Symfony form type, or FQCN for Symfony >=3.0 28 | options: # options passed to form 29 | required: false 30 | constraints: 31 | Symfony\Component\Validator\Constraints\Range: 32 | min: 1 33 | max: 65535 34 | ``` 35 | 36 | **Note:** In Symfony 3, use the fully qualified class name instead of the form type name. 37 | 38 | 39 | #### Settings validation 40 | 41 | Settings validation uses [Symfony Forms Component](http://symfony.com/doc/current/book/forms.html#built-in-field-types). 42 | You just specify, for example, type *[text](http://symfony.com/doc/current/reference/forms/types/text.html)* and use it's options like *max_length*, etc. 43 | Also you can use [built-in](http://symfony.com/doc/current/reference/constraints.html) or [custom constraints](http://symfony.com/doc/current/cookbook/validation/custom_constraint.html). 44 | 45 | ```yaml 46 | dmishh_settings: 47 | settings: 48 | my_first_setting: 49 | type: text 50 | options: 51 | max_length: 15 52 | constraints: 53 | Symfony\Component\Validator\Constraints\Regex: 54 | pattern: "/^\d+$/" 55 | ``` 56 | 57 | __Note:__ [validation](#validation) is provided only at the form level. 58 | 59 | #### Security 60 | 61 | To protect settings modification bundle uses Symfony Security Component. 62 | You can limit global settings modification with ```manage_global_settings_role``` and grant access to authenticated users to modify their settings. 63 | 64 | ```yaml 65 | dmishh_settings: 66 | security: 67 | manage_global_settings_role: ROLE_USER 68 | users_can_manage_own_settings: true 69 | ``` 70 | 71 | #### Caching 72 | 73 | If you want to cache your settings you may provide a cache service that implements `Psr\Cache\CacheItemPoolInterface`. 74 | Every time you fetch a setting from the database we will cache it for `cache_lifetime` seconds. If you edit the 75 | setting we will automatically invalidate the cache. 76 | 77 | ```yaml 78 | dmishh_settings: 79 | cache_service: cache.provider.my_redis 80 | cache_lifetime: 3600 81 | 82 | # Using cache/adapter-bundle 83 | cache_adapter: 84 | providers: 85 | my_redis: 86 | factory: 'cache.factory.redis' 87 | ``` 88 | 89 | Read more about how you configure the cache adapter bundle on [www.php-cache.com](http://www.php-cache.com). 90 | 91 | -------------------------------------------------------------------------------- /Resources/doc/general-usage.md: -------------------------------------------------------------------------------- 1 | ## SettingsBundle 2 | 3 | * [Installation](installation.md) 4 | * **General usage** 5 | * [Scopes](scopes.md) 6 | * [Advanced configuration](advanced-configuration.md) 7 | * [I18n](i18n.md) 8 | * [Customization](customization.md) 9 | * [FAQ](faq.md) 10 | 11 | ## General usage 12 | 13 | * In controllers: 14 | 15 | ```php 16 | set('my_first_setting', 'value'); 22 | 23 | // Get setting value by its name 24 | $settingsManager->get('my_first_setting'); // => 'value' 25 | 26 | // Get all settings 27 | $settingsManager->all(); // => array('my_first_setting' => 'value') 28 | 29 | // Set settings' values from associative name-value array 30 | $settingsManager->setMany(array('my_first_setting' => 'new_value')); 31 | $this->get('settings_manager')->get('my_first_setting'); // => 'new_value'$settingsManager 32 | 33 | ``` 34 | 35 | ```php 36 | id; 47 | // } 48 | // } 49 | 50 | // These are same examples as above with only difference that they are for current user 51 | $settingsManager->set('my_first_setting', 'user_value', $this->getUser()); 52 | $settingsManager->get('my_first_setting', $this->getUser()); // => 'user_value' 53 | $settingsManager->all($this->getUser()); // array('my_first_setting' => 'user_value') 54 | $settingsManager->setMany(array('my_first_setting' => 'new_user_value'), $this->getUser()); 55 | $settingsManager->get('my_first_setting', $this->getUser()); // => 'new_user_value' 56 | 57 | 58 | // PER ENTITY SETTINGS 59 | 60 | // This is the most interesting part. You can have settings for any entity. 61 | // Just make sure you have unique values for getSettingIdentifier() 62 | 63 | // class Company implements SettingsOwnerInterface { 64 | // public function getSettingIdentifier() { 65 | // return 'company_' . $this->id; 66 | // } 67 | // } 68 | 69 | $myCompany = new Company(); 70 | $settingsManager->set('delivery_frequency_setting', 'daily', $myCompany); 71 | $settingsManager->get('delivery_frequency_setting', $this->getUser()); // => 'daily' 72 | ``` 73 | 74 | * In services: you must inject @settings_manager or the whole @service_container into your service and use it in the same way as in controllers (like in the example above) 75 | 76 | * In Twig templates: 77 | 78 | ```twig 79 | {# Global setting #} 80 | {{ get_setting('some_setting') }} {# => 'value' #} 81 | 82 | {# User setting #} 83 | {{ get_setting('some_user_setting', app.user) }} {# => 'value' #} 84 | 85 | {# Getting all global settings #} 86 | {% for setting in get_all_settings() %} 87 | {{ setting }} {# => 'value', ... #} 88 | {% endfor %} 89 | ``` 90 | -------------------------------------------------------------------------------- /Controller/SettingsController.php: -------------------------------------------------------------------------------- 1 | translator = $translator; 49 | $this->settingsManager = $settingsManager; 50 | $this->template = $template; 51 | $this->securityManageOwnSettings = $securityManageOwnSettings; 52 | $this->securityRole = $securityRole; 53 | } 54 | 55 | /** 56 | * @throws AccessDeniedException 57 | */ 58 | public function manageGlobalAction(Request $request): Response 59 | { 60 | if (null !== $this->securityRole && !$this->get('security.authorization_checker')->isGranted($this->securityRole)) { 61 | throw new AccessDeniedException($this->translator->trans('not_allowed_to_edit_global_settings', [], 'settings')); 62 | } 63 | 64 | return $this->manage($request); 65 | } 66 | 67 | /** 68 | * @throws AccessDeniedException 69 | */ 70 | public function manageOwnAction(Request $request): Response 71 | { 72 | if (null === $this->get('security.token_storage')->getToken()) { 73 | throw new AccessDeniedException($this->translator->trans('must_be_logged_in_to_edit_own_settings', [], 'settings')); 74 | } 75 | 76 | if (!$this->securityManageOwnSettings) { 77 | throw new AccessDeniedException($this->translator->trans('not_allowed_to_edit_own_settings', [], 'settings')); 78 | } 79 | 80 | $user = $this->get('security.token_storage')->getToken()->getUser(); 81 | if (!$user instanceof SettingsOwnerInterface) { 82 | //For this to work the User entity must implement SettingsOwnerInterface 83 | throw new AccessDeniedException(); 84 | } 85 | 86 | return $this->manage($request, $user); 87 | } 88 | 89 | protected function manage(Request $request, ?SettingsOwnerInterface $owner = null): Response 90 | { 91 | $form = $this->createForm(SettingsType::class, $this->settingsManager->all($owner)); 92 | $form->handleRequest($request); 93 | 94 | if ($form->isSubmitted() && $form->isValid()) { 95 | $this->settingsManager->setMany($form->getData(), $owner); 96 | $this->addFlash('success', $this->translator->trans('settings_updated', [], 'settings')); 97 | 98 | return $this->redirect($request->getUri()); 99 | } 100 | 101 | return $this->render($this->template, [ 102 | 'settings_form' => $form->createView(), 103 | ]); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('dmishh_settings'); 22 | } else { 23 | $rootNode = $treeBuilder->getRootNode(); 24 | } 25 | 26 | $scopes = [ 27 | SettingsManagerInterface::SCOPE_ALL, 28 | SettingsManagerInterface::SCOPE_GLOBAL, 29 | SettingsManagerInterface::SCOPE_USER, 30 | ]; 31 | 32 | $rootNode 33 | ->children() 34 | ->scalarNode('template') 35 | ->defaultValue('DmishhSettingsBundle:Settings:manage.html.twig') 36 | ->end() 37 | ->scalarNode('cache_service')->defaultNull()->info('A service implementing Psr\Cache\CacheItemPoolInterface')->end() 38 | ->integerNode('cache_lifetime')->defaultValue(3600)->end() 39 | ->arrayNode('security') 40 | ->addDefaultsIfNotSet() 41 | ->children() 42 | ->scalarNode('manage_global_settings_role')->defaultValue(null)->end() 43 | ->booleanNode('users_can_manage_own_settings')->defaultValue(true)->end() 44 | ->end() 45 | ->end() 46 | ->enumNode('serialization') 47 | ->defaultValue('php') 48 | ->values(['php', 'json']) 49 | ->end() 50 | ->arrayNode('settings') 51 | ->prototype('array') 52 | ->addDefaultsIfNotSet() 53 | ->children() 54 | ->scalarNode('scope') 55 | ->defaultValue('all') 56 | ->validate() 57 | ->ifNotInArray($scopes) 58 | ->thenInvalid('Invalid scope %s. Valid scopes are: '.implode(', ', array_map(function ($s) { return '"'.$s.'"'; }, $scopes)).'.') 59 | ->end() 60 | ->end() 61 | ->scalarNode('type')->defaultValue(TextType::class)->end() 62 | 63 | ->variableNode('options') 64 | ->info('The options given to the form builder') 65 | ->defaultValue([]) 66 | ->validate() 67 | ->always(function ($v) { 68 | if (!\is_array($v)) { 69 | throw new InvalidTypeException(); 70 | } 71 | 72 | return $v; 73 | }) 74 | ->end() 75 | ->end() 76 | ->variableNode('constraints') 77 | ->info('The constraints on this option. Example, use constraits found in Symfony\Component\Validator\Constraints') 78 | ->defaultValue([]) 79 | ->validate() 80 | ->always(function ($v) { 81 | if (!\is_array($v)) { 82 | throw new InvalidTypeException(); 83 | } 84 | 85 | return $v; 86 | }) 87 | ->end() 88 | ->end() 89 | ->end() 90 | ->end() 91 | ->end() 92 | ->end(); 93 | 94 | return $treeBuilder; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Manager/CachedSettingsManager.php: -------------------------------------------------------------------------------- 1 | settingsManager = $settingsManager; 33 | $this->storage = $storage; 34 | $this->cacheLifeTime = $cacheLifeTime; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function get(string $name, ?SettingsOwnerInterface $owner = null, $default = null) 41 | { 42 | if (null !== $cached = $this->fetchFromCache($name, $owner)) { 43 | return $cached; 44 | } 45 | 46 | $value = $this->settingsManager->get($name, $owner, $default); 47 | $this->storeInCache($name, $value, $owner); 48 | 49 | return $value; 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function all(?SettingsOwnerInterface $owner = null): array 56 | { 57 | if (null !== $cached = $this->fetchFromCache(null, $owner)) { 58 | return $cached; 59 | } 60 | 61 | $value = $this->settingsManager->all($owner); 62 | $this->storeInCache(null, $value, $owner); 63 | 64 | return $value; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function set(string $name, $value, ?SettingsOwnerInterface $owner = null): void 71 | { 72 | $this->invalidateCache($name, $owner); 73 | $this->invalidateCache(null, $owner); 74 | 75 | $this->settingsManager->set($name, $value, $owner); 76 | } 77 | 78 | /** 79 | * {@inheritdoc} 80 | */ 81 | public function setMany(array $settings, ?SettingsOwnerInterface $owner = null): void 82 | { 83 | foreach ($settings as $key => $value) { 84 | $this->invalidateCache($key, $owner); 85 | } 86 | $this->invalidateCache(null, $owner); 87 | 88 | $this->settingsManager->setMany($settings, $owner); 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | */ 94 | public function clear(string $name, ?SettingsOwnerInterface $owner = null): void 95 | { 96 | $this->invalidateCache($name, $owner); 97 | $this->invalidateCache(null, $owner); 98 | 99 | $this->settingsManager->clear($name, $owner); 100 | } 101 | 102 | /** 103 | * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise 104 | */ 105 | protected function invalidateCache(?string $name, ?SettingsOwnerInterface $owner = null): bool 106 | { 107 | return $this->storage->deleteItem($this->getCacheKey($name, $owner)); 108 | } 109 | 110 | /** 111 | * Get from cache. 112 | * 113 | * @return mixed|null if nothing was found in cache 114 | */ 115 | protected function fetchFromCache(?string $name, ?SettingsOwnerInterface $owner = null) 116 | { 117 | $cacheKey = $this->getCacheKey($name, $owner); 118 | 119 | return $this->storage->getItem($cacheKey)->get(); 120 | } 121 | 122 | /** 123 | * Store in cache. 124 | * 125 | * @param SettingsOwnerInterface $owner 126 | * 127 | * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise 128 | */ 129 | protected function storeInCache(?string $name, $value, ?SettingsOwnerInterface $owner = null): bool 130 | { 131 | $item = $this->storage->getItem($this->getCacheKey($name, $owner)) 132 | ->set($value) 133 | ->expiresAfter($this->cacheLifeTime); 134 | 135 | return $this->storage->save($item); 136 | } 137 | 138 | /** 139 | * @param SettingsOwnerInterface $owner 140 | */ 141 | protected function getCacheKey(?string $key, ?SettingsOwnerInterface $owner = null): string 142 | { 143 | return sprintf(self::PREFIX, $owner ? $owner->getSettingIdentifier() : '', $key); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Tests/CachedSettingsManagerTest.php: -------------------------------------------------------------------------------- 1 | createOwner(); 15 | $name = 'name'; 16 | $value = 'foobar'; 17 | $defaultValue = 'default'; 18 | 19 | $settingsManager = \Mockery::mock(SettingsManager::class); 20 | $settingsManager->shouldReceive('get')->once()->with($name, $owner, $defaultValue)->andReturn($value); 21 | 22 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 23 | ->setMethods(['fetchFromCache', 'storeInCache']) 24 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 25 | ->getMock(); 26 | $cachedSettingsManager->expects($this->once()) 27 | ->method('fetchFromCache') 28 | ->with($this->equalTo($name), $this->equalTo($owner)) 29 | ->willReturn(null); 30 | $cachedSettingsManager->expects($this->once()) 31 | ->method('storeInCache') 32 | ->with($this->equalTo($name), $this->equalTo($value), $this->equalTo($owner)) 33 | ->willReturn(false); 34 | 35 | $this->assertEquals($value, $cachedSettingsManager->get($name, $owner, $defaultValue)); 36 | } 37 | 38 | public function testGetHit() 39 | { 40 | $owner = $this->createOwner(); 41 | $name = 'name'; 42 | $value = 'foobar'; 43 | $defaultValue = 'default'; 44 | 45 | $settingsManager = \Mockery::mock(SettingsManager::class); 46 | 47 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 48 | ->setMethods(['fetchFromCache', 'storeInCache']) 49 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 50 | ->getMock(); 51 | $cachedSettingsManager->expects($this->once()) 52 | ->method('fetchFromCache') 53 | ->with($this->equalTo($name), $this->equalTo($owner)) 54 | ->willReturn($value); 55 | 56 | $this->assertEquals($value, $cachedSettingsManager->get($name, $owner, $defaultValue)); 57 | } 58 | 59 | public function testAll() 60 | { 61 | $owner = $this->createOwner(); 62 | $value = ['foo' => 'bar']; 63 | 64 | $settingsManager = \Mockery::mock(SettingsManager::class); 65 | $settingsManager->shouldReceive('all')->once()->with($owner)->andReturn($value); 66 | 67 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 68 | ->setMethods(['fetchFromCache', 'storeInCache']) 69 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 70 | ->getMock(); 71 | $cachedSettingsManager->expects($this->once()) 72 | ->method('fetchFromCache') 73 | ->with($this->equalTo(null), $this->equalTo($owner)) 74 | ->willReturn(null); 75 | $cachedSettingsManager->expects($this->once()) 76 | ->method('storeInCache') 77 | ->with($this->equalTo(null), $this->equalTo($value), $this->equalTo($owner)) 78 | ->willReturn(false); 79 | 80 | $this->assertEquals($value, $cachedSettingsManager->all($owner)); 81 | } 82 | 83 | public function testAllHit() 84 | { 85 | $owner = $this->createOwner(); 86 | $value = ['foo' => 'bar']; 87 | 88 | $settingsManager = \Mockery::mock(SettingsManager::class); 89 | 90 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 91 | ->setMethods(['fetchFromCache', 'storeInCache']) 92 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 93 | ->getMock(); 94 | $cachedSettingsManager->expects($this->once()) 95 | ->method('fetchFromCache') 96 | ->with($this->equalTo(null), $this->equalTo($owner)) 97 | ->willReturn($value); 98 | 99 | $this->assertEquals($value, $cachedSettingsManager->all($owner)); 100 | } 101 | 102 | public function testSet() 103 | { 104 | $owner = $this->createOwner(); 105 | $name = 'name'; 106 | $value = 'foobar'; 107 | 108 | $settingsManager = \Mockery::mock(SettingsManager::class); 109 | $settingsManager->shouldReceive('set')->once()->with($name, $value, $owner); 110 | 111 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 112 | ->setMethods(['invalidateCache']) 113 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 114 | ->getMock(); 115 | 116 | // Clear the cache 117 | $cachedSettingsManager->expects($this->at(0)) 118 | ->method('invalidateCache') 119 | ->with($this->equalTo($name), $this->equalTo($owner)) 120 | ->willReturn(true); 121 | 122 | // Clear all cache for this owner 123 | $cachedSettingsManager->expects($this->at(1)) 124 | ->method('invalidateCache') 125 | ->with($this->equalTo(null), $this->equalTo($owner)) 126 | ->willReturn(true); 127 | 128 | $cachedSettingsManager->set($name, $value, $owner); 129 | } 130 | 131 | public function testSetMany() 132 | { 133 | $owner = $this->createOwner(); 134 | $settings = ['name0' => 'value0', 'name1' => 'value1', 'name2' => 'value2']; 135 | 136 | $settingsManager = \Mockery::mock(SettingsManager::class); 137 | $settingsManager->shouldReceive('setMany')->once()->with($settings, $owner); 138 | 139 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 140 | ->setMethods(['invalidateCache']) 141 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 142 | ->getMock(); 143 | $cachedSettingsManager->expects($this->exactly(4)) 144 | ->method('invalidateCache') 145 | ->with($this->logicalOr('name0', 'name1', 'name2', null), $owner); 146 | 147 | $cachedSettingsManager->setMany($settings, $owner); 148 | } 149 | 150 | public function testClear() 151 | { 152 | $owner = $this->createOwner(); 153 | $name = 'name'; 154 | 155 | $settingsManager = \Mockery::mock(SettingsManager::class); 156 | $settingsManager->shouldReceive('clear')->once()->with($name, $owner); 157 | 158 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 159 | ->setMethods(['invalidateCache']) 160 | ->setConstructorArgs([$settingsManager, $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(), 4711]) 161 | ->getMock(); 162 | $cachedSettingsManager->expects($this->at(0)) 163 | ->method('invalidateCache') 164 | ->with($this->equalTo($name), $this->equalTo($owner)) 165 | ->willReturn(true); 166 | $cachedSettingsManager->expects($this->at(1)) 167 | ->method('invalidateCache') 168 | ->with($this->equalTo(null), $this->equalTo($owner)) 169 | ->willReturn(true); 170 | 171 | $cachedSettingsManager->clear($name, $owner); 172 | } 173 | 174 | /** 175 | * Make sure we do always return a string, no matter input. 176 | */ 177 | public function testGetCacheKey() 178 | { 179 | $name = 'name'; 180 | $owner = $this->createOwner(); 181 | 182 | $getCacheKey = new \ReflectionMethod(CachedSettingsManager::class, 'getCacheKey'); 183 | $getCacheKey->setAccessible(true); 184 | 185 | $cachedSettingsManager = $this->getMockBuilder(CachedSettingsManager::class) 186 | ->disableOriginalConstructor() 187 | ->getMock(); 188 | 189 | $this->assertStringContainsString('dmishh_settings', $getCacheKey->invoke($cachedSettingsManager, $name, $owner)); 190 | $this->assertStringContainsString('dmishh_settings', $getCacheKey->invoke($cachedSettingsManager, $name, null)); 191 | $this->assertStringContainsString('dmishh_settings', $getCacheKey->invoke($cachedSettingsManager, null, $owner)); 192 | $this->assertStringContainsString('dmishh_settings', $getCacheKey->invoke($cachedSettingsManager, null, null)); 193 | } 194 | 195 | /** 196 | * @param string $ownerId 197 | * 198 | * @return \Dmishh\SettingsBundle\Entity\SettingsOwnerInterface 199 | */ 200 | protected function createOwner($ownerId = 'user1') 201 | { 202 | return \Mockery::mock( 203 | 'Dmishh\SettingsBundle\Entity\SettingsOwnerInterface', 204 | ['getSettingIdentifier' => $ownerId] 205 | ); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Manager/SettingsManager.php: -------------------------------------------------------------------------------- 1 | 19 | * @author Artem Zhuravlov 20 | */ 21 | class SettingsManager implements SettingsManagerInterface 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private $globalSettings; 27 | 28 | /** 29 | * @var array 30 | */ 31 | private $ownerSettings; 32 | 33 | /** 34 | * @var ObjectManager 35 | */ 36 | private $em; 37 | 38 | /** 39 | * @var EntityRepository 40 | */ 41 | private $repository; 42 | 43 | /** 44 | * @var SerializerInterface 45 | */ 46 | private $serializer; 47 | 48 | /** 49 | * @var array 50 | */ 51 | private $settingsConfiguration; 52 | 53 | public function __construct( 54 | EntityManagerInterface $em, 55 | SerializerInterface $serializer, 56 | array $settingsConfiguration = [] 57 | ) { 58 | $this->em = $em; 59 | $this->repository = $em->getRepository(Setting::class); 60 | $this->serializer = $serializer; 61 | $this->settingsConfiguration = $settingsConfiguration; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function get(string $name, ?SettingsOwnerInterface $owner = null, $default = null) 68 | { 69 | $this->validateSetting($name, $owner); 70 | $this->loadSettings($owner); 71 | 72 | $value = null; 73 | 74 | switch ($this->settingsConfiguration[$name]['scope']) { 75 | case SettingsManagerInterface::SCOPE_GLOBAL: 76 | $value = $this->globalSettings[$name] ?? null; 77 | break; 78 | case SettingsManagerInterface::SCOPE_ALL: 79 | $value = $this->globalSettings[$name] ?? null; 80 | // Do not break here. Try to fetch the users settings 81 | // no break 82 | case SettingsManagerInterface::SCOPE_USER: 83 | if (null !== $owner) { 84 | $value = $this->ownerSettings[$owner->getSettingIdentifier()][$name] ?? $value; 85 | } 86 | break; 87 | } 88 | 89 | return null === $value ? $default : $value; 90 | } 91 | 92 | /** 93 | * {@inheritdoc} 94 | */ 95 | public function all(?SettingsOwnerInterface $owner = null): array 96 | { 97 | $this->loadSettings($owner); 98 | 99 | if (null === $owner) { 100 | return $this->globalSettings; 101 | } 102 | 103 | $settings = $this->ownerSettings[$owner->getSettingIdentifier()]; 104 | 105 | // If some user setting is not defined, please use the value from global 106 | foreach ($settings as $key => $value) { 107 | if (null === $value && isset($this->globalSettings[$key])) { 108 | $settings[$key] = $this->globalSettings[$key]; 109 | } 110 | } 111 | 112 | return $settings; 113 | } 114 | 115 | /** 116 | * {@inheritdoc} 117 | */ 118 | public function set(string $name, $value, ?SettingsOwnerInterface $owner = null): void 119 | { 120 | $this->setWithoutFlush($name, $value, $owner); 121 | $this->flush([$name], $owner); 122 | } 123 | 124 | /** 125 | * {@inheritdoc} 126 | */ 127 | public function setMany(array $settings, ?SettingsOwnerInterface $owner = null): void 128 | { 129 | foreach ($settings as $name => $value) { 130 | $this->setWithoutFlush($name, $value, $owner); 131 | } 132 | 133 | $this->flush(array_keys($settings), $owner); 134 | } 135 | 136 | /** 137 | * {@inheritdoc} 138 | */ 139 | public function clear(string $name, ?SettingsOwnerInterface $owner = null): void 140 | { 141 | $this->set($name, null, $owner); 142 | } 143 | 144 | /** 145 | * Find a setting by name form an array of settings. 146 | * 147 | * @param Setting[] $haystack 148 | */ 149 | protected function findSettingByName(array $haystack, string $needle): ?Setting 150 | { 151 | foreach ($haystack as $setting) { 152 | if ($setting->getName() === $needle) { 153 | return $setting; 154 | } 155 | } 156 | 157 | return null; 158 | } 159 | 160 | /** 161 | * Sets setting value to private array. Used for settings' batch saving. 162 | */ 163 | private function setWithoutFlush(string $name, $value, ?SettingsOwnerInterface $owner = null): void 164 | { 165 | $this->validateSetting($name, $owner); 166 | $this->loadSettings($owner); 167 | 168 | if (null === $owner) { 169 | $this->globalSettings[$name] = $value; 170 | } else { 171 | $this->ownerSettings[$owner->getSettingIdentifier()][$name] = $value; 172 | } 173 | } 174 | 175 | /** 176 | * Flushes settings defined by $names to database. 177 | * 178 | * @throws UnknownSerializerException 179 | */ 180 | private function flush(array $names, ?SettingsOwnerInterface $owner = null): void 181 | { 182 | $settings = $this->repository->findBy([ 183 | 'name' => $names, 184 | 'ownerId' => null === $owner ? null : $owner->getSettingIdentifier(), 185 | ]); 186 | 187 | // Assert: $settings might be a smaller set than $names 188 | 189 | // For each settings that you are trying to save 190 | foreach ($names as $name) { 191 | try { 192 | $value = $this->get($name, $owner); 193 | } catch (WrongScopeException $e) { 194 | continue; 195 | } 196 | 197 | /** @var Setting $setting */ 198 | $setting = $this->findSettingByName($settings, $name); 199 | 200 | if (!$setting) { 201 | // if the setting does not exist in DB, create it 202 | $setting = new Setting(); 203 | $setting->setName($name); 204 | if (null !== $owner) { 205 | $setting->setOwnerId($owner->getSettingIdentifier()); 206 | } 207 | $this->em->persist($setting); 208 | } 209 | 210 | $setting->setValue($this->serializer->serialize($value)); 211 | } 212 | 213 | $this->em->flush(); 214 | } 215 | 216 | /** 217 | * Checks that $name is valid setting and it's scope is also valid. 218 | * 219 | * @param SettingsOwnerInterface $owner 220 | * 221 | * @return SettingsManager 222 | * 223 | * @throws UnknownSettingException 224 | * @throws WrongScopeException 225 | */ 226 | private function validateSetting(string $name, ?SettingsOwnerInterface $owner = null): void 227 | { 228 | // Name validation 229 | if (!\is_string($name) || !\array_key_exists($name, $this->settingsConfiguration)) { 230 | throw new UnknownSettingException($name); 231 | } 232 | 233 | // Scope validation 234 | $scope = $this->settingsConfiguration[$name]['scope']; 235 | if (SettingsManagerInterface::SCOPE_ALL !== $scope) { 236 | if (SettingsManagerInterface::SCOPE_GLOBAL === $scope && null !== $owner || SettingsManagerInterface::SCOPE_USER === $scope && null === $owner) { 237 | throw new WrongScopeException($scope, $name); 238 | } 239 | } 240 | } 241 | 242 | /** 243 | * Settings lazy loading. 244 | */ 245 | private function loadSettings(SettingsOwnerInterface $owner = null): void 246 | { 247 | // Global settings 248 | if (null === $this->globalSettings) { 249 | $this->globalSettings = $this->getSettingsFromRepository(); 250 | } 251 | 252 | // User settings 253 | if (null !== $owner && (null === $this->ownerSettings || !\array_key_exists($owner->getSettingIdentifier(), $this->ownerSettings))) { 254 | $this->ownerSettings[$owner->getSettingIdentifier()] = $this->getSettingsFromRepository($owner); 255 | } 256 | } 257 | 258 | /** 259 | * Retreives settings from repository. 260 | * 261 | * @throws UnknownSerializerException 262 | */ 263 | private function getSettingsFromRepository(?SettingsOwnerInterface $owner = null): array 264 | { 265 | $settings = []; 266 | 267 | foreach (array_keys($this->settingsConfiguration) as $name) { 268 | try { 269 | $this->validateSetting($name, $owner); 270 | $settings[$name] = null; 271 | } catch (WrongScopeException $e) { 272 | continue; 273 | } 274 | } 275 | 276 | /** @var Setting $setting */ 277 | foreach ($this->repository->findBy(['ownerId' => null === $owner ? null : $owner->getSettingIdentifier()]) as $setting) { 278 | if (\array_key_exists($setting->getName(), $settings)) { 279 | $settings[$setting->getName()] = $this->serializer->unserialize($setting->getValue()); 280 | } 281 | } 282 | 283 | return $settings; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Call to an undefined method object\\:\\:isGranted\\(\\)\\.$#" 5 | count: 1 6 | path: Controller/SettingsController.php 7 | 8 | - 9 | message: "#^Call to an undefined method object\\:\\:getToken\\(\\)\\.$#" 10 | count: 2 11 | path: Controller/SettingsController.php 12 | 13 | - 14 | message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#" 15 | count: 1 16 | path: DependencyInjection/Configuration.php 17 | 18 | - 19 | message: "#^Method Dmishh\\\\SettingsBundle\\\\DependencyInjection\\\\DmishhSettingsExtension\\:\\:load\\(\\) has no return typehint specified\\.$#" 20 | count: 1 21 | path: DependencyInjection/DmishhSettingsExtension.php 22 | 23 | - 24 | message: "#^Method Dmishh\\\\SettingsBundle\\\\DependencyInjection\\\\DmishhSettingsExtension\\:\\:load\\(\\) has parameter \\$configs with no value type specified in iterable type array\\.$#" 25 | count: 1 26 | path: DependencyInjection/DmishhSettingsExtension.php 27 | 28 | - 29 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\Setting\\:\\:setName\\(\\) has no return typehint specified\\.$#" 30 | count: 1 31 | path: Entity/Setting.php 32 | 33 | - 34 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\Setting\\:\\:setOwnerId\\(\\) has no return typehint specified\\.$#" 35 | count: 1 36 | path: Entity/Setting.php 37 | 38 | - 39 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Entity\\\\SettingsOwnerInterface\\:\\:getSettingIdentifier\\(\\) has no return typehint specified\\.$#" 40 | count: 1 41 | path: Entity/SettingsOwnerInterface.php 42 | 43 | - 44 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\UnknownSerializerException\\:\\:__construct\\(\\) has parameter \\$serializerClass with no typehint specified\\.$#" 45 | count: 1 46 | path: Exception/UnknownSerializerException.php 47 | 48 | - 49 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\UnknownSettingException\\:\\:__construct\\(\\) has parameter \\$settingName with no typehint specified\\.$#" 50 | count: 1 51 | path: Exception/UnknownSettingException.php 52 | 53 | - 54 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\WrongScopeException\\:\\:__construct\\(\\) has parameter \\$scope with no typehint specified\\.$#" 55 | count: 1 56 | path: Exception/WrongScopeException.php 57 | 58 | - 59 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Exception\\\\WrongScopeException\\:\\:__construct\\(\\) has parameter \\$settingName with no typehint specified\\.$#" 60 | count: 1 61 | path: Exception/WrongScopeException.php 62 | 63 | - 64 | message: "#^Property Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:\\$settingsConfiguration has no typehint specified\\.$#" 65 | count: 1 66 | path: Form/Type/SettingsType.php 67 | 68 | - 69 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:__construct\\(\\) has parameter \\$settingsConfiguration with no value type specified in iterable type array\\.$#" 70 | count: 1 71 | path: Form/Type/SettingsType.php 72 | 73 | - 74 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has no return typehint specified\\.$#" 75 | count: 1 76 | path: Form/Type/SettingsType.php 77 | 78 | - 79 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has parameter \\$builder with no value type specified in iterable type Symfony\\\\Component\\\\Form\\\\FormBuilderInterface\\.$#" 80 | count: 1 81 | path: Form/Type/SettingsType.php 82 | 83 | - 84 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:buildForm\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" 85 | count: 1 86 | path: Form/Type/SettingsType.php 87 | 88 | - 89 | message: "#^Cannot instantiate interface Dmishh\\\\SettingsBundle\\\\Exception\\\\SettingsException\\.$#" 90 | count: 1 91 | path: Form/Type/SettingsType.php 92 | 93 | - 94 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Form\\\\Type\\\\SettingsType\\:\\:configureOptions\\(\\) has no return typehint specified\\.$#" 95 | count: 1 96 | path: Form/Type/SettingsType.php 97 | 98 | - 99 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:__construct\\(\\) has parameter \\$cacheLifeTime with no typehint specified\\.$#" 100 | count: 1 101 | path: Manager/CachedSettingsManager.php 102 | 103 | - 104 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:get\\(\\) has no return typehint specified\\.$#" 105 | count: 1 106 | path: Manager/CachedSettingsManager.php 107 | 108 | - 109 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" 110 | count: 1 111 | path: Manager/CachedSettingsManager.php 112 | 113 | - 114 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" 115 | count: 1 116 | path: Manager/CachedSettingsManager.php 117 | 118 | - 119 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" 120 | count: 1 121 | path: Manager/CachedSettingsManager.php 122 | 123 | - 124 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\CachedSettingsManager\\:\\:storeInCache\\(\\) has parameter \\$value with no typehint specified\\.$#" 125 | count: 1 126 | path: Manager/CachedSettingsManager.php 127 | 128 | - 129 | message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$globalSettings type has no value type specified in iterable type array\\.$#" 130 | count: 1 131 | path: Manager/SettingsManager.php 132 | 133 | - 134 | message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$ownerSettings type has no value type specified in iterable type array\\.$#" 135 | count: 1 136 | path: Manager/SettingsManager.php 137 | 138 | - 139 | message: "#^Property Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:\\$settingsConfiguration type has no value type specified in iterable type array\\.$#" 140 | count: 1 141 | path: Manager/SettingsManager.php 142 | 143 | - 144 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:__construct\\(\\) has parameter \\$settingsConfiguration with no value type specified in iterable type array\\.$#" 145 | count: 1 146 | path: Manager/SettingsManager.php 147 | 148 | - 149 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:get\\(\\) has no return typehint specified\\.$#" 150 | count: 1 151 | path: Manager/SettingsManager.php 152 | 153 | - 154 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" 155 | count: 1 156 | path: Manager/SettingsManager.php 157 | 158 | - 159 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" 160 | count: 1 161 | path: Manager/SettingsManager.php 162 | 163 | - 164 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" 165 | count: 1 166 | path: Manager/SettingsManager.php 167 | 168 | - 169 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:setWithoutFlush\\(\\) has parameter \\$value with no typehint specified\\.$#" 170 | count: 1 171 | path: Manager/SettingsManager.php 172 | 173 | - 174 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:flush\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" 175 | count: 1 176 | path: Manager/SettingsManager.php 177 | 178 | - 179 | message: "#^Negated boolean expression is always false\\.$#" 180 | count: 1 181 | path: Manager/SettingsManager.php 182 | 183 | - 184 | message: "#^PHPDoc tag @return with type Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager is incompatible with native type void\\.$#" 185 | count: 1 186 | path: Manager/SettingsManager.php 187 | 188 | - 189 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManager\\:\\:getSettingsFromRepository\\(\\) return type has no value type specified in iterable type array\\.$#" 190 | count: 1 191 | path: Manager/SettingsManager.php 192 | 193 | - 194 | message: "#^Parameter \\#1 \\$key of function array_key_exists expects int\\|string, string\\|null given\\.$#" 195 | count: 1 196 | path: Manager/SettingsManager.php 197 | 198 | - 199 | message: "#^Parameter \\#1 \\$serialized of method Dmishh\\\\SettingsBundle\\\\Serializer\\\\SerializerInterface\\:\\:unserialize\\(\\) expects string, string\\|null given\\.$#" 200 | count: 1 201 | path: Manager/SettingsManager.php 202 | 203 | - 204 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:get\\(\\) has no return typehint specified\\.$#" 205 | count: 1 206 | path: Manager/SettingsManagerInterface.php 207 | 208 | - 209 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" 210 | count: 1 211 | path: Manager/SettingsManagerInterface.php 212 | 213 | - 214 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" 215 | count: 1 216 | path: Manager/SettingsManagerInterface.php 217 | 218 | - 219 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Manager\\\\SettingsManagerInterface\\:\\:setMany\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" 220 | count: 1 221 | path: Manager/SettingsManagerInterface.php 222 | 223 | - 224 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" 225 | count: 1 226 | path: Serializer/JsonSerializer.php 227 | 228 | - 229 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:serialize\\(\\) should return string but returns string\\|false\\.$#" 230 | count: 1 231 | path: Serializer/JsonSerializer.php 232 | 233 | - 234 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\JsonSerializer\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" 235 | count: 1 236 | path: Serializer/JsonSerializer.php 237 | 238 | - 239 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\PhpSerializer\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" 240 | count: 1 241 | path: Serializer/PhpSerializer.php 242 | 243 | - 244 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\PhpSerializer\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" 245 | count: 1 246 | path: Serializer/PhpSerializer.php 247 | 248 | - 249 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\SerializerInterface\\:\\:serialize\\(\\) has parameter \\$data with no typehint specified\\.$#" 250 | count: 1 251 | path: Serializer/SerializerInterface.php 252 | 253 | - 254 | message: "#^Method Dmishh\\\\SettingsBundle\\\\Serializer\\\\SerializerInterface\\:\\:unserialize\\(\\) has no return typehint specified\\.$#" 255 | count: 1 256 | path: Serializer/SerializerInterface.php 257 | 258 | -------------------------------------------------------------------------------- /Tests/SettingsManagerTest.php: -------------------------------------------------------------------------------- 1 | expectException('\Dmishh\SettingsBundle\Exception\UnknownSettingException'); 15 | $settingsManager = $this->createSettingsManager(); 16 | $settingsManager->get('unknown_setting'); 17 | } 18 | 19 | public function testGlobalSettingsAccessor() 20 | { 21 | $settingsManager = $this->createSettingsManager(); 22 | $settingsManager->set('some_setting', 'VALUE_GLOBAL'); 23 | $this->assertEquals('VALUE_GLOBAL', $settingsManager->get('some_setting')); 24 | } 25 | 26 | public function testGlobalSettingsClear() 27 | { 28 | $settingsManager = $this->createSettingsManager(); 29 | $settingsManager->set('some_setting', 'VALUE_GLOBAL'); 30 | $settingsManager->clear('some_setting'); 31 | $this->assertNull($settingsManager->get('some_setting')); 32 | } 33 | 34 | public function testUserSettingsAccessor() 35 | { 36 | $owner = $this->createOwner(); 37 | $settingsManager = $this->createSettingsManager(); 38 | $settingsManager->set('some_setting', 'VALUE_USER', $owner); 39 | $this->assertEquals('VALUE_USER', $settingsManager->get('some_setting', $owner)); 40 | } 41 | 42 | public function testUserSettingsClear() 43 | { 44 | $owner = $this->createOwner(); 45 | $settingsManager = $this->createSettingsManager(); 46 | $settingsManager->set('some_setting', 'VALUE_GLOBAL', $owner); 47 | $settingsManager->clear('some_setting', $owner); 48 | $this->assertNull($settingsManager->get('some_setting', $owner)); 49 | } 50 | 51 | public function testGlobalAndUserSettingsArentIntersect() 52 | { 53 | $owner = $this->createOwner(); 54 | $settingsManager = $this->createSettingsManager(); 55 | $settingsManager->set('some_setting', 'VALUE_GLOBAL'); 56 | $settingsManager->set('some_setting', 'VALUE_USER', $owner); 57 | $this->assertEquals('VALUE_GLOBAL', $settingsManager->get('some_setting')); 58 | $this->assertEquals('VALUE_USER', $settingsManager->get('some_setting', $owner)); 59 | 60 | // in reverse order 61 | $settingsManager->set('some_setting', 'VALUE_USER_2', $owner); 62 | $settingsManager->set('some_setting', 'VALUE_GLOBAL_2'); 63 | $this->assertEquals('VALUE_GLOBAL_2', $settingsManager->get('some_setting')); 64 | $this->assertEquals('VALUE_USER_2', $settingsManager->get('some_setting', $owner)); 65 | } 66 | 67 | public function testUsersSettingsArentIntersect() 68 | { 69 | $owner1 = $this->createOwner(1); 70 | $owner2 = $this->createOwner(2); 71 | $settingsManager = $this->createSettingsManager(); 72 | $settingsManager->set('some_setting', 'VALUE_USER_1', $owner1); 73 | $settingsManager->set('some_setting', 'VALUE_USER_2', $owner2); 74 | $this->assertEquals('VALUE_USER_1', $settingsManager->get('some_setting', $owner1)); 75 | $this->assertEquals('VALUE_USER_2', $settingsManager->get('some_setting', $owner2)); 76 | 77 | // in reverse order 78 | $settingsManager->set('some_setting', 'VALUE_USER_2', $owner2); 79 | $settingsManager->set('some_setting', 'VALUE_USER_1', $owner1); 80 | $this->assertEquals('VALUE_USER_1', $settingsManager->get('some_setting', $owner1)); 81 | $this->assertEquals('VALUE_USER_2', $settingsManager->get('some_setting', $owner2)); 82 | } 83 | 84 | public function testPersistence() 85 | { 86 | $settingsManager = $this->createSettingsManager(); 87 | $settingsManager->set('some_setting', 'VALUE_GLOBAL'); 88 | 89 | // creating new manager and cheking for some_setting 90 | $settingsManager = $this->createSettingsManager(); 91 | $this->assertEquals('VALUE_GLOBAL', $settingsManager->get('some_setting')); 92 | 93 | // clear 94 | $settingsManager->clear('some_setting'); 95 | $settingsManager = $this->createSettingsManager(); 96 | $this->assertNull($settingsManager->get('some_setting')); 97 | } 98 | 99 | public function testSetUserSettingInGlobalScopeRaisesException() 100 | { 101 | $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); 102 | 103 | $owner = $this->createOwner(); 104 | $settingsManager = $this->createSettingsManager(); 105 | $settingsManager->set('some_global_setting', 'VALUE_GLOBAL'); 106 | $this->assertEquals('VALUE_GLOBAL', $settingsManager->get('some_global_setting')); 107 | 108 | $settingsManager->set('some_global_setting', 'VALUE_GLOBAL', $owner); 109 | } 110 | 111 | public function testGetUserSettingInGlobalScopeRaisesException() 112 | { 113 | $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); 114 | 115 | $owner = $this->createOwner(); 116 | $settingsManager = $this->createSettingsManager(); 117 | $settingsManager->get('some_global_setting', $owner); 118 | } 119 | 120 | public function testSetGlobalSettingInUserScopeRaisesException() 121 | { 122 | $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); 123 | 124 | $owner = $this->createOwner(); 125 | $settingsManager = $this->createSettingsManager(); 126 | $settingsManager->set('some_user_setting', 'VALUE_USER', $owner); 127 | $this->assertEquals('VALUE_USER', $settingsManager->get('some_global_setting', $owner)); 128 | 129 | $settingsManager->set('some_user_setting', 'VALUE_USER'); 130 | } 131 | 132 | public function testGetGlobalSettingInUserScopeRaisesException() 133 | { 134 | $this->expectException('\Dmishh\SettingsBundle\Exception\WrongScopeException'); 135 | 136 | $settingsManager = $this->createSettingsManager(); 137 | $settingsManager->get('some_user_setting'); 138 | } 139 | 140 | public function testGetAllGlobalSettings() 141 | { 142 | $settingsManager = $this->createSettingsManager(); 143 | $this->assertEquals( 144 | ['some_setting' => null, 'some_setting2' => null, 'some_global_setting' => null], 145 | $settingsManager->all() 146 | ); 147 | } 148 | 149 | public function testGetAllUserSettings() 150 | { 151 | $owner = $this->createOwner(); 152 | $settingsManager = $this->createSettingsManager(); 153 | $this->assertEquals( 154 | ['some_setting' => null, 'some_setting2' => null, 'some_user_setting' => null], 155 | $settingsManager->all($owner) 156 | ); 157 | } 158 | 159 | public function testScopeAll() 160 | { 161 | $owner = $this->createOwner(); 162 | $settingsManager = $this->createSettingsManager(); 163 | 164 | // Global settings should be shown if there is no user setting defined 165 | $settingsManager->set('some_setting', 'value'); 166 | $this->assertEquals('value', $settingsManager->get('some_setting')); 167 | $this->assertEquals( 168 | 'value', 169 | $settingsManager->get('some_setting', $owner), 170 | 'Did not get global value when local value was undefined.' 171 | ); 172 | $this->assertEquals( 173 | ['some_setting' => 'value', 'some_setting2' => null, 'some_user_setting' => null], 174 | $settingsManager->all($owner), 175 | 'Did not get global value when local value was undefined.' 176 | ); 177 | 178 | // The users settings should always be prioritised over the global one (if it exists) 179 | $settingsManager->set('some_setting', 'user_value', $owner); 180 | $this->assertEquals( 181 | 'user_value', 182 | $settingsManager->get('some_setting', $owner), 183 | 'User/Local value should have priority over global.' 184 | ); 185 | $this->assertEquals('value', $settingsManager->get('some_setting')); 186 | $this->assertEquals( 187 | ['some_setting' => 'user_value', 'some_setting2' => null, 'some_user_setting' => null], 188 | $settingsManager->all($owner), 189 | 'User/Local value should have priority over global.' 190 | ); 191 | } 192 | 193 | public function testValidSerizalizationTypes() 194 | { 195 | $settingsManager = $this->createSettingsManager([], 'php'); 196 | $settingsManager->set('some_setting', 123); 197 | $this->assertEquals(123, $settingsManager->get('some_setting')); 198 | 199 | $settingsManager = $this->createSettingsManager([], 'json'); 200 | $settingsManager->set('some_setting', 123); 201 | $this->assertEquals(123, $settingsManager->get('some_setting')); 202 | } 203 | 204 | public function testSetSettingWithInvalidSerizalizationType() 205 | { 206 | $this->expectException('\Dmishh\SettingsBundle\Exception\SettingsException'); 207 | 208 | $settingsManager = $this->createSettingsManager([], 'unknown_serialization_type'); 209 | $settingsManager->set('some_setting', 123); 210 | } 211 | 212 | public function testGetSettingWithInvalidSerizalizationType() 213 | { 214 | $this->expectException('\Dmishh\SettingsBundle\Exception\SettingsException'); 215 | 216 | $settingsManager = $this->createSettingsManager([]); 217 | $settingsManager->set('some_setting', 123); 218 | 219 | $settingsManager = $this->createSettingsManager([], 'unknown_serialization_type'); 220 | $settingsManager->get('some_setting'); 221 | } 222 | 223 | public function testGetDefaultValue() 224 | { 225 | $user = $this->createOwner(); 226 | $settingsManager = $this->createSettingsManager(); 227 | 228 | //test default global value 229 | $this->assertNull($settingsManager->get('some_setting')); 230 | $this->assertEquals('foobar', $settingsManager->get('some_setting', null, 'foobar')); 231 | 232 | //test default user value 233 | $this->assertNull($settingsManager->get('some_setting')); 234 | $this->assertEquals('foobar', $settingsManager->get('some_setting', $user, 'foobar')); 235 | 236 | //test when there is an actual value 237 | $settingsManager->set('some_setting', 'value'); 238 | $this->assertEquals('value', $settingsManager->get('some_setting', null, 'foobar')); 239 | $this->assertEquals('value', $settingsManager->get('some_setting', $user, 'foobar')); 240 | } 241 | 242 | /** 243 | * @see https://github.com/dmishh/SettingsBundle/issues/28 244 | */ 245 | public function testFlush() 246 | { 247 | $names = ['foo', 'bar', 'baz']; 248 | $settings = 'foobar'; 249 | $owner = null; 250 | $value = 'settingValue'; 251 | $serializedValue = 'sValue'; 252 | 253 | $flushMethod = new \ReflectionMethod('Dmishh\SettingsBundle\Manager\SettingsManager', 'flush'); 254 | $flushMethod->setAccessible(true); 255 | 256 | $serializer = $this 257 | ->getMockBuilder('Dmishh\SettingsBundle\Serializer\PhpSerializer') 258 | ->setMethods(['serialize']) 259 | ->getMock(); 260 | 261 | $serializer 262 | ->expects($this->exactly(\count($names))) 263 | ->method('serialize') 264 | ->with($this->equalTo($value)) 265 | ->willReturn($serializedValue); 266 | 267 | $repo = $this 268 | ->getMockBuilder('Doctrine\ORM\EntityRepository') 269 | ->disableOriginalConstructor() 270 | ->setMethods(['findBy']) 271 | ->getMock(); 272 | 273 | $repo->expects($this->once())->method('findBy')->with( 274 | $this->equalTo( 275 | [ 276 | 'name' => $names, 277 | 'ownerId' => $owner, 278 | ] 279 | ) 280 | )->willReturn($settings); 281 | 282 | $em = $this 283 | ->getMockBuilder('Doctrine\Orm\EntityManager') 284 | ->disableOriginalConstructor() 285 | ->setMethods(['getRepository', 'flush']) 286 | ->getMock(); 287 | 288 | $em->expects($this->once())->method('getRepository')->willReturn($repo); 289 | $em->expects($this->once())->method('flush'); 290 | 291 | $setting = $this 292 | ->getMockBuilder('Dmishh\SettingsBundle\Entity\Settings') 293 | ->disableOriginalConstructor() 294 | ->setMethods(['setValue']) 295 | ->getMock(); 296 | 297 | $setting->expects($this->exactly(\count($names)))->method('setValue')->with($this->equalTo($serializedValue)); 298 | 299 | $manager = $this 300 | ->getMockBuilder('Dmishh\SettingsBundle\Manager\SettingsManager') 301 | ->setConstructorArgs([$em, $serializer, []]) 302 | ->setMethods(['findSettingByName', 'get']) 303 | ->getMock(); 304 | 305 | $manager 306 | ->expects($this->exactly(\count($names))) 307 | ->method('get') 308 | ->withConsecutive( 309 | [$this->equalTo('foo'), $owner], 310 | [$this->equalTo('bar'), $owner], 311 | [$this->equalTo('baz'), $owner] 312 | ) 313 | ->willReturn($value); 314 | 315 | $manager 316 | ->expects($this->exactly(\count($names))) 317 | ->method('findSettingByName') 318 | ->withConsecutive( 319 | [$settings, $this->equalTo('foo')], 320 | [$settings, $this->equalTo('bar')], 321 | [$settings, $this->equalTo('baz')] 322 | )->willReturn($setting); 323 | 324 | $flushMethod->invoke($manager, $names, $owner); 325 | } 326 | 327 | public function testFindSettingByName() 328 | { 329 | $settingsManager = $this->createSettingsManager(); 330 | 331 | $s1 = $this->createSetting('foo'); 332 | $s2 = $this->createSetting('bar'); 333 | $s3 = $this->createSetting('baz'); 334 | $s4 = $this->createSetting('foo'); 335 | $settings = [$s1, $s2, $s3, $s4]; 336 | 337 | $method = new \ReflectionMethod('Dmishh\SettingsBundle\Manager\SettingsManager', 'findSettingByName'); 338 | $method->setAccessible(true); 339 | 340 | $result = $method->invoke($settingsManager, $settings, 'bar'); 341 | $this->assertEquals($s2, $result); 342 | 343 | $result = $method->invoke($settingsManager, $settings, 'biz'); 344 | $this->assertNull($result); 345 | 346 | $result = $method->invoke($settingsManager, $settings, 'foo'); 347 | $this->assertEquals($s1, $result); 348 | } 349 | 350 | protected function createSetting($name) 351 | { 352 | $s = $this->getMockBuilder('Dmishh\SettingsBundle\Entity\Setting') 353 | ->setMethods(['getName']) 354 | ->getMock(); 355 | 356 | $s->expects($this->any()) 357 | ->method('getName') 358 | ->willReturn($name); 359 | 360 | return $s; 361 | } 362 | 363 | /** 364 | * @param string $ownerId 365 | * 366 | * @return \Dmishh\SettingsBundle\Entity\SettingsOwnerInterface 367 | */ 368 | protected function createOwner($ownerId = 'user1') 369 | { 370 | return Mockery::mock( 371 | 'Dmishh\SettingsBundle\Entity\SettingsOwnerInterface', 372 | ['getSettingIdentifier' => $ownerId] 373 | ); 374 | } 375 | 376 | protected function createSettingsManager(array $configuration = [], $serialization = 'php') 377 | { 378 | if (empty($configuration)) { 379 | $configuration = [ 380 | 'some_setting' => ['scope' => SettingsManagerInterface::SCOPE_ALL], 381 | 'some_setting2' => ['scope' => SettingsManagerInterface::SCOPE_ALL], 382 | 'some_global_setting' => ['scope' => SettingsManagerInterface::SCOPE_GLOBAL], 383 | 'some_user_setting' => ['scope' => SettingsManagerInterface::SCOPE_USER], 384 | ]; 385 | } 386 | 387 | $serializer = SerializerFactory::create($serialization); 388 | 389 | return new SettingsManager($this->em, $serializer, $configuration); 390 | } 391 | } 392 | --------------------------------------------------------------------------------