Passed
Push — master ( 2d425f...eb03d1 )
by Jan
04:57 queued 10s
created

EntityURLGenerator::createURL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 14
nc 1
nop 1
dl 0
loc 18
rs 9.7998
c 1
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\Services;
44
45
use App\Entity\Attachments\Attachment;
46
use App\Entity\Attachments\AttachmentType;
47
use App\Entity\Attachments\PartAttachment;
48
use App\Entity\Base\AbstractDBElement;
49
use App\Entity\Devices\Device;
50
use App\Entity\Parts\Category;
51
use App\Entity\Parts\Footprint;
52
use App\Entity\Parts\Manufacturer;
53
use App\Entity\Parts\MeasurementUnit;
54
use App\Entity\Parts\Part;
55
use App\Entity\Parts\PartLot;
56
use App\Entity\Parts\Storelocation;
57
use App\Entity\Parts\Supplier;
58
use App\Entity\PriceInformations\Currency;
59
use App\Entity\PriceInformations\Orderdetail;
60
use App\Entity\PriceInformations\Pricedetail;
61
use App\Entity\UserSystem\Group;
62
use App\Entity\UserSystem\User;
63
use App\Exceptions\EntityNotSupportedException;
64
use App\Services\Attachments\AttachmentURLGenerator;
65
use function array_key_exists;
66
use function get_class;
67
use InvalidArgumentException;
68
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
69
70
/**
71
 * This service can be used to generate links to controllers for different aspects of an entity
72
 * (like info, edit, delete, etc.)
73
 * Useful for Twig, where you can generate a link to an entity using a filter.
74
 */
75
class EntityURLGenerator
76
{
77
    protected $attachmentURLGenerator;
78
    /**
79
     * @var UrlGeneratorInterface
80
     */
81
    protected $urlGenerator;
82
83
    public function __construct(UrlGeneratorInterface $urlGenerator, AttachmentURLGenerator $attachmentURLGenerator)
84
    {
85
        $this->urlGenerator = $urlGenerator;
86
        $this->attachmentURLGenerator = $attachmentURLGenerator;
87
    }
88
89
    /**
90
     * Generates an URL to the page using the given page type and element.
91
     * For the given types, the [type]URL() functions are called (e.g. infoURL()).
92
     * Not all entity class and $type combinations are supported.
93
     *
94
     * @param mixed  $entity The element for which the page should be generated
95
     * @param string $type   The page type. Currently supported: 'info', 'edit', 'create', 'clone', 'list'/'list_parts'
96
     *
97
     * @return string the link to the desired page
98
     *
99
     * @throws EntityNotSupportedException thrown if the entity is not supported for the given type
100
     * @throws InvalidArgumentException    thrown if the givent type is not existing
101
     */
102
    public function getURL($entity, string $type)
103
    {
104
        switch ($type) {
105
            case 'info':
106
                return $this->infoURL($entity);
107
            case 'edit':
108
                return $this->editURL($entity);
109
            case 'create':
110
                return $this->createURL($entity);
111
            case 'clone':
112
                return $this->cloneURL($entity);
113
            case 'list':
114
            case 'list_parts':
115
                return $this->listPartsURL($entity);
116
            case 'delete':
117
                return $this->deleteURL($entity);
118
            case 'file_download':
119
                return $this->downloadURL($entity);
120
            case 'file_view':
121
                return $this->viewURL($entity);
122
        }
123
124
        throw new InvalidArgumentException('Method is not supported!');
125
    }
126
127
    /**
128
     * Gets the URL to view the given element at a given timestamp
129
     * @param AbstractDBElement $entity
130
     * @param  \DateTime  $dateTime
131
     * @return string
132
     */
133
    public function timeTravelURL(AbstractDBElement $entity, \DateTime $dateTime): string
134
    {
135
        $map = [
136
            Part::class => 'part_info',
137
138
            //As long we does not have own things for it use edit page
139
            AttachmentType::class => 'attachment_type_edit',
140
            Category::class => 'category_edit',
141
            Device::class => 'device_edit',
142
            Supplier::class => 'supplier_edit',
143
            Manufacturer::class => 'manufacturer_edit',
144
            Storelocation::class => 'store_location_edit',
145
            Footprint::class => 'footprint_edit',
146
            User::class => 'user_edit',
147
            Currency::class => 'currency_edit',
148
            MeasurementUnit::class => 'measurement_unit_edit',
149
            Group::class => 'group_edit',
150
        ];
151
152
        try {
153
            return $this->urlGenerator->generate(
154
                $this->mapToController($map, $entity),
155
                [
156
                    'id' => $entity->getID(),
157
                    'timestamp' => $dateTime->getTimestamp()
158
                ]
159
            );
160
        } catch (EntityNotSupportedException $exception) {
161
            if ($entity instanceof PartLot) {
162
                return $this->urlGenerator->generate('part_info', [
163
                    'id' => $entity->getPart()->getID(),
164
                    'timestamp' => $dateTime->getTimestamp()
165
                ]);
166
            }
167
            if ($entity instanceof PartAttachment) {
168
                return $this->urlGenerator->generate('part_info', [
169
                    'id' => $entity->getElement()->getID(),
170
                    'timestamp' => $dateTime->getTimestamp()
171
                ]);
172
            }
173
            if ($entity instanceof Orderdetail) {
174
                return $this->urlGenerator->generate('part_info', [
175
                    'id' => $entity->getPart()->getID(),
176
                    'timestamp' => $dateTime->getTimestamp()
177
                ]);
178
            }
179
            if ($entity instanceof Pricedetail) {
180
                return $this->urlGenerator->generate('part_info', [
181
                    'id' => $entity->getOrderdetail()->getPart()->getID(),
182
                    'timestamp' => $dateTime->getTimestamp()
183
                ]);
184
            }
185
        }
186
187
        //Otherwise throw an error
188
        throw new EntityNotSupportedException('The given entity is not supported yet!');
189
    }
190
191
    public function viewURL($entity): string
192
    {
193
        if ($entity instanceof Attachment) {
194
            if ($entity->isExternal()) { //For external attachments, return the link to external path
195
                return $entity->getURL();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity->getURL() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
196
            }
197
            //return $this->urlGenerator->generate('attachment_view', ['id' => $entity->getID()]);
198
            return $this->attachmentURLGenerator->getViewURL($entity);
199
        }
200
201
        //Otherwise throw an error
202
        throw new EntityNotSupportedException('The given entity is not supported yet!');
203
    }
204
205
    public function downloadURL($entity): string
206
    {
207
        if ($entity instanceof Attachment) {
208
            if ($entity->isExternal()) { //For external attachments, return the link to external path
209
                return $entity->getURL();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity->getURL() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
210
            }
211
212
            return $this->attachmentURLGenerator->getDownloadURL($entity);
213
        }
214
215
        //Otherwise throw an error
216
        throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', get_class($entity)));
217
    }
218
219
    /**
220
     * Generates an URL to a page, where info about this entity can be viewed.
221
     *
222
     * @param AbstractDBElement $entity The entity for which the info should be generated
223
     *
224
     * @return string The URL to the info page
225
     *
226
     * @throws EntityNotSupportedException If the method is not supported for the given Entity
227
     */
228
    public function infoURL(AbstractDBElement $entity): string
229
    {
230
        $map = [
231
            Part::class => 'part_info',
232
233
            //As long we does not have own things for it use edit page
234
            AttachmentType::class => 'attachment_type_edit',
235
            Category::class => 'category_edit',
236
            Device::class => 'device_edit',
237
            Supplier::class => 'supplier_edit',
238
            Manufacturer::class => 'manufacturer_edit',
239
            Storelocation::class => 'store_location_edit',
240
            Footprint::class => 'footprint_edit',
241
            User::class => 'user_edit',
242
            Currency::class => 'currency_edit',
243
            MeasurementUnit::class => 'measurement_unit_edit',
244
            Group::class => 'group_edit',
245
        ];
246
247
        return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
248
    }
249
250
    /**
251
     * Generates an URL to a page, where this entity can be edited.
252
     *
253
     * @param mixed $entity The entity for which the edit link should be generated
254
     *
255
     * @return string the URL to the edit page
256
     *
257
     * @throws EntityNotSupportedException If the method is not supported for the given Entity
258
     */
259
    public function editURL($entity): string
260
    {
261
        $map = [
262
            Part::class => 'part_edit',
263
            AttachmentType::class => 'attachment_type_edit',
264
            Category::class => 'category_edit',
265
            Device::class => 'device_edit',
266
            Supplier::class => 'supplier_edit',
267
            Manufacturer::class => 'manufacturer_edit',
268
            Storelocation::class => 'store_location_edit',
269
            Footprint::class => 'footprint_edit',
270
            User::class => 'user_edit',
271
            Currency::class => 'currency_edit',
272
            MeasurementUnit::class => 'measurement_unit_edit',
273
            Group::class => 'group_edit',
274
        ];
275
276
        return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
277
    }
278
279
    /**
280
     * Generates an URL to a page, where a entity of this type can be created.
281
     *
282
     * @param mixed $entity The entity for which the link should be generated
283
     *
284
     * @return string the URL to the page
285
     *
286
     * @throws EntityNotSupportedException If the method is not supported for the given Entity
287
     */
288
    public function createURL($entity): string
289
    {
290
        $map = [
291
            Part::class => 'part_new',
292
            AttachmentType::class => 'attachment_type_new',
293
            Category::class => 'category_new',
294
            Device::class => 'device_new',
295
            Supplier::class => 'supplier_new',
296
            Manufacturer::class => 'manufacturer_new',
297
            Storelocation::class => 'store_location_new',
298
            Footprint::class => 'footprint_new',
299
            User::class => 'user_new',
300
            Currency::class => 'currency_new',
301
            MeasurementUnit::class => 'measurement_unit_new',
302
            Group::class => 'group_new',
303
        ];
304
305
        return $this->urlGenerator->generate($this->mapToController($map, $entity));
306
    }
307
308
    /**
309
     * Generates an URL to a page, where a new entity can be created, that has the same informations as the
310
     * given entity (element cloning).
311
     *
312
     * @param AbstractDBElement $entity The entity for which the link should be generated
313
     *
314
     * @return string the URL to the page
315
     *
316
     * @throws EntityNotSupportedException If the method is not supported for the given Entity
317
     */
318
    public function cloneURL(AbstractDBElement $entity): string
319
    {
320
        $map = [
321
            Part::class => 'part_clone',
322
        ];
323
324
        return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
325
    }
326
327
    /**
328
     * Generates an URL to a page, where all parts are listed, which are contained in the given element.
329
     *
330
     * @param AbstractDBElement $entity The entity for which the link should be generated
331
     *
332
     * @return string the URL to the page
333
     *
334
     * @throws EntityNotSupportedException If the method is not supported for the given Entity
335
     */
336
    public function listPartsURL(AbstractDBElement $entity): string
337
    {
338
        $map = [
339
            Category::class => 'part_list_category',
340
            Footprint::class => 'part_list_footprint',
341
            Manufacturer::class => 'part_list_manufacturer',
342
            Supplier::class => 'part_list_supplier',
343
            Storelocation::class => 'part_list_store_location',
344
        ];
345
346
        return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
347
    }
348
349
    public function deleteURL(AbstractDBElement $entity): string
350
    {
351
        $map = [
352
            Part::class => 'part_delete',
353
            AttachmentType::class => 'attachment_type_delete',
354
            Category::class => 'category_delete',
355
            Device::class => 'device_delete',
356
            Supplier::class => 'supplier_delete',
357
            Manufacturer::class => 'manufacturer_delete',
358
            Storelocation::class => 'store_location_delete',
359
            Footprint::class => 'footprint_delete',
360
            User::class => 'user_delete',
361
            Currency::class => 'currency_delete',
362
            MeasurementUnit::class => 'measurement_unit_delete',
363
            Group::class => 'group_delete',
364
        ];
365
366
        return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
367
    }
368
369
    /**
370
     * Finds the controller name for the class of the entity using the given map.
371
     * Throws an exception if the entity class is not known to the map.
372
     *
373
     * @param array $map    The map that should be used for determing the controller
374
     * @param mixed $entity The entity for which the controller name should be determined
375
     *
376
     * @return string The name of the controller fitting the entity class
377
     *
378
     * @throws EntityNotSupportedException
379
     */
380
    protected function mapToController(array $map, $entity): string
381
    {
382
        $class = get_class($entity);
383
384
        //Check if we have an direct mapping for the given class
385
        if (! array_key_exists($class, $map)) {
386
            //Check if we need to check inheritance by looping through our map
387
            foreach ($map as $key => $value) {
388
                if (is_a($entity, $key)) {
389
                    return $map[$key];
390
                }
391
            }
392
393
            throw new EntityNotSupportedException(sprintf('The given entity is not supported yet! Passed class type: %s', get_class($entity)));
394
        }
395
396
        return $map[$class];
397
    }
398
}
399