├── CHANGELOG.md ├── CacheAdapter ├── CacheAdapterInterface.php ├── DoctrineCacheBundleAdapter.php ├── NullAdapter.php └── SymfonyCacheComponentAdapter.php ├── Controller └── SettingsController.php ├── CraueConfigBundle.php ├── DependencyInjection ├── Configuration.php └── CraueConfigExtension.php ├── Entity ├── BaseSetting.php ├── Setting.php └── SettingInterface.php ├── Form ├── ModifySettingsForm.php └── Type │ └── SettingType.php ├── LICENSE ├── README.md ├── Repository └── SettingRepository.php ├── Resources ├── config │ ├── controller.xml │ ├── doctrine-mapping-with-default-setting │ │ ├── BaseSetting.orm.xml │ │ └── Setting.orm.xml │ ├── doctrine-mapping │ │ └── BaseSetting.orm.xml │ ├── form.xml │ ├── routing │ │ └── settings.xml │ ├── twig.xml │ └── util.xml ├── public │ └── css │ │ └── form.css ├── translations │ ├── CraueConfigBundle.de.yml │ ├── CraueConfigBundle.en.yml │ ├── CraueConfigBundle.nl.yml │ └── CraueConfigBundle.ru.yml └── views │ ├── Settings │ ├── modify.html.twig │ ├── modify_content.html.twig │ ├── modify_form.html.twig │ └── modify_form_empty.html.twig │ └── layout.html.twig ├── Twig └── Extension │ └── ConfigTemplateExtension.php ├── UPGRADE-2.0.md ├── Util └── Config.php └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.7.0] – 2023-08-07 4 | 5 | - dropped support for Symfony 5.3, 6.0, 6.1, 6.2 6 | 7 | [2.7.0]: https://github.com/craue/CraueConfigBundle/compare/2.6.0...2.7.0 8 | 9 | ## [2.6.0] – 2022-01-24 10 | 11 | - added autowiring alias for `Craue\ConfigBundle\CacheAdapter\CacheAdapterInterface` 12 | - added support for Symfony 6 13 | - dropped support for Symfony 3.4, 5.1, 5.2 14 | 15 | [2.6.0]: https://github.com/craue/CraueConfigBundle/compare/2.5.0...2.6.0 16 | 17 | ## [2.5.0] – 2020-12-17 18 | 19 | - [#57]: added autowiring alias for `Craue\ConfigBundle\Util\Config` 20 | - added support for PHP 8.0 21 | - dropped support for PHP 7.0, 7.1, 7.2 22 | - dropped support for Symfony 4.2, 4.3, 5.0 23 | 24 | [#57]: https://github.com/craue/CraueConfigBundle/issues/57 25 | [2.5.0]: https://github.com/craue/CraueConfigBundle/compare/2.4.0...2.5.0 26 | 27 | ## [2.4.0] – 2019-12-03 28 | 29 | - added support for Symfony 5.* 30 | - dropped support for Symfony 4.1 31 | 32 | [2.4.0]: https://github.com/craue/CraueConfigBundle/compare/2.3.0...2.4.0 33 | 34 | ## [2.3.0] – 2019-08-01 35 | 36 | - [#50]: prevent creating the default table when using a custom entity 37 | 38 | [#50]: https://github.com/craue/CraueConfigBundle/issues/50 39 | [2.3.0]: https://github.com/craue/CraueConfigBundle/compare/2.2.1...2.3.0 40 | 41 | ## [2.2.1] – 2019-06-01 42 | 43 | - deprecated support for Symfony Cache component's PSR-16 Simple Cache 44 | - code updates to avoid deprecation notices 45 | 46 | [2.2.1]: https://github.com/craue/CraueConfigBundle/compare/2.2.0...2.2.1 47 | 48 | ## [2.2.0] – 2019-01-06 49 | 50 | - added support for Symfony Cache component's PSR-16 Simple Cache 51 | - dropped support for Symfony 2.7, 2.8, 3.0, 3.1, 3.2, 3.3, 4.0 52 | - dropped support for PHP 5.3, 5.4, 5.5, 5.6 53 | - dropped support for HHVM 54 | 55 | [2.2.0]: https://github.com/craue/CraueConfigBundle/compare/2.1.0...2.2.0 56 | 57 | ## [2.1.0] – 2018-01-02 58 | 59 | - [#41]: 60 | - added support for Symfony 4.* 61 | - no longer use Assetic 62 | 63 | [#41]: https://github.com/craue/CraueConfigBundle/issues/41 64 | [2.1.0]: https://github.com/craue/CraueConfigBundle/compare/2.0.0...2.1.0 65 | 66 | ## [2.0.0] – 2017-05-11 67 | 68 | - BC breaks (follow `UPGRADE-2.0.md` to upgrade): 69 | - [#20]: 70 | - use XML instead of annotations for Doctrine mapping to allow overriding it 71 | - bumped Symfony dependency to 2.7 72 | - simplified the built-in form by removing hidden fields 73 | - removed some class parameters 74 | - [#35]: added a cache for settings 75 | - avoid warnings about missing translations when rendering the built-in form 76 | 77 | [#20]: https://github.com/craue/CraueConfigBundle/issues/20 78 | [#35]: https://github.com/craue/CraueConfigBundle/issues/35 79 | [2.0.0]: https://github.com/craue/CraueConfigBundle/compare/1.4.2...2.0.0 80 | 81 | ## [1.4.2] – 2017-03-27 82 | 83 | - prevent changes to names and sections of settings via hidden fields in the built-in form 84 | - use namespaced template names (available since Twig 1.10) 85 | 86 | [1.4.2]: https://github.com/craue/CraueConfigBundle/compare/1.4.1...1.4.2 87 | 88 | ## [1.4.1] – 2015-12-29 89 | 90 | - added support for PHP 7.0 and HHVM 91 | 92 | [1.4.1]: https://github.com/craue/CraueConfigBundle/compare/1.4.0...1.4.1 93 | 94 | ## [1.4.0] – 2015-11-30 95 | 96 | - added support for Symfony 3.* 97 | - dropped support for Symfony 2.1 and 2.2 98 | 99 | [1.4.0]: https://github.com/craue/CraueConfigBundle/compare/1.3.2...1.4.0 100 | 101 | ## [1.3.2] – 2015-11-30 102 | 103 | - [#22]+[#23]+[#28]: added conditional code updates to avoid deprecation notices with Symfony 2.8 104 | - simplified controller code by injecting the `Request` into the action 105 | 106 | [#22]: https://github.com/craue/CraueConfigBundle/issues/22 107 | [#23]: https://github.com/craue/CraueConfigBundle/issues/23 108 | [#28]: https://github.com/craue/CraueConfigBundle/issues/28 109 | [1.3.2]: https://github.com/craue/CraueConfigBundle/compare/1.3.1...1.3.2 110 | 111 | ## [1.3.1] – 2015-02-27 112 | 113 | - [#17]: added Russian translation 114 | - added conditional code updates to avoid deprecation notices 115 | 116 | [#17]: https://github.com/craue/CraueConfigBundle/issues/17 117 | [1.3.1]: https://github.com/craue/CraueConfigBundle/compare/1.3.0...1.3.1 118 | 119 | ## [1.3.0] – 2014-11-04 120 | 121 | - [#16]: added method `getBySection` to get settings with a given section 122 | 123 | [#16]: https://github.com/craue/CraueConfigBundle/issues/16 124 | [1.3.0]: https://github.com/craue/CraueConfigBundle/compare/1.2.0...1.3.0 125 | 126 | ## [1.2.0] – 2014-05-22 127 | 128 | - [#10]: added Twig function `craue_setting` 129 | - [#12]: added Dutch translation 130 | 131 | [#10]: https://github.com/craue/CraueConfigBundle/issues/10 132 | [#12]: https://github.com/craue/CraueConfigBundle/issues/12 133 | [1.2.0]: https://github.com/craue/CraueConfigBundle/compare/1.1.4...1.2.0 134 | 135 | ## [1.1.4] – 2013-12-08 136 | 137 | - fixed handling flash messages in the base template 138 | - fixed translating setting names in the modification form 139 | - adjusted the Composer requirements to also allow Symfony 2.4 and up (now 2.1 and up) 140 | 141 | [1.1.4]: https://github.com/craue/CraueConfigBundle/compare/1.1.3...1.1.4 142 | 143 | ## [1.1.3] – 2013-11-21 144 | 145 | - [#7]: fixed the base template to be compatible with Symfony 2.3 as well (now 2.1 and up) 146 | 147 | [#7]: https://github.com/craue/CraueConfigBundle/issues/7 148 | [1.1.3]: https://github.com/craue/CraueConfigBundle/compare/1.1.2...1.1.3 149 | 150 | ## [1.1.2] – 2013-09-25 151 | 152 | - adjusted the Composer requirements to also allow Symfony 2.3 153 | 154 | [1.1.2]: https://github.com/craue/CraueConfigBundle/compare/1.1.1...1.1.2 155 | 156 | ## [1.1.1] – 2013-05-02 157 | 158 | - allow a custom route for redirection after handling form submission 159 | 160 | [1.1.1]: https://github.com/craue/CraueConfigBundle/compare/1.1.0...1.1.1 161 | 162 | ## [1.1.0] – 2013-02-28 163 | 164 | - adjustments to changes in the Form component for Symfony 2.1.* 165 | - [#2]: added setters to the `Config` class 166 | 167 | [#2]: https://github.com/craue/CraueConfigBundle/issues/2 168 | [1.1.0]: https://github.com/craue/CraueConfigBundle/compare/1.0.0...1.1.0 169 | 170 | ## 1.0.0 - 2012-05-26 171 | 172 | - first stable release for Symfony 2.0.* 173 | 174 | ## 2011-07-07 175 | 176 | - initial commit 177 | -------------------------------------------------------------------------------- /CacheAdapter/CacheAdapterInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011-2023 Christian Raue 8 | * @license http://opensource.org/licenses/mit-license.php MIT License 9 | */ 10 | interface CacheAdapterInterface { 11 | 12 | /** 13 | * Deletes all cache entries. 14 | * @return bool Whether the operation was successful. 15 | */ 16 | function clear(); 17 | 18 | /** 19 | * @param string $key 20 | * @return bool 21 | */ 22 | function has($key); 23 | 24 | /** 25 | * @param string $key 26 | * @return mixed 27 | */ 28 | function get($key); 29 | 30 | /** 31 | * @param string $key 32 | * @param mixed $value 33 | * @return bool Whether the entry was successfully stored in the cache. 34 | */ 35 | function set($key, $value); 36 | 37 | /** 38 | * @param array $keysAndValues 39 | * @return bool Whether the entries were successfully stored in the cache. 40 | */ 41 | function setMultiple(array $keysAndValues); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /CacheAdapter/DoctrineCacheBundleAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright 2011-2023 Christian Raue 10 | * @license http://opensource.org/licenses/mit-license.php MIT License 11 | */ 12 | class DoctrineCacheBundleAdapter implements CacheAdapterInterface { 13 | 14 | /** 15 | * @var CacheProvider 16 | */ 17 | private $cache; 18 | 19 | public function __construct(CacheProvider $cache) { 20 | $this->cache = $cache; 21 | } 22 | 23 | public function clear() { 24 | return $this->cache->deleteAll(); 25 | } 26 | 27 | public function has($key) { 28 | return $this->cache->contains($key); 29 | } 30 | 31 | public function get($key) { 32 | return $this->cache->fetch($key); 33 | } 34 | 35 | public function set($key, $value) { 36 | return $this->cache->save($key, $value); 37 | } 38 | 39 | public function setMultiple(array $keysAndValues) { 40 | return $this->cache->saveMultiple($keysAndValues); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /CacheAdapter/NullAdapter.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011-2023 Christian Raue 8 | * @license http://opensource.org/licenses/mit-license.php MIT License 9 | */ 10 | class NullAdapter implements CacheAdapterInterface { 11 | 12 | public function clear() { 13 | return true; 14 | } 15 | 16 | public function has($key) { 17 | return false; 18 | } 19 | 20 | public function get($key) { 21 | return null; 22 | } 23 | 24 | public function set($key, $value) { 25 | return false; 26 | } 27 | 28 | public function setMultiple(array $keysAndValues) { 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /CacheAdapter/SymfonyCacheComponentAdapter.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright 2011-2023 Christian Raue 12 | * @license http://opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | class SymfonyCacheComponentAdapter implements CacheAdapterInterface { 15 | 16 | /** 17 | * @var CacheItemPoolInterface 18 | */ 19 | private $cache; 20 | 21 | public function __construct($cache) { 22 | if ($cache instanceof CacheItemPoolInterface) { 23 | $this->cache = $cache; 24 | return; 25 | } 26 | 27 | if ($cache instanceof CacheInterface) { 28 | @trigger_error(sprintf('Configuring a cache of type %s is deprecated since CraueConfigBundle 2.2.1. Use %s instead.', CacheInterface::class, CacheItemPoolInterface::class), E_USER_DEPRECATED); 29 | 30 | $this->cache = new Psr16Adapter($cache); 31 | return; 32 | } 33 | 34 | throw new \InvalidArgumentException(sprintf('Expected argument of type "%s" or "%s", but "%s" given.', 35 | CacheItemPoolInterface::class, 36 | CacheInterface::class, 37 | is_object($cache) ? get_class($cache) : gettype($cache))); 38 | } 39 | 40 | public function clear() { 41 | return $this->cache->clear(); 42 | } 43 | 44 | public function has($key) { 45 | return $this->cache->hasItem($key); 46 | } 47 | 48 | public function get($key) { 49 | return $this->cache->getItem($key)->get(); 50 | } 51 | 52 | public function set($key, $value) { 53 | $cacheItem = $this->cache->getItem($key); 54 | $cacheItem->set($value); 55 | 56 | return $this->cache->save($cacheItem); 57 | } 58 | 59 | public function setMultiple(array $keysAndValues) { 60 | foreach ($keysAndValues as $key => $value) { 61 | $cacheItem = $this->cache->getItem($key); 62 | $cacheItem->set($value); 63 | if (!$this->cache->saveDeferred($cacheItem)) { 64 | return false; 65 | } 66 | } 67 | 68 | return $this->cache->commit(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Controller/SettingsController.php: -------------------------------------------------------------------------------- 1 | 20 | * @copyright 2011-2023 Christian Raue 21 | * @license http://opensource.org/licenses/mit-license.php MIT License 22 | */ 23 | class SettingsController extends AbstractController { 24 | 25 | public function modifyAction(CacheAdapterInterface $cache, FormFactoryInterface $formFactory, Request $request, 26 | SessionInterface $session, Environment $twig, EntityManagerInterface $em, TranslatorInterface $translator) { 27 | $repo = $em->getRepository($this->container->getParameter('craue_config.entity_name')); 28 | $allStoredSettings = $repo->findAll(); 29 | 30 | $formData = [ 31 | 'settings' => $allStoredSettings, 32 | ]; 33 | 34 | $form = $formFactory->create(ModifySettingsForm::class, $formData); 35 | 36 | if ($request->getMethod() === 'POST') { 37 | $form->handleRequest($request); 38 | 39 | if ($form->isSubmitted() && $form->isValid()) { 40 | // update the cache 41 | foreach ($formData['settings'] as $formSetting) { 42 | $storedSetting = $this->getSettingByName($allStoredSettings, $formSetting->getName()); 43 | if ($storedSetting !== null) { 44 | $cache->set($storedSetting->getName(), $storedSetting->getValue()); 45 | } 46 | } 47 | 48 | $em->flush(); 49 | 50 | if ($session instanceof Session) { 51 | $session->getFlashBag()->set('notice', $translator->trans('settings_changed', [], 'CraueConfigBundle')); 52 | } 53 | 54 | return $this->redirectToRoute($this->container->getParameter('craue_config.redirectRouteAfterModify')); 55 | } 56 | } 57 | 58 | return new Response($twig->render('@CraueConfig/Settings/modify.html.twig', [ 59 | 'form' => $form->createView(), 60 | 'sections' => $this->getSections($allStoredSettings), 61 | ])); 62 | } 63 | 64 | /** 65 | * @param SettingInterface[] $settings 66 | * @return string[] (may also contain a null value) 67 | */ 68 | protected function getSections(array $settings) { 69 | $sections = []; 70 | 71 | foreach ($settings as $setting) { 72 | $section = $setting->getSection(); 73 | if (!in_array($section, $sections, true)) { 74 | $sections[] = $section; 75 | } 76 | } 77 | 78 | sort($sections); 79 | 80 | return $sections; 81 | } 82 | 83 | /** 84 | * @param SettingInterface[] $settings 85 | * @param string $name 86 | * @return SettingInterface|null 87 | */ 88 | protected function getSettingByName(array $settings, $name) { 89 | foreach ($settings as $setting) { 90 | if ($setting->getName() === $name) { 91 | return $setting; 92 | } 93 | } 94 | 95 | return null; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /CraueConfigBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright 2011-2023 Christian Raue 10 | * @license http://opensource.org/licenses/mit-license.php MIT License 11 | */ 12 | class CraueConfigBundle extends Bundle { 13 | } 14 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011-2023 Christian Raue 14 | * @license http://opensource.org/licenses/mit-license.php MIT License 15 | */ 16 | class Configuration implements ConfigurationInterface { 17 | 18 | /** 19 | * {@inheritDoc} 20 | */ 21 | public function getConfigTreeBuilder() : TreeBuilder { 22 | $supportedDrivers = ['doctrine_orm']; 23 | 24 | $treeBuilder = new TreeBuilder('craue_config'); 25 | 26 | $treeBuilder->getRootNode() 27 | ->children() 28 | ->enumNode('db_driver') 29 | ->values($supportedDrivers) 30 | ->defaultValue($supportedDrivers[0]) 31 | ->end() 32 | ->scalarNode('entity_name') 33 | ->defaultValue(Setting::class) 34 | ->end() 35 | ->end() 36 | ; 37 | 38 | return $treeBuilder; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /DependencyInjection/CraueConfigExtension.php: -------------------------------------------------------------------------------- 1 | 16 | * @copyright 2011-2023 Christian Raue 17 | * @license http://opensource.org/licenses/mit-license.php MIT License 18 | */ 19 | class CraueConfigExtension extends Extension implements PrependExtensionInterface { 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function load(array $configs, ContainerBuilder $container) : void { 25 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 26 | $loader->load('controller.xml'); 27 | $loader->load('form.xml'); 28 | $loader->load('twig.xml'); 29 | $loader->load('util.xml'); 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public function prepend(ContainerBuilder $container) : void { 36 | $config = $this->processConfiguration(new Configuration(), $container->getExtensionConfig($this->getAlias())); 37 | 38 | $container->setParameter('craue_config.db_driver.' . $config['db_driver'], true); 39 | $container->setParameter('craue_config.entity_name', $config['entity_name']); 40 | 41 | $container->prependExtensionConfig('doctrine', [ 42 | 'orm' => [ 43 | 'mappings' => [ 44 | 'CraueConfigBundle' => [ 45 | 'type' => 'xml', 46 | 'dir' => 'Resources/config/' . ($config['entity_name'] === Setting::class ? 'doctrine-mapping-with-default-setting' : 'doctrine-mapping'), 47 | 'prefix' => 'Craue\ConfigBundle\Entity', 48 | ], 49 | ], 50 | ], 51 | ]); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Entity/BaseSetting.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright 2011-2023 Christian Raue 10 | * @license http://opensource.org/licenses/mit-license.php MIT License 11 | */ 12 | abstract class BaseSetting implements SettingInterface { 13 | 14 | /** 15 | * @var string 16 | * @Assert\NotBlank 17 | */ 18 | protected $name; 19 | 20 | /** 21 | * @var string|null 22 | */ 23 | protected $value; 24 | 25 | /** 26 | * @var string|null 27 | */ 28 | protected $section; 29 | 30 | public function setName($name) { 31 | $this->name = $name; 32 | } 33 | 34 | public function getName() { 35 | return $this->name; 36 | } 37 | 38 | public function setValue($value) { 39 | $this->value = $value; 40 | } 41 | 42 | public function getValue() { 43 | return $this->value; 44 | } 45 | 46 | public function setSection($section) { 47 | $this->section = $section; 48 | } 49 | 50 | public function getSection() { 51 | return $this->section; 52 | } 53 | 54 | /** 55 | * Creates a {@code SettingInterface}. 56 | * @param string $name 57 | * @param string|null $value 58 | * @param string|null $section 59 | * @return SettingInterface 60 | */ 61 | public static function create($name, $value = null, $section = null) { 62 | $setting = new static(); 63 | $setting->setName($name); 64 | $setting->setValue($value); 65 | $setting->setSection($section); 66 | 67 | return $setting; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Entity/Setting.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011-2023 Christian Raue 8 | * @license http://opensource.org/licenses/mit-license.php MIT License 9 | */ 10 | class Setting extends BaseSetting { 11 | } 12 | -------------------------------------------------------------------------------- /Entity/SettingInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011-2023 Christian Raue 8 | * @license http://opensource.org/licenses/mit-license.php MIT License 9 | */ 10 | interface SettingInterface { 11 | 12 | function setName($name); 13 | function getName(); 14 | 15 | function setValue($value); 16 | function getValue(); 17 | 18 | function setSection($section); 19 | function getSection(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Form/ModifySettingsForm.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011-2023 Christian Raue 14 | * @license http://opensource.org/licenses/mit-license.php MIT License 15 | */ 16 | class ModifySettingsForm extends AbstractType { 17 | 18 | /** 19 | * {@inheritDoc} 20 | */ 21 | public function buildForm(FormBuilderInterface $builder, array $options) : void { 22 | $settingsForm = $builder->create('settings', FormType::class); 23 | 24 | foreach ($options['data']['settings'] as $setting) { 25 | /* @var $setting SettingInterface */ 26 | $settingsForm->add($setting->getName(), SettingType::class, [ 27 | 'data' => $setting, 28 | ]); 29 | } 30 | 31 | $builder->add($settingsForm); 32 | } 33 | 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | public function getBlockPrefix() : string { 38 | return 'craue_config_modifySettings'; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Form/Type/SettingType.php: -------------------------------------------------------------------------------- 1 | 16 | * @copyright 2011-2023 Christian Raue 17 | * @license http://opensource.org/licenses/mit-license.php MIT License 18 | */ 19 | class SettingType extends AbstractType { 20 | 21 | /** 22 | * @var string 23 | */ 24 | protected $entityName; 25 | 26 | public function __construct($entityName) { 27 | $this->entityName = $entityName; 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public function buildForm(FormBuilderInterface $builder, array $options) : void { 34 | $builder->add('value', null, [ 35 | 'required' => false, 36 | 'translation_domain' => 'CraueConfigBundle', 37 | ]); 38 | 39 | $builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) : void { 40 | $form = $event->getForm(); 41 | $submittedData = $event->getData(); 42 | 43 | // replace non-submitted values by defaults - this avoids nulling values of settings missing from the request 44 | // idea from https://stackoverflow.com/questions/11687760/form-avoid-setting-null-to-non-submitted-field/16522446#16522446 45 | foreach ($form->all() as $name => $child) { 46 | if (!isset($submittedData[$name])) { 47 | $submittedData[$name] = $child->getData(); 48 | } 49 | } 50 | 51 | $event->setData($submittedData); 52 | }); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function finishView(FormView $view, FormInterface $form, array $options) : void { 59 | /* @var $setting SettingInterface */ 60 | $setting = $form->getData(); 61 | 62 | $view->children['value']->vars['label'] = $setting->getName(); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | public function configureOptions(OptionsResolver $resolver) : void { 69 | $resolver->setDefaults([ 70 | 'data_class' => $this->entityName, 71 | ]); 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public function getBlockPrefix() : string { 78 | return 'craue_config_setting'; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011-2023 Christian Raue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Information 2 | 3 | [![Tests](https://github.com/craue/CraueConfigBundle/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/craue/CraueConfigBundle/actions/workflows/tests.yml) 4 | [![Coverage Status](https://coveralls.io/repos/github/craue/CraueConfigBundle/badge.svg?branch=master)](https://coveralls.io/github/craue/CraueConfigBundle?branch=master) 5 | 6 | CraueConfigBundle manages configuration settings stored in the database and makes them accessible via a service in your 7 | Symfony project. These settings are similar to those defined in `parameters.yml` but can be modified at runtime, e.g. 8 | by an admin user. 9 | 10 | # Installation 11 | 12 | ## Get the bundle 13 | 14 | Let Composer download and install the bundle by running 15 | 16 | ```sh 17 | composer require craue/config-bundle 18 | ``` 19 | 20 | in a shell. 21 | 22 | ## Enable the bundle 23 | 24 | If you don't use Symfony Flex, register the bundle manually: 25 | 26 | ```php 27 | // in config/bundles.php 28 | return [ 29 | // ... 30 | Craue\ConfigBundle\CraueConfigBundle::class => ['all' => true], 31 | ]; 32 | ``` 33 | 34 | ## Create the table 35 | 36 | Preferably you do this by calling 37 | 38 | ```sh 39 | # in a shell 40 | php bin/console doctrine:migrations:diff 41 | php bin/console doctrine:migrations:migrate 42 | ``` 43 | 44 | or 45 | 46 | ```sh 47 | # in a shell 48 | php bin/console doctrine:schema:update 49 | ``` 50 | 51 | or however you like. 52 | 53 | ## Add the route to manage settings (optional) 54 | 55 | You can either import the default routing configuration 56 | 57 | ```yaml 58 | # in app/config/routing.yml 59 | craue_config_settings: 60 | resource: "@CraueConfigBundle/Resources/config/routing/settings.xml" 61 | prefix: /settings 62 | ``` 63 | 64 | ...or add your own to have full control over the URL pattern. 65 | 66 | ```yaml 67 | # in app/config/routing.yml 68 | craue_config_settings_modify: 69 | path: /settings/modify 70 | defaults: 71 | _controller: Craue\ConfigBundle\Controller\SettingsController::modifyAction 72 | ``` 73 | 74 | Some CSS is needed to render the form correctly. Install the assets in your project: 75 | 76 | ```sh 77 | # in a shell 78 | php bin/console assets:install --symlink web 79 | ``` 80 | 81 | # Usage 82 | 83 | ## Defining settings 84 | 85 | This bundle does _not_ provide functionality to create new settings because this would make no sense at runtime. 86 | Those settings will be used in your application and thus code needs to be written for that. 87 | This means that you have to create new settings in the database table `craue_config_setting` yourself, e.g. using a 88 | migration. 89 | 90 | ## Managing settings' values 91 | 92 | If you added the route described above you can manage the values of all defined settings in a simple form. 93 | By default, you can access that form by browsing to `/settings/modify`. 94 | But you probably want to limit access to this form in your security configuration. 95 | 96 | ## Reading and writing settings 97 | 98 | For accessing settings, the bundle provides the service `Craue\ConfigBundle\Util\Config`. 99 | To use it directly in a controller, either add an autowired type-hinted argument to the action... 100 | 101 | ```php 102 | // in src/Controller/MyController.php 103 | use Craue\ConfigBundle\Util\Config; 104 | 105 | public function indexAction(Config $config) { 106 | // use $config 107 | } 108 | ``` 109 | 110 | ...or let your controller extend `Symfony\Bundle\FrameworkBundle\Controller\AbstractController` and make the 111 | service alias `craue_config` available by defining `getSubscribedServices`: 112 | 113 | ```php 114 | // in src/Controller/MyController.php 115 | use Craue\ConfigBundle\Util\Config; 116 | 117 | public function indexAction() { 118 | // use $this->get('craue_config') 119 | } 120 | 121 | public static function getSubscribedServices() { 122 | return array_merge(parent::getSubscribedServices(), [ 123 | 'craue_config' => Config::class, 124 | ]); 125 | } 126 | ``` 127 | 128 | The service defines the following methods: 129 | 130 | - `all()` - get an associative array of all defined settings and their values 131 | - `get($name)` - get the value of the specified setting 132 | - `getBySection($section)` - like `all()`, but get only settings within the specified section (or those without a section if explicitly passing `null`) 133 | - `set($name, $value)` - set the value of the specified setting 134 | - `setMultiple([$name1 => $value1, $name2 => $value2])` - set values for multiple settings at once 135 | 136 | Keep in mind that each setting has to be present, or an exception will be thrown. 137 | 138 | ## Usage in Twig templates 139 | 140 | The Twig extension in this bundle supports reading settings directly in your template. 141 | 142 | ```twig 143 | {{ craue_setting('name-of-a-setting') }} 144 | ``` 145 | 146 | # Enable caching (optional) 147 | 148 | To reduce the number of database queries, you can set up a cache for settings. First, you have to choose which cache 149 | implementation you'd like to use. Currently, there are adapters available for: 150 | - [DoctrineCacheBundle](https://github.com/doctrine/DoctrineCacheBundle) 151 | - [Symfony Cache component](https://symfony.com/doc/current/components/cache.html) 152 | 153 | Refer to the documentation of each implementation for details and read on in the corresponding section below. When 154 | done, `CraueConfigBundle` will automatically cache settings (using the built-in `craue_config_cache_adapter` service). 155 | 156 | Keep in mind to clear the cache (if needed) after modifying settings outside of your app (e.g. by Doctrine migrations): 157 | 158 | ```sh 159 | # in a shell 160 | php bin/console doctrine:cache:clear craue_config_cache 161 | ``` 162 | 163 |
164 | Cache implementation: DoctrineCacheBundle 165 | 166 | Set the parameter `craue_config.cache_adapter.class` appropriately and configure a so-called cache provider with the 167 | alias `craue_config_cache_provider`: 168 | 169 | ```yaml 170 | # in app/config/config.yml 171 | parameters: 172 | craue_config.cache_adapter.class: Craue\ConfigBundle\CacheAdapter\DoctrineCacheBundleAdapter 173 | 174 | doctrine_cache: 175 | providers: 176 | craue_config_cache: 177 | apc: ~ 178 | namespace: craue_config 179 | aliases: 180 | - craue_config_cache_provider 181 | ``` 182 |
183 | 184 |
185 | Cache implementation: Symfony Cache component 186 | 187 | Set the parameter `craue_config.cache_adapter.class` appropriately and configure a so-called cache pool with the 188 | service id `craue_config_cache_provider`: 189 | 190 | ```yaml 191 | # in app/config/config.yml 192 | parameters: 193 | craue_config.cache_adapter.class: Craue\ConfigBundle\CacheAdapter\SymfonyCacheComponentAdapter 194 | 195 | services: 196 | craue_config_cache_provider: 197 | class: Symfony\Component\Cache\Adapter\FilesystemAdapter 198 | public: false 199 | arguments: 200 | - 'craue_config' 201 | - 0 202 | - '%kernel.cache_dir%' 203 | ``` 204 |
205 | 206 | # Customization 207 | 208 | ## Redirect to a different page after submitting the built-in form 209 | 210 | If you've enabled the build-in form, you can define where to redirect on successfully saving the changes by setting the 211 | target route name: 212 | 213 | ```yaml 214 | # in app/config/parameters.yml 215 | parameters: 216 | craue_config.redirectRouteAfterModify: craue_config_settings_modify 217 | ``` 218 | 219 | ## Rendering of settings in sections 220 | 221 | If you want to render settings in a group (called section here), you'll have to assign those settings a common section 222 | name (in the database). Optionally, you can influence the order of these sections: 223 | 224 | ```yaml 225 | # in app/config/parameters.yml 226 | parameters: 227 | craue_config.configTemplate.sectionOrder: [section1, section2, section3] 228 | ``` 229 | 230 | Settings without a section will be rendered at first. Sections without explicit ordering are rendered at last. 231 | 232 | ## Translation 233 | 234 | You can add translations for all settings (and sections) to be shown in the form by adding them to translation files 235 | with the `CraueConfigBundle` domain, e.g. 236 | 237 | ```yaml 238 | # in app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml 239 | name-of-a-setting: name of the setting 240 | 241 | # in app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml 242 | name-of-a-setting: Name der Einstellung 243 | ``` 244 | 245 | ## Using a custom entity for settings 246 | 247 | The custom entity has to provide a mapping for the field `value`. The class `BaseSetting` defines this field, but no 248 | mapping for it. This allows easy overriding, including the data type. In the following example, the `value` field will 249 | be mapped to a `text` column, which will in turn render the built-in form fields as `textarea`. 250 | 251 | So create the entity and its appropriate mapping: 252 | 253 | ```php 254 | // src/MyCompany/MyBundle/Entity/MySetting.php 255 | use Craue\ConfigBundle\Entity\BaseSetting; 256 | use Doctrine\ORM\Mapping as ORM; 257 | 258 | /** 259 | * @ORM\Entity(repositoryClass="Craue\ConfigBundle\Repository\SettingRepository") 260 | * @ORM\Table(name="my_setting") 261 | */ 262 | class MySetting extends BaseSetting { 263 | 264 | /** 265 | * @var string|null 266 | * @ORM\Column(name="value", type="text", nullable=true) 267 | */ 268 | protected $value; 269 | 270 | /** 271 | * @var string|null 272 | * @ORM\Column(name="comment", type="string", nullable=true) 273 | */ 274 | protected $comment; 275 | 276 | public function setComment($comment) { 277 | $this->comment = $comment; 278 | } 279 | 280 | public function getComment() { 281 | return $this->comment; 282 | } 283 | 284 | } 285 | ``` 286 | 287 | And make the bundle aware of it: 288 | 289 | ```yaml 290 | # in app/config/config.yml 291 | craue_config: 292 | entity_name: MyCompany\MyBundle\Entity\MySetting 293 | ``` 294 | -------------------------------------------------------------------------------- /Repository/SettingRepository.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright 2011-2023 Christian Raue 11 | * @license http://opensource.org/licenses/mit-license.php MIT License 12 | */ 13 | class SettingRepository extends EntityRepository { 14 | 15 | /** 16 | * @param string[] $names 17 | * @return SettingInterface[] Array of settings, indexed by name. 18 | */ 19 | public function findByNames(array $names) { 20 | return $this->createQueryBuilder('s', 's.name') 21 | ->where('s.name IN (:names)') 22 | ->getQuery() 23 | ->execute(['names' => $names]) 24 | ; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Resources/config/controller.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Resources/config/doctrine-mapping-with-default-setting/BaseSetting.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Resources/config/doctrine-mapping-with-default-setting/Setting.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Resources/config/doctrine-mapping/BaseSetting.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Resources/config/form.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | 13 | 14 | %craue_config.entity_name% 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Resources/config/routing/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | Craue\ConfigBundle\Controller\SettingsController::modifyAction 13 | 14 | 15 | -------------------------------------------------------------------------------- /Resources/config/twig.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | %craue_config.configTemplate.sectionOrder% 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Resources/config/util.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | Craue\ConfigBundle\CacheAdapter\NullAdapter 13 | craue_config_settings_modify 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | %craue_config.entity_name% 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Resources/public/css/form.css: -------------------------------------------------------------------------------- 1 | .craue_config_settings_modify label:after { 2 | content: ': '; 3 | } 4 | 5 | .craue_config_settings_modify input[type=text] { 6 | width: 300px; 7 | } 8 | -------------------------------------------------------------------------------- /Resources/translations/CraueConfigBundle.de.yml: -------------------------------------------------------------------------------- 1 | no_settings_defined: Es wurden noch keine Einstellungen definiert. 2 | modify_settings: übernehmen 3 | settings_changed: Die Einstellungen wurden geändert. 4 | -------------------------------------------------------------------------------- /Resources/translations/CraueConfigBundle.en.yml: -------------------------------------------------------------------------------- 1 | no_settings_defined: There are no settings defined yet. 2 | modify_settings: apply 3 | settings_changed: The settings were changed. 4 | -------------------------------------------------------------------------------- /Resources/translations/CraueConfigBundle.nl.yml: -------------------------------------------------------------------------------- 1 | no_settings_defined: Er zijn op dit moment geen instellingen gedefinieerd. 2 | modify_settings: toepassen 3 | settings_changed: De instellingen zijn opgeslagen. 4 | -------------------------------------------------------------------------------- /Resources/translations/CraueConfigBundle.ru.yml: -------------------------------------------------------------------------------- 1 | no_settings_defined: Никаких настроек пока не создано. 2 | modify_settings: применить 3 | settings_changed: Настройки успешно сохранены. 4 | -------------------------------------------------------------------------------- /Resources/views/Settings/modify.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@CraueConfig/layout.html.twig' %} 2 | 3 | {% block craue_config_content %} 4 | {% include '@CraueConfig/Settings/modify_content.html.twig' %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /Resources/views/Settings/modify_content.html.twig: -------------------------------------------------------------------------------- 1 | {% if form.settings | length > 0 %} 2 | {% include '@CraueConfig/Settings/modify_form.html.twig' %} 3 | {% else %} 4 | {% include '@CraueConfig/Settings/modify_form_empty.html.twig' %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /Resources/views/Settings/modify_form.html.twig: -------------------------------------------------------------------------------- 1 | {{ form_start(form, {'attr': {'class': 'craue_config_settings_modify'}}) }} 2 | {{ form_errors(form) }} 3 | 4 | {% for section in sections | craue_sortSections %} 5 | {% if section is not empty %} 6 |
7 | {{ section | trans({}, 'CraueConfigBundle') }} 8 | {% endif %} 9 | 10 | {% for setting in form.settings %} 11 | {% if setting.vars.value.section == section %} 12 | {{ form_row(setting.value) }} 13 | {% endif %} 14 | {% endfor %} 15 | 16 | {% if section is not empty %} 17 |
18 | {% endif %} 19 | {% endfor %} 20 | 21 | {{ form_rest(form) }} 22 | 23 | 24 | {{ form_end(form) }} 25 | -------------------------------------------------------------------------------- /Resources/views/Settings/modify_form_empty.html.twig: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ 'no_settings_defined' | trans({}, 'CraueConfigBundle') }} 4 |

5 |
6 | -------------------------------------------------------------------------------- /Resources/views/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block stylesheets %} 6 | 7 | {% endblock %} 8 | 9 | 10 | {% for type, flashMessages in app.session.flashbag.all() %} 11 | {% for flashMessage in flashMessages %} 12 |
13 | {{- flashMessage -}} 14 |
15 | {% endfor %} 16 | {% endfor %} 17 | 18 | {% block craue_config_content %} 19 | {% endblock %} 20 | 21 | 22 | -------------------------------------------------------------------------------- /Twig/Extension/ConfigTemplateExtension.php: -------------------------------------------------------------------------------- 1 | 12 | * @copyright 2011-2023 Christian Raue 13 | * @license http://opensource.org/licenses/mit-license.php MIT License 14 | */ 15 | class ConfigTemplateExtension extends AbstractExtension { 16 | 17 | /** 18 | * @var string[] 19 | */ 20 | protected $sectionOrder = []; 21 | 22 | /** 23 | * @var Config 24 | */ 25 | protected $config; 26 | 27 | /** 28 | * @param string[] $sectionOrder The order in which sections will be rendered. 29 | */ 30 | public function setSectionOrder(array $sectionOrder = []) { 31 | $this->sectionOrder = $sectionOrder; 32 | } 33 | 34 | /** 35 | * @param Config $config 36 | */ 37 | public function setConfig(Config $config) { 38 | $this->config = $config; 39 | } 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | public function getName() { 45 | return 'craue_config_template'; 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | public function getFilters() : array { 52 | return [ 53 | new TwigFilter('craue_sortSections', [$this, 'sortSections']), 54 | ]; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public function getFunctions() : array { 61 | return [ 62 | new TwigFunction('craue_setting', [$this, 'getSetting']), 63 | ]; 64 | } 65 | 66 | /** 67 | * @param string[] $sections 68 | * @return string[] 69 | */ 70 | public function sortSections(array $sections) { 71 | $finalSectionOrder = []; 72 | 73 | // add null section first (if it exists) 74 | $nullIndex = array_search(null, $sections, true); 75 | if ($nullIndex !== false) { 76 | $finalSectionOrder[] = $sections[$nullIndex]; 77 | unset($sections[$nullIndex]); 78 | } 79 | 80 | // add sections in given order 81 | foreach (array_intersect($this->sectionOrder, $sections) as $section) { 82 | $finalSectionOrder[] = $section; 83 | } 84 | 85 | // add remaining sections 86 | foreach (array_diff($sections, $this->sectionOrder) as $section) { 87 | $finalSectionOrder[] = $section; 88 | } 89 | 90 | return $finalSectionOrder; 91 | } 92 | 93 | /** 94 | * @param string $name Name of the setting. 95 | * @return string|null Value of the setting. 96 | * @throws \RuntimeException If the setting is not defined. 97 | */ 98 | public function getSetting($name) { 99 | return $this->config->get($name); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /UPGRADE-2.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.x to 2.0 2 | 3 | ## Database 4 | 5 | After upgrading the bundle, you should migrate your database to remove the useless `UNIQUE` index for field `name` from table `craue_config_setting`. The `PRIMARY` index for field `name` will remain. 6 | 7 | ## Built-in form/template 8 | 9 | - If you're overriding the template `modify_form.html.twig` in your project, you probably need to adapt it to changes in the built-in form type. Before, two hidden form fields `name` and `section` were added only to read their `value` variable in the template. They have been removed now. But you can still access the properties of settings in the template via the `value` variable of each `setting` form field: 10 | 11 | before: 12 | ```twig 13 | {% for setting in form.settings %} 14 | {{ setting.section.vars.value }} 15 | {{ setting.name.vars.value }} 16 | {% endfor %} 17 | ``` 18 | 19 | after: 20 | ```twig 21 | {% for setting in form.settings %} 22 | {{ setting.vars.value.section }} 23 | {{ setting.vars.value.name }} 24 | {% endfor %} 25 | ``` 26 | 27 | ## Services 28 | 29 | - If you've used the DI parameter `craue_config.configTemplate.class`, you now need to override the Twig extension with service id `twig.extension.craue_config_template` instead. 30 | - If you've used the DI parameter `craue_config.config.class`, you now need to override the service with id `craue_config` instead. 31 | -------------------------------------------------------------------------------- /Util/Config.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011-2023 Christian Raue 14 | * @license http://opensource.org/licenses/mit-license.php MIT License 15 | */ 16 | class Config { 17 | 18 | /** 19 | * @var CacheAdapterInterface 20 | */ 21 | protected $cache; 22 | 23 | /** 24 | * @var EntityManager 25 | */ 26 | protected $em; 27 | 28 | /** 29 | * @var SettingRepository|null 30 | */ 31 | protected $repo; 32 | 33 | /** 34 | * @var string 35 | */ 36 | protected $entityName; 37 | 38 | public function __construct(CacheAdapterInterface $cache = null) { 39 | $this->setCache($cache ?? new NullAdapter()); 40 | } 41 | 42 | public function setCache(CacheAdapterInterface $cache) { 43 | $this->cache = $cache; 44 | } 45 | 46 | public function setEntityManager(EntityManager $em) { 47 | if ($this->em !== $em) { 48 | if ($this->em !== null) { 49 | $this->cache->clear(); 50 | } 51 | 52 | $this->em = $em; 53 | $this->repo = null; 54 | } 55 | } 56 | 57 | public function setEntityName($entityName) { 58 | $this->entityName = $entityName; 59 | $this->repo = null; 60 | } 61 | 62 | /** 63 | * @param string $name Name of the setting. 64 | * @return string|null Value of the setting. 65 | * @throws \RuntimeException If the setting is not defined. 66 | */ 67 | public function get($name) { 68 | if ($this->cache->has($name)) { 69 | return $this->cache->get($name); 70 | } 71 | 72 | $setting = $this->getRepo()->findOneBy([ 73 | 'name' => $name, 74 | ]); 75 | 76 | if ($setting === null) { 77 | throw $this->createNotFoundException($name); 78 | } 79 | 80 | $this->cache->set($name, $setting->getValue()); 81 | 82 | return $setting->getValue(); 83 | } 84 | 85 | /** 86 | * @param string $name Name of the setting to update. 87 | * @param string|null $value New value for the setting. 88 | * @throws \RuntimeException If the setting is not defined. 89 | */ 90 | public function set($name, $value) { 91 | $setting = $this->getRepo()->findOneBy([ 92 | 'name' => $name, 93 | ]); 94 | 95 | if ($setting === null) { 96 | throw $this->createNotFoundException($name); 97 | } 98 | 99 | $setting->setValue($value); 100 | $this->em->flush(); 101 | 102 | $this->cache->set($name, $value); 103 | } 104 | 105 | /** 106 | * @param array $newSettings List of settings (as name => value) to update. 107 | * @throws \RuntimeException If at least one of the settings is not defined. 108 | */ 109 | public function setMultiple(array $newSettings) { 110 | if ($newSettings === []) { 111 | return; 112 | } 113 | 114 | $settings = $this->getRepo()->findByNames(array_keys($newSettings)); 115 | 116 | foreach ($newSettings as $name => $value) { 117 | if (!isset($settings[$name])) { 118 | throw $this->createNotFoundException($name); 119 | } 120 | 121 | $settings[$name]->setValue($value); 122 | } 123 | 124 | $this->em->flush(); 125 | 126 | $this->cache->setMultiple($newSettings); 127 | } 128 | 129 | /** 130 | * @return array with name => value 131 | */ 132 | public function all() { 133 | $settings = $this->getAsNamesAndValues($this->getRepo()->findAll()); 134 | 135 | $this->cache->setMultiple($settings); 136 | 137 | return $settings; 138 | } 139 | 140 | /** 141 | * @param string|null $section Name of the section to fetch settings for. 142 | * @return array with name => value 143 | */ 144 | public function getBySection($section) { 145 | $settings = $this->getAsNamesAndValues($this->getRepo()->findBy(['section' => $section])); 146 | 147 | $this->cache->setMultiple($settings); 148 | 149 | return $settings; 150 | } 151 | 152 | /** 153 | * @param SettingInterface[] $settings 154 | * @return array with name => value 155 | */ 156 | protected function getAsNamesAndValues(array $settings) { 157 | $result = []; 158 | 159 | foreach ($settings as $setting) { 160 | $result[$setting->getName()] = $setting->getValue(); 161 | } 162 | 163 | return $result; 164 | } 165 | 166 | /** 167 | * @return SettingRepository 168 | */ 169 | protected function getRepo() { 170 | if ($this->repo === null) { 171 | $repo = $this->em->getRepository($this->entityName); 172 | 173 | if (!$repo instanceof SettingRepository) { 174 | throw new \RuntimeException(sprintf('Entity repository of type "%s" expected, but got "%s".', SettingRepository::class, get_class($repo))); 175 | } 176 | 177 | $this->repo = $repo; 178 | } 179 | 180 | return $this->repo; 181 | } 182 | 183 | /** 184 | * @param string $name Name of the setting. 185 | * @return \RuntimeException 186 | */ 187 | protected function createNotFoundException($name) { 188 | return new \RuntimeException(sprintf('Setting "%s" couldn\'t be found.', $name)); 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "craue/config-bundle", 3 | "description": "Database-stored settings made available via a service for your Symfony project.", 4 | "license": "MIT", 5 | "type": "symfony-bundle", 6 | "keywords": [ 7 | "config", 8 | "symfony" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Christian Raue", 13 | "email": "christian.raue@gmail.com" 14 | }, 15 | { 16 | "name": "Symfony Community", 17 | "homepage": "https://github.com/craue/CraueConfigBundle/contributors" 18 | } 19 | ], 20 | "homepage": "https://github.com/craue/CraueConfigBundle", 21 | "require": { 22 | "php": "^7.3 || ^8", 23 | "doctrine/doctrine-bundle": "^1.6.12 || ^2", 24 | "psr/simple-cache": "^1 || ^2 || ^3", 25 | "symfony/cache": "^4.4 || ^5.4 || ^6.3", 26 | "symfony/config": "^4.4 || ^5.4 || ^6.3", 27 | "symfony/dependency-injection": "^4.4 || ^5.4 || ^6.3", 28 | "symfony/form": "^4.4 || ^5.4 || ^6.3", 29 | "symfony/framework-bundle": "^4.4 || ^5.4 || ^6.3", 30 | "symfony/http-foundation": "^4.4 || ^5.4 || ^6.3", 31 | "symfony/http-kernel": "^4.4 || ^5.4 || ^6.3", 32 | "symfony/options-resolver": "^4.4 || ^5.4 || ^6.3", 33 | "symfony/validator": "^4.4 || ^5.4 || ^6.3" 34 | }, 35 | "require-dev": { 36 | "composer/semver": "^3.0", 37 | "craue/translations-tests": "^1.0", 38 | "doctrine/orm": "^2.5.14", 39 | "phpstan/extension-installer": "^1.1", 40 | "phpstan/phpstan": "^1.4", 41 | "phpstan/phpstan-deprecation-rules": "^1.0", 42 | "phpstan/phpstan-strict-rules": "^1.1", 43 | "phpstan/phpstan-symfony": "^1.1", 44 | "phpunit/phpunit": "^9.6", 45 | "symfony/asset": "^4.4 || ^5.4 || ^6.3", 46 | "symfony/browser-kit": "^4.4 || ^5.4 || ^6.3", 47 | "symfony/phpunit-bridge": "^6.3", 48 | "symfony/twig-bundle": "^4.4 || ^5.4 || ^6.3", 49 | "symfony/web-profiler-bundle": "^4.4 || ^5.4 || ^6.3" 50 | }, 51 | "conflict": { 52 | "doctrine/cache": "<1.6" 53 | }, 54 | "minimum-stability": "stable", 55 | "autoload": { 56 | "psr-4": { 57 | "Craue\\ConfigBundle\\": "" 58 | }, 59 | "exclude-from-classmap": [ 60 | "/Tests/" 61 | ] 62 | }, 63 | "config": { 64 | "allow-plugins": { 65 | "phpstan/extension-installer": true, 66 | "symfony/flex": true 67 | }, 68 | "sort-packages": true 69 | }, 70 | "extra": { 71 | "branch-alias": { 72 | "dev-master": "3.0.x-dev" 73 | } 74 | } 75 | } 76 | --------------------------------------------------------------------------------