PHPackages                             whitedigital-eu/settings-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. whitedigital-eu/settings-bundle

ActiveSymfony-bundle

whitedigital-eu/settings-bundle
===============================

Settings bundle

0.1.2(2y ago)0278↓100%2[1 issues](https://github.com/whitedigital-eu/settings-bundle/issues)MITPHPPHP &gt;=8.1.0

Since Jan 13Pushed 2y ago1 watchersCompare

[ Source](https://github.com/whitedigital-eu/settings-bundle)[ Packagist](https://packagist.org/packages/whitedigital-eu/settings-bundle)[ RSS](/packages/whitedigital-eu-settings-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (14)Versions (6)Used By (0)

Settings Bundle
===============

[](#settings-bundle)

### What is it?

[](#what-is-it)

This bundle adds option to define multiple data structures to be stored as individual settings in a database, with caching support

### System Requirements

[](#system-requirements)

PHP 8.1+ Symfony 6.2+

### Installation

[](#installation)

The recommended way to install is via Composer:

```
composer require whitedigital-eu/settings-bundle
```

---

After this, you need to update your database schema to use Settings entity.
If using migrations:

```
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
```

If by schema update:

```
bin/console doctrine:schema:update --force
```

This is it, now you can use settings service. It is configured and autowired as `SettingsService`.

To add a new setting, simply create a class that extends SettingsInterface.

```
use WhiteDigital\SettingsBundle\Contracts\SettingsInterface;

class GasTankInventoryCodeSettings implements SettingsInterface
{
    /** Gāzes balonu materiālu kodi  */
    public ?array $inventoryAltIds = null;
}
```

### Exposing settings to API endpoints via ApiPlatform

[](#exposing-settings-to-api-endpoints-via-apiplatform)

To allow changing of the settings via api platform, create a custom ApiResource class, and provider/processor. And add a custom store normalizer.

Provider:

```
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Exception\ResourceClassNotFoundException;
use ApiPlatform\Metadata\CollectionOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\SettingsResource;
use Psr\Cache\InvalidArgumentException;
use ReflectionException;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use WhiteDigital\SettingsBundle\Repository\SettingsRepository;
use WhiteDigital\SettingsBundle\Service\SettingsService;

/**
 * @implements ProviderInterface
 */
class SettingsResourceProvider extends BaseResourceProvider
{
    public function __construct(
        private readonly SettingsService    $settingsService,
        private readonly SettingsRepository $repository,
        iterable                            $collectionExtensions = [],
    )
    {
        parent::__construct($collectionExtensions);
    }

    /**
     * @throws ReflectionException
     * @throws ExceptionInterface
     * @throws ResourceClassNotFoundException
     * @throws InvalidArgumentException
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        if ($operation instanceof CollectionOperationInterface) {
            return $this->getCollection($operation, $context);
        }
        return $this->getItem($uriVariables['id'], $context);
    }

    /**
     * @param array $context
     * @throws InvalidArgumentException
     */
    public function getCollection(Operation $operation, array $context = []): mixed
    {
        $this->settingsService->populateDatabase();
        return $this->applyFilterExtensionsToCollection($this->repository->createQueryBuilder('s'),
            new QueryNameGenerator(), $operation, $context);
    }

    /**
     * @param array $context
     * @throws ExceptionInterface
     * @throws ResourceClassNotFoundException
     * @throws ReflectionException
     */
    public function getItem(mixed $id, array $context = []): ?SettingsResource
    {
        $entity = $this->repository->find($id);
        if (null !== $entity) {
            return SettingsResource::create($entity, $context);
        }
        return null;
    }
}
```

Processor:

```
use ApiPlatform\Exception\ResourceClassNotFoundException;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use App\ApiResource\SettingsResource;
use Psr\Cache\InvalidArgumentException;
use ReflectionException;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use WhiteDigital\EntityResourceMapper\Mapper\EntityToResourceMapper;
use WhiteDigital\EntityResourceMapper\Mapper\ResourceToEntityMapper;
use WhiteDigital\SettingsBundle\Entity\Settings;
use WhiteDigital\SettingsBundle\Repository\SettingsRepository;
use WhiteDigital\SettingsBundle\Service\SettingsService;

class SettingsResourceProcessor extends BaseResourceProcessor
{
    public function __construct(
        private readonly EntityToResourceMapper $entityToResourceMapper,
        private readonly ResourceToEntityMapper $resourceToEntityMapper,
        private readonly SettingsRepository     $repository,
        private readonly SettingsService        $settingsService,
        private readonly Security               $security,
    )
    {
    }

    /**
     * @throws InvalidArgumentException
     * @throws ResourceClassNotFoundException
     * @throws ReflectionException
     * @throws ExceptionInterface
     */
    public function process(
        mixed     $data,
        Operation $operation,
        array     $uriVariables = [],
        array     $context = []
    ): ?SettingsResource
    {
        if ($operation instanceof Patch) {
            return $this->persist($data, $context);
        }
        throw new MethodNotAllowedHttpException(['GET', 'PATCH']);
    }

    /**
     * @throws InvalidArgumentException
     * @throws ResourceClassNotFoundException
     * @throws ExceptionInterface
     * @throws ReflectionException
     */
    protected function persist(mixed $data, array $context = []): SettingsResource
    {
        /** @var Settings $settings */
        $settings = $data; // already denormalized with custom SettingsStoreNormalizer
        $settings->setLastModifiedBy($this->security->getUser()?->getUserIdentifier());
        $this->repository->save($settings, true);
        $this->settingsService->invalidateCache($settings->getClass());
        return SettingsResource::create($settings, $context);
    }
}
```

Normalizer:

```
use ApiPlatform\Api\IriConverterInterface;
use App\ApiResource\SettingsResource;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use WhiteDigital\SettingsBundle\Enum\SettingsStoreTypeEnum;
use WhiteDigital\SettingsBundle\Repository\SettingsRepository;
use WhiteDigital\SettingsBundle\Service\SettingsService;

class SettingsStoreNormalizer implements DenormalizerInterface
{
    public function __construct(
        private readonly SettingsRepository    $settingsRepository,
        private readonly IriConverterInterface $iriConverter,
    )
    {
    }

    /**
     * @param array $context
     */
    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): object
    {
        $id = $context['uri_variables']['id'];
        if (null === $settingsEntity = $this->settingsRepository->find($id)) {
            throw new \RuntimeException('Settings entity not found by id: ' . $id);
        }

        $existingStore = $settingsEntity->getStore();

        foreach ($data['store'] as $key => $value) {
            $existingStoreValue = $existingStore[$key];
            if ((null !== $value['value']) && (null !== $existingStoreValue->getResourceClass())) {
                $resource = $this->iriConverter->getResourceFromIri($value['value']);
                $resourceClass = SettingsService::getShortName($resource::class);
                if ($existingStoreValue->getResourceClass() !== $resourceClass) {
                    throw new \RuntimeException(sprintf('Resource class %s must match property "%s" class %s',
                        $resourceClass, $key, $existingStore[$key]->getResourceClass()));
                }
            }
            if (SettingsStoreTypeEnum::Date === $existingStoreValue->getType()) { // validate only
                try {
                    if (null !== $value['value']) {
                        new \DateTimeImmutable($value['value']);
                    }
                } catch (\Exception $exception) {
                    throw new \RuntimeException('Date format ' . $value['value'] . ' invalid. Expected Y-m-d.');
                }
            }
            // validate only
            if (null !== $value['value'] && SettingsStoreTypeEnum::Array === $existingStoreValue->getType() && !is_array($value['value'])) {
                throw new \RuntimeException($value['value'] . ' Is not a valid array');
            }
            if (null === $value['value'] && in_array($existingStoreValue->getType(), [
                    SettingsStoreTypeEnum::Integer,
                    SettingsStoreTypeEnum::String,
                    SettingsStoreTypeEnum::Float,
                ], true)) {
                throw new \RuntimeException('Integer, String, Float settings values cannot be null.');
            }
            $existingStoreValue->setValue($value['value']);
            $existingStore[$key] = $existingStoreValue;
        }

        $settingsEntity->setStore($existingStore);

        return $settingsEntity;
    }

    /**
     * @param array $context
     */
    public function supportsDenormalization(
        mixed   $data,
        string  $type,
        ?string $format = null,
        array   $context = []
    ): bool
    {
        return SettingsResource::class === $type;
    }
}
```

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 50% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~159 days

Total

3

Last Release

894d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c4db85e32518c5a6caa2fd625032a2b016ef42d60cf8a101c165cc5c0048b221?d=identicon)[k0d3r1s](/maintainers/k0d3r1s)

![](https://www.gravatar.com/avatar/d54d4c03974bdc6923785b90b54575e77570057e72fd6b9915e06656e60b842e?d=identicon)[andis.cirulis](/maintainers/andis.cirulis)

---

Top Contributors

[![acirulis](https://avatars.githubusercontent.com/u/27766961?v=4)](https://github.com/acirulis "acirulis (2 commits)")[![raraworks](https://avatars.githubusercontent.com/u/18421085?v=4)](https://github.com/raraworks "raraworks (2 commits)")

---

Tags

phpsymfonySymfony Bundleapi-platform

###  Code Quality

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/whitedigital-eu-settings-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/whitedigital-eu-settings-bundle/health.svg)](https://phpackages.com/packages/whitedigital-eu-settings-bundle)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M647](/packages/sylius-sylius)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k16.7M309](/packages/easycorp-easyadmin-bundle)[contao/core-bundle

Contao Open Source CMS

1231.6M2.3k](/packages/contao-core-bundle)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
