Completed
Push — master ( 442fec...fe6334 )
by
unknown
01:30 queued 37s
created

MessageStateProvider::provide()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 10
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\State;
8
9
use ApiPlatform\Doctrine\Orm\Paginator;
10
use ApiPlatform\Doctrine\Orm\State\CollectionProvider;
11
use ApiPlatform\Doctrine\Orm\State\ItemProvider;
12
use ApiPlatform\Metadata\Operation;
13
use ApiPlatform\State\ProviderInterface;
14
use Chamilo\CoreBundle\Entity\Message;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Doctrine\ORM\QueryBuilder;
17
use Symfony\Bundle\SecurityBundle\Security;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
20
/**
21
 * @template-implements ProviderInterface<Message>
22
 */
23
final class MessageStateProvider implements ProviderInterface
24
{
25
    public function __construct(
26
        private readonly CollectionProvider $collectionProvider,
27
        private readonly ItemProvider $itemProvider,
28
        private readonly EntityManagerInterface $entityManager,
29
        private readonly Security $security,
30
        private readonly RequestStack $requestStack
31
    ) {}
32
33
    /**
34
     * Provides data based on the operation type (collection or item).
35
     *
36
     * @return Paginator|Message|null
37
     */
38
    public function provide(Operation $operation, array $uriVariables = [], array $context = [])
39
    {
40
        $isCollection = $context['operation_type'] === 'collection';
41
42
        if ($isCollection) {
43
            return $this->handleCollection($operation, $context);
44
        }
45
46
        // Delegate to ItemProvider for individual operations
47
        return $this->itemProvider->provide($operation, $uriVariables, $context);
48
    }
49
50
    /**
51
     * Handles collection-level operations with filtering and pagination.
52
     */
53
    private function handleCollection(Operation $operation, array $context): Paginator
54
    {
55
        $user = $this->security->getUser();
56
        if (!$user) {
57
            throw new \LogicException('User not found.');
58
        }
59
60
        // Retrieve initial filters if they exist
61
        $filters = $context['filters'] ?? [];
62
        $this->applyFilters($filters);
63
64
        // Add base filters for the authenticated user
65
        $filters['sender'] = $user->getId();
66
        $filters['receivers.receiver'] = $user->getId();
67
68
        // Update context with merged filters
69
        $context['filters'] = $filters;
70
71
        // Check if advanced filtering is applied
72
        $isSearchApplied = isset($filters['search']) || count($filters) > 2;
73
74
        if (!$isSearchApplied) {
75
            // Delegate to CollectionProvider for standard query handling
76
            return $this->collectionProvider->provide($operation, [], $context);
77
        }
78
79
        // Build custom query for advanced filters
80
        return $this->applyCustomQuery($operation, $context, $filters);
81
    }
82
83
    /**
84
     * Builds and applies a custom query with filtering, sorting, and pagination.
85
     */
86
    private function applyCustomQuery(Operation $operation, array $context, array $filters): Paginator
87
    {
88
        // Main query
89
        $queryBuilder = $this->createQueryWithFilters($filters);
90
91
        // Apply pagination and sorting
92
        $order = $context['filters']['order']['sendDate'] ?? 'ASC';
93
        $queryBuilder->orderBy('m.sendDate', $order);
94
95
        $itemsPerPage = (int) ($context['filters']['itemsPerPage'] ?? 10);
96
        $currentPage = (int) ($context['filters']['page'] ?? 1);
97
98
        $queryBuilder->setFirstResult(($currentPage - 1) * $itemsPerPage)
99
            ->setMaxResults($itemsPerPage);
100
101
        // Count query for total items
102
        $countQueryBuilder = $this->createQueryWithFilters($filters, true);
103
        $totalItems = (int) $countQueryBuilder->getQuery()->getSingleScalarResult();
104
105
        // Doctrine Paginator
106
        $doctrinePaginator = new \Doctrine\ORM\Tools\Pagination\Paginator($queryBuilder, true);
107
108
        // Adjust OutputWalkers as needed
109
        $needsOutputWalkers = count($queryBuilder->getDQLPart('join')) > 0;
110
        $doctrinePaginator->setUseOutputWalkers($needsOutputWalkers);
111
112
        $paginator = new Paginator($doctrinePaginator);
113
114
        return $paginator;
115
    }
116
117
    /**
118
     * Creates a query with filters applied dynamically.
119
     */
120
    private function createQueryWithFilters(array $filters, bool $isCountQuery = false): QueryBuilder
121
    {
122
        $queryBuilder = $this->entityManager->createQueryBuilder();
123
124
        // Adjust SELECT statement for count or distinct query
125
        if ($isCountQuery) {
126
            $queryBuilder->select('COUNT(DISTINCT m.id)');
127
        } else {
128
            $queryBuilder->select('DISTINCT m');
129
        }
130
131
        $queryBuilder->from(Message::class, 'm')
132
            ->leftJoin('m.receivers', 'r', 'WITH', 'r.deletedAt IS NULL OR r.deletedAt > CURRENT_TIMESTAMP()')
133
            ->where('m.sender = :user OR r.receiver = :user')
134
            ->setParameter('user', $filters['sender']);
135
136
        // Dynamically apply filters
137
        foreach ($filters as $key => $value) {
138
            switch ($key) {
139
                case 'msgType':
140
                    $queryBuilder->andWhere('m.msgType = :msgType')->setParameter('msgType', $value);
141
                    break;
142
                case 'status':
143
                    $queryBuilder->andWhere('m.status = :status')->setParameter('status', $value);
144
                    break;
145
                case 'receivers.receiver':
146
                    $queryBuilder->andWhere('r.receiver = :receiver')->setParameter('receiver', $value);
147
                    break;
148
                case 'receivers.receiverType':
149
                    $queryBuilder->andWhere('r.receiverType = :receiverType')->setParameter('receiverType', $value);
150
                    break;
151
                case 'receivers.read':
152
                    $queryBuilder->andWhere('r.read = :read')->setParameter('read', !($value === 'false'));
153
                    break;
154
                case 'search':
155
                    $queryBuilder->andWhere('m.title LIKE :search OR m.content LIKE :search')
156
                        ->setParameter('search', '%' . $value . '%');
157
                    break;
158
            }
159
        }
160
161
        return $queryBuilder;
162
    }
163
164
    /**
165
     * Merges request filters into the provided filter array.
166
     */
167
    private function applyFilters(array &$filters): void
168
    {
169
        $request = $this->requestStack->getMainRequest();
170
        if ($request) {
171
            $requestFilters = $request->query->all();
172
            $filters = array_merge($filters, $requestFilters);
173
        }
174
    }
175
}
176