Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.21% covered (warning)
84.21%
16 / 19
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserPreferencesTwigExtension
83.33% covered (warning)
83.33%
15 / 18
60.00% covered (warning)
60.00%
3 / 5
9.37
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFunctions
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getUserPreference
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
3.14
 getAllUserPreferences
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getCurrentUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Preference\Presentation\Twig;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Preference\Domain\Model\PreferenceScope;
12use App\Core\Preference\Domain\Repository\UserPreferenceRepositoryInterface;
13use App\Core\Preference\Domain\Service\ConfigurationCascadeResolverInterface;
14use Twig\Extension\AbstractExtension;
15use Twig\TwigFunction;
16
17/**
18 * Twig Extension for accessing resolved user preferences in templates.
19 *
20 * @package App\Core\Preference\Presentation\Twig
21 */
22final class UserPreferencesTwigExtension extends AbstractExtension
23{
24    /**
25     * UserPreferencesTwigExtension constructor.
26     *
27     * @param ConfigurationCascadeResolverInterface $resolver   Cascade resolution service.
28     * @param UserPreferenceRepositoryInterface     $repository User preference repository.
29     */
30    public function __construct(
31        private readonly ConfigurationCascadeResolverInterface $resolver,
32        private readonly UserPreferenceRepositoryInterface $repository,
33    ) {
34    }
35
36    /**
37     * @inheritDoc
38     */
39    public function getFunctions(): array
40    {
41        return [
42            new TwigFunction('user_pref', [$this, 'getUserPreference']),
43            new TwigFunction('user_prefs', [$this, 'getAllUserPreferences']),
44        ];
45    }
46
47    /**
48     * Resolves a user preference key with fallback.
49     *
50     * @param string      $key        Preference key name.
51     * @param string|null $moduleName Optional module name.
52     * @param mixed       $default    Fallback default value.
53     * @return mixed Resolved configuration value.
54     */
55    public function getUserPreference(string $key, ?string $moduleName = null, mixed $default = null): mixed
56    {
57        $userId = $this->getCurrentUserId();
58        if ($userId <= 0) {
59            return $default;
60        }
61
62        $deviceFingerprint = !empty($_COOKIE['ammonly_device_fingerprint'])
63            ? (string) $_COOKIE['ammonly_device_fingerprint']
64            : null;
65
66        $scope = PreferenceScope::forModule($userId, $moduleName ?? '', $deviceFingerprint);
67        return $this->resolver->resolve($scope, $key, $default);
68    }
69
70    /**
71     * Retrieves all preferences for the active user.
72     *
73     * @param string|null $moduleName Optional module filter.
74     * @return array<string, mixed> Key-value map.
75     */
76    public function getAllUserPreferences(?string $moduleName = null): array
77    {
78        $userId = $this->getCurrentUserId();
79        if ($userId <= 0) {
80            return [];
81        }
82
83        return $this->repository->findAllByUser($userId, $moduleName);
84    }
85
86    /**
87     * Resolves current user ID from session.
88     *
89     * @return int Active user ID or 0.
90     */
91    private function getCurrentUserId(): int
92    {
93        return isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : 0;
94    }
95}