namespace App\Entity;
#[ORM\Entity(repositoryClass: UserPreferenceRepository::class)]
#[ORM\Table(name: 'user_preferences')]
#[ORM\UniqueConstraint(name: 'uniq_preference_context_key', columns: ['context', 'name'])]
class UserPreference extends BasePreference
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id = null;
public function getId(): ?Uuid
{
return $this->id;
}
}
#[ORM\Entity]
#[ORM\Table(name: 'user_selections')]
class UserSelection extends BaseSelection
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id = null;
public function getId(): ?Uuid
{
return $this->id;
}
}services:
app.persistent_state.users_resolver:
class: Tito10047\PersistentStateBundle\Resolver\ObjectContextResolver
arguments:
$class: App\Entity\User\User
$prefix: "user_"
app.storage.doctrine:
class: Tito10047\PersistentStateBundle\Preference\Storage\PreferenceDoctrineStorage
arguments:
- '@doctrine.orm.entity_manager'
- App\Entity\UserPreference
app.selection.storage.doctrine:
class: Tito10047\PersistentStateBundle\Selection\Storage\SelectionDoctrineStorage
arguments:
- '@doctrine.orm.entity_manager'
- App\Entity\UserSelection
persistent_state.transformer.card_transformer:
class: Tito10047\PersistentStateBundle\Transformer\ObjectIdValueTransformer
arguments:
$class: App\Entity\Card
persistent_state:
preference:
managers:
default:
storage: '@persistent_state.preference.storage.session'
doctrine:
storage: '@app.storage.doctrine'
selection:
managers:
default:
storage: '@persistent_state.selection.storage.session'
transformer: '@persistent_state.transformer.card_transformer'
db:
storage: '@app.selection.storage.doctrine'use \Symfony\Component\DependencyInjection\Attribute\Autowire;
use \Symfony\Component\HttpFoundation\Request;
use \Tito10047\PersistentStateBundle\Preference\Service\PreferenceInterface;
use \Tito10047\PersistentStateBundle\Preference\Service\PreconfiguredPreferenceInterface;
use \Tito10047\PersistentStateBundle\Preference\Service\PreferenceManagerInterface;
use \Tito10047\PersistentStateBundle\Selection\Service\SelectionManagerInterface;
use \Tito10047\PersistentStateBundle\Selection\Service\SelectionInterface;
use \Doctrine\ORM\EntityManagerInterface;
use \App\Entity\User;
use \App\Entity\Company;
use \App\Entity\Product;
class Foo{
private ?PreferenceInterface $storage = null;
public function __construct(
private readonly PreconfiguredPreferenceInterface $sessionPrefManager,
#[Autowire(service: 'persistent_state.preference.manager.doctrine')]
private readonly PreferenceManagerInterface $doctrinePrefManager,
#[Autowire(service: 'persistent_state.selection.manager.db')]
private readonly SelectionManagerInterface $dbSelectionManager,
private readonly SelectionManagerInterface $selectionManager,
private readonly EntityManagerInterface $em
) {
if ($user = $this->getUser()) {
$this->storage = $doctrinePrefManager->getPreference($user);
} else {
$this->storage = $sessionPrefManager->getPreference("user");
}
}
public function bar(User $user, Company $company, Product $product, Request $request){
$userPref = $this->storage;
$companyPref = $this->doctrinePrefManager->getPreference($company);
$cartSelection = $this->selectionManager->getSelection("card", $this->getUser());
$companySelection = $this->dbSelectionManager->getSelection("products", $company);
$cartSelection->select($product, [
'quantity' => $request->get('qty', 1),
'added_at' => new \DateTime()
]);
$companySelection->select($product);
$userPref->set('foo', 'bar');
$userPref->set('baz', [1,2,3]);
$companyPref->set('foo2', 'bar');
$companyPref->set('baz2', [1,2,3]);
$this->em->flush();
$foo = $userPref->get('foo');
$baz = $userPref->get('baz');
$foo2 = $companyPref->get('foo2');
$baz2 = $companyPref->get('baz2');
$selectedItems = $cartSelection->getSelectedIdentifiers();
$selectedProducts = $companySelection->getSelectedIdentifiers();
$cartSelection->destroy();
}
}<div>
User Foo: {{ preference(user, 'foo') }}<br>
Company pref: {{ company|pref('foo2') }}
</div>Inspect stored preferences for a specific context directly from CLI.
Usage:
php bin/console debug:preference "user_15" --manager=my_pref_manager
Output example:
Context: user_15
Storage: doctrine
+-------+-------+
| Key | Value |
+-------+-------+
| theme | dark |
| limit | 50 |
+-------+-------+
{% set logs = this.logs %}
{% set isAllSelected = persistent_selection_is_selected_all("main_logs") %}
{% set isCurrentSelected = true %}
{% for log in logs %}
{% set isCurrentSelected = isCurrentSelected and persistent_selection_is_selected("main_logs",log) %}
{% endfor %}
<div persistent_selection_stimulus_controller("main_logs", null,{
selectAllClass:'btn-primary',
unselectAllClass:'btn-outline-secondary',
},"default",true)>
<div class="nav d-flex justify-content-between pb-1">
<div class="d-flex flex-row">
{# SELECT ROWS ON CURRENT PAGE #}
<button
class="btn btn-{% if isCurrentSelected %}primary{% else %}outline-secondary{% endif %} btn-sm text-nowrap m-1"
data-action="{{ persistent_selection_stimulus_controller_name }}#selectCurrentPage"
>
<twig:ux:icon name="bi:check" width="20px" height="20px"/>
{{ 'Select visible'|trans }}
</button>
{# SELECT ROWS ON ALL PAGES #}
<button
class="btn btn-{% if isAllSelected %}primary{% else %}outline-secondary{% endif %} btn-sm text-nowrap m-1"
data-action="{{ persistent_selection_stimulus_controller_name }}#selectAll"
>
<twig:ux:icon name="bi:check-all" width="20px" height="20px"/>
{{ 'Select all'|trans }}
</button>
</div>
{# PERFORM ACTION ON SELECTED ROWS #}
<div>
<button class="btn btn-outline-secondary btn-sm" aria-current="page">
<twig:ux:icon name="game-icons:magic-broom" width="20px" height="20px"/>
{{ 'Fix selected'|trans }}
</button>
</div>
</div>
<ul class="list-group">
{% for log in logs %}
<li>
{{ persistent_selection_row_selector("main_logs",log,{class:"m-1 align-bottom"}) }}
{{ log.name }}
</li>
{% endfor %}
</ul>
</div>class MyController{
public function __construct(
private readonly SelectionManagerInterface $selectionManager,
private readonly EntityManagerInterface $em,
) {}
public function performAction(User $user, Product $product){
$logsSelection = $this->selectionManager->getSelection($user,"main_logs");
foreach($logsSelection->getSelectedIdentifiers() as $logId){
$this->em->remove($this->em->getReference(Log::class,$logId));
}
}
}Notes:
- The
contextargument accepts either a pre-resolved key likeuser_15or any object supported by your configured context resolvers. - The
--manageroption selects which preference manager to use. It maps to the service idpersistent.manager.{name}and defaults todefaultwhen omitted. - The Storage line reflects the underlying storage:
session,doctrine, or the short class name for custom storages. - Non-scalar values are JSON-encoded for readability;
nulland booleans are rendered asnull,true/false.