Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
137 / 137
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
ModuleController
100.00% covered (success)
100.00%
137 / 137
100.00% covered (success)
100.00%
7 / 7
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 create
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
1 / 1
6
 update
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
7
 delete
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 redirectWithFlash
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace App\Web\Controller;
6
7use App\Domain\Module\Entity\ModuleRecord;
8use App\Domain\Module\Repository\ModuleRecordRepository;
9use App\Domain\Shared\Query\GridQuerySpecification;
10use App\Infrastructure\Shared\Query\GridQueryBuilder;
11use App\Shared\Host\TablePrefixResolver;
12use App\Shared\UI\ListView\ListViewData;
13use Psr\Http\Message\ResponseFactoryInterface;
14use Psr\Http\Message\ResponseInterface;
15use Psr\Http\Message\ServerRequestInterface;
16use Yiisoft\Yii\View\Renderer\WebViewRenderer;
17
18 class ModuleController
19{
20    private const ROUTE_MODULES = '/modules';
21
22    public function __construct(
23        private WebViewRenderer $viewRenderer,
24        private GridQueryBuilder $queryBuilder,
25        private ModuleRecordRepository $repository,
26        private TablePrefixResolver $prefixResolver,
27        private ResponseFactoryInterface $responseFactory
28    ) {
29        $this->viewRenderer = $viewRenderer->withControllerName('module');
30    }
31
32    public function index(ServerRequestInterface $request): ResponseInterface
33    {
34        $allowedColumns = [
35            'code' => 'code',
36            'name' => 'name',
37            'icon' => 'icon',
38            'scope' => 'scope',
39            'table_name' => 'table_name',
40            'status' => 'status',
41            'created_at' => 'created_at',
42        ];
43
44        /** @var array<string, mixed> $queryParams */
45        $queryParams = $request->getQueryParams();
46        $spec = new GridQuerySpecification($queryParams, $allowedColumns, 'code', 'ASC');
47
48        $targetTable = $this->prefixResolver->resolve('modules');
49        $queryResult = $this->queryBuilder->fetchData($targetTable, $spec, 'code', 'ASC');
50
51        $listView = new ListViewData(
52            items: $queryResult['items'],
53            columns: [
54                'code' => 'Kod Modułu',
55                'name' => 'Nazwa Modułu',
56                'icon' => 'Ikona',
57                'scope' => 'Zakres (Scope)',
58                'table_name' => 'Tabela Bazy',
59                'status' => 'Status',
60            ],
61            currentPage: $spec->getPage(),
62            pageSize: $spec->getPageSize(),
63            totalItems: $queryResult['totalCount'],
64            sortField: $spec->getSortField(),
65            sortDirection: $spec->getSortDirection(),
66            filterParams: $spec->getFilters(),
67            createUrl: '/modules/create',
68            viewRoute: '/modules/view?id={id}',
69            updateRoute: '/modules/update?id={id}',
70            deleteRoute: '/modules/delete?id={id}',
71            tableExists: (bool) ($queryResult['tableExists'] ?? true),
72            tableName: (string) ($queryResult['tableName'] ?? 'modules')
73        );
74
75        return $this->viewRenderer->render('//module/index.twig', [
76            'list' => $listView,
77            'activeTable' => $targetTable,
78        ]);
79    }
80
81    public function view(ServerRequestInterface $request): ResponseInterface
82    {
83        $id = (string) ($request->getQueryParams()['id'] ?? '');
84        $targetTable = $this->prefixResolver->resolve('modules');
85        $module = $this->repository->findById($id, $targetTable);
86
87        if ($module === null) {
88            return $this->redirectWithFlash(self::ROUTE_MODULES);
89        }
90
91        return $this->viewRenderer->render('//module/view.twig', [
92            'module' => $module,
93            'activeTable' => $targetTable,
94        ]);
95    }
96
97    public function create(ServerRequestInterface $request): ResponseInterface
98    {
99        $targetTable = $this->prefixResolver->resolve('modules');
100        $errors = [];
101        $data = [];
102
103        if ($request->getMethod() === 'POST') {
104            /** @var array<string, string> $data */
105            $data = (array) $request->getParsedBody();
106            $code = trim($data['code'] ?? '');
107            $name = trim($data['name'] ?? '');
108            $icon = trim($data['icon'] ?? 'bi-box-seam');
109            $scope = trim($data['scope'] ?? 'both');
110            $tableName = trim($data['table_name'] ?? '');
111            $status = trim($data['status'] ?? 'active');
112
113            if ($code === '') {
114                $errors['code'] = 'Kod modułu jest wymagany.';
115            }
116            if ($name === '') {
117                $errors['name'] = 'Nazwa modułu jest wymagana.';
118            }
119
120            if (empty($errors)) {
121                $record = new ModuleRecord(
122                    id: 'mod-' . bin2hex(random_bytes(4)),
123                    code: $code,
124                    name: $name,
125                    scope: $scope,
126                    tableName: $tableName,
127                    status: $status,
128                    createdAt: time(),
129                    updatedAt: time(),
130                    icon: $icon !== '' ? $icon : 'bi-box-seam'
131                );
132                $this->repository->save($record, $targetTable);
133                return $this->redirectWithFlash(self::ROUTE_MODULES);
134            }
135        }
136
137        return $this->viewRenderer->render('//module/create.twig', [
138            'errors' => $errors,
139            'data' => $data,
140            'activeTable' => $targetTable,
141        ]);
142    }
143
144    public function update(ServerRequestInterface $request): ResponseInterface
145    {
146        $id = (string) ($request->getQueryParams()['id'] ?? '');
147        $targetTable = $this->prefixResolver->resolve('modules');
148        $module = $this->repository->findById($id, $targetTable);
149
150        if ($module === null) {
151            return $this->redirectWithFlash(self::ROUTE_MODULES);
152        }
153
154        $errors = [];
155        $data = [
156            'code' => $module->getCode(),
157            'name' => $module->getName(),
158            'icon' => $module->getIcon(),
159            'scope' => $module->getScope(),
160            'table_name' => $module->getTableName(),
161            'status' => $module->getStatus(),
162        ];
163
164        if ($request->getMethod() === 'POST') {
165            /** @var array<string, string> $data */
166            $data = (array) $request->getParsedBody();
167            $code = trim($data['code'] ?? '');
168            $name = trim($data['name'] ?? '');
169            $icon = trim($data['icon'] ?? 'bi-box-seam');
170            $scope = trim($data['scope'] ?? 'both');
171            $tableName = trim($data['table_name'] ?? '');
172            $status = trim($data['status'] ?? 'active');
173
174            if ($code === '') {
175                $errors['code'] = 'Kod modułu jest wymagany.';
176            }
177            if ($name === '') {
178                $errors['name'] = 'Nazwa modułu jest wymagana.';
179            }
180
181            if (empty($errors)) {
182                $updatedRecord = new ModuleRecord(
183                    id: $module->getId(),
184                    code: $code,
185                    name: $name,
186                    scope: $scope,
187                    tableName: $tableName,
188                    status: $status,
189                    createdAt: $module->getCreatedAt(),
190                    updatedAt: time(),
191                    icon: $icon !== '' ? $icon : 'bi-box-seam'
192                );
193                $this->repository->save($updatedRecord, $targetTable);
194                return $this->redirectWithFlash(self::ROUTE_MODULES);
195            }
196        }
197
198        return $this->viewRenderer->render('//module/update.twig', [
199            'module' => $module,
200            'errors' => $errors,
201            'data' => $data,
202            'activeTable' => $targetTable,
203        ]);
204    }
205
206    public function delete(ServerRequestInterface $request): ResponseInterface
207    {
208        $id = (string) ($request->getQueryParams()['id'] ?? '');
209        $targetTable = $this->prefixResolver->resolve('modules');
210
211        if ($id !== '') {
212            $this->repository->delete($id, $targetTable);
213        }
214
215        return $this->redirectWithFlash(self::ROUTE_MODULES);
216    }
217
218    private function redirectWithFlash(string $url): ResponseInterface
219    {
220        $response = $this->responseFactory->createResponse(302);
221        return $response->withHeader('Location', $url);
222    }
223}