Skip to content
This repository was archived by the owner on Apr 3, 2023. It is now read-only.

Commit d701dd8

Browse files
author
Teddy Roncin
committed
✨ (Routes /ues) Added routes for UEs
Added route GET /ues/search : it can search UEs by their code and the type of ECTS they give. Added route GET /ues/{code} that returns informations about the UE with the provided code. Added a new search filter for ApiPlatform which allows to change the name of the parameters in the URL. Fixed a small bug in the UpdateAtSubscriber class that could make the server produce an error
1 parent 8bf6d4c commit d701dd8

13 files changed

+276
-4
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace App\ApiPlatform;
4+
5+
use ApiPlatform\Api\IdentifiersExtractorInterface;
6+
use ApiPlatform\Api\IriConverterInterface;
7+
use ApiPlatform\Doctrine\Common\Filter\SearchFilterInterface;
8+
use ApiPlatform\Doctrine\Common\Filter\SearchFilterTrait;
9+
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
10+
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
11+
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
12+
use ApiPlatform\Metadata\Operation;
13+
use Doctrine\DBAL\Types\Types;
14+
use Doctrine\ORM\QueryBuilder;
15+
use Doctrine\Persistence\ManagerRegistry;
16+
use Psr\Log\LoggerInterface;
17+
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
18+
19+
final class SearchWithCustomParametersNamesFilter extends AbstractFilter implements SearchFilterInterface
20+
{
21+
use SearchFilterTrait;
22+
23+
protected array $aliasMap;
24+
private SearchFilter $searchFilter;
25+
26+
public function __construct(ManagerRegistry $managerRegistry, IriConverterInterface $iriConverter, PropertyAccessorInterface $propertyAccessor = null, LoggerInterface $logger = null, array $properties = null, IdentifiersExtractorInterface $identifiersExtractor = null, array $aliasMap = null)
27+
{
28+
parent::__construct($managerRegistry, $logger, $properties);
29+
$this->aliasMap = $aliasMap;
30+
$this->searchFilter = new SearchFilter($managerRegistry, $iriConverter, $propertyAccessor, $logger, $properties, $identifiersExtractor);
31+
}
32+
33+
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
34+
{
35+
$this->searchFilter->filterProperty($property, $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operation, $context);
36+
}
37+
38+
protected function denormalizePropertyName(int|string $property): string
39+
{
40+
return array_search($property, $this->aliasMap, strict: true) ?: $property;
41+
}
42+
43+
protected function normalizePropertyName(int|string $property): string
44+
{
45+
return $this->aliasMap[$property] ?? $property;
46+
}
47+
48+
protected function getType(string $doctrineType): string
49+
{
50+
return match ($doctrineType) {
51+
Types::ARRAY => 'array',
52+
Types::BIGINT, Types::INTEGER, Types::SMALLINT => 'int',
53+
Types::BOOLEAN => 'bool',
54+
Types::DATE_MUTABLE, Types::TIME_MUTABLE, Types::DATETIME_MUTABLE, Types::DATETIMETZ_MUTABLE, Types::DATE_IMMUTABLE, Types::TIME_IMMUTABLE, Types::DATETIME_IMMUTABLE, Types::DATETIMETZ_IMMUTABLE => \DateTimeInterface::class,
55+
Types::FLOAT => 'float',
56+
default => 'string',
57+
};
58+
}
59+
60+
protected function getIriConverter(): IriConverterInterface
61+
{
62+
return $this->iriConverter;
63+
}
64+
65+
protected function getPropertyAccessor(): PropertyAccessorInterface
66+
{
67+
return $this->propertyAccessor;
68+
}
69+
}

src/Entity/UE.php

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,45 @@
22

33
namespace App\Entity;
44

5+
use ApiPlatform\Metadata\ApiFilter;
6+
use ApiPlatform\Metadata\ApiProperty;
7+
use ApiPlatform\Metadata\ApiResource;
8+
use ApiPlatform\Metadata\Get;
9+
use ApiPlatform\Metadata\GetCollection;
10+
use App\ApiPlatform\SearchWithCustomParametersNamesFilter;
511
use App\Repository\UERepository;
612
use DateTime;
713
use Doctrine\Common\Collections\ArrayCollection;
814
use Doctrine\Common\Collections\Collection;
915
use Doctrine\ORM\Mapping as ORM;
1016
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
17+
use Symfony\Component\Serializer\Annotation\Groups;
1118
use Symfony\Component\Uid\Uuid;
1219
use Symfony\Component\Validator\Constraints as Assert;
1320

1421
/**
1522
* @ORM\Entity(repositoryClass=UERepository::class)
1623
* @ORM\Table(name="ues")
1724
*/
25+
#[
26+
ApiResource(
27+
shortName: 'ue',
28+
operations: [
29+
new GetCollection(
30+
uriTemplate: '/ues/search',
31+
normalizationContext: ['groups' => ['ue:read:some'], 'skip_null_values' => false],
32+
),
33+
new Get(
34+
normalizationContext: ['groups' => ['ue:read:one'], 'skip_null_values' => false],
35+
),
36+
],
37+
),
38+
ApiFilter(
39+
SearchWithCustomParametersNamesFilter::class,
40+
properties: ['code' => 'partial', 'credits.category.code' => 'exact'],
41+
arguments: ['aliasMap' => ['credits.category.code' => 'category']],
42+
),
43+
]
1844
class UE
1945
{
2046
/**
@@ -25,17 +51,27 @@ class UE
2551
*
2652
* @Assert\Uuid
2753
*/
54+
#[Groups([
55+
'ue:read:some',
56+
'ue:read:one',
57+
])]
58+
#[ApiProperty(identifier: false)]
2859
private $id;
2960

3061
/**
3162
* The code of the UE (e.g. "MATH01").
3263
*
33-
* @ORM\Column(type="string", length=10)
64+
* @ORM\Column(type="string", length=10, unique=true)
3465
*
3566
* @Assert\Type("string")
3667
* @Assert\Length(min=1, max=10)
3768
* @Assert\Regex("/^[a-zA-Z]{1,5}[0-9]{1,2}$/")
3869
*/
70+
#[Groups([
71+
'ue:read:some',
72+
'ue:read:one',
73+
])]
74+
#[ApiProperty(identifier: true)]
3975
private $code;
4076

4177
/**
@@ -46,6 +82,10 @@ class UE
4682
* @Assert\Type("string")
4783
* @Assert\Length(min=1, max=255)
4884
*/
85+
#[Groups([
86+
'ue:read:some',
87+
'ue:read:one',
88+
])]
4989
private $name;
5090

5191
/**
@@ -57,6 +97,9 @@ class UE
5797
* @Assert\LessThanOrEqual(100)
5898
* @Assert\GreaterThanOrEqual(0)
5999
*/
100+
#[Groups([
101+
'ue:read:one',
102+
])]
60103
private $validationRate;
61104

62105
/**
@@ -86,13 +129,20 @@ class UE
86129
* @ORM\ManyToOne(targetEntity=UTTFiliere::class, inversedBy="UEs")
87130
* @ORM\JoinColumn(name="filiere_code", referencedColumnName="code")
88131
*/
132+
#[Groups([
133+
'ue:read:one',
134+
])]
89135
private $filiere;
90136

91137
/**
92138
* The amount of UECredits of this UE. A UECredit object is a number of credit in a UECreditCategory.
93139
*
94140
* @ORM\OneToMany(targetEntity=UECredit::class, mappedBy="UE", orphanRemoval=true)
95141
*/
142+
#[Groups([
143+
'ue:read:one',
144+
'ue:read:some',
145+
])]
96146
private $credits;
97147

98148
/**
@@ -112,41 +162,72 @@ class UE
112162
* inverseJoinColumns={@ORM\JoinColumn(name="semester_code", referencedColumnName="code")}
113163
* )
114164
*/
165+
#[Groups([
166+
'ue:read:one',
167+
])]
115168
private $openSemester;
116169

170+
/**
171+
* Whether this UE is closed. A UE can still be opened, but not available at a given semester
172+
* (it could for example only be available in spring). When it is marked as closed, it should never be available again.
173+
*
174+
* @ORM\Column(type="boolean", options={"default": false})
175+
* @Assert\Type("boolean")
176+
*/
177+
#[Groups([
178+
'ue:read:some',
179+
'ue:read:one',
180+
])]
181+
private $isClosed = false;
182+
117183
/**
118184
* The relation to the entity that store the work time of this UE.
119185
*
120186
* @ORM\OneToOne(targetEntity=UEWorkTime::class, mappedBy="UE", cascade={"persist", "remove"})
121187
*/
188+
#[Groups([
189+
'ue:read:one',
190+
])]
122191
private $workTime;
123192

124193
/**
125194
* The relation to the entity that store the info of this UE given by UTT.
126195
*
127196
* @ORM\OneToOne(targetEntity=UEInfo::class, mappedBy="UE", cascade={"persist", "remove"})
128197
*/
198+
#[Groups([
199+
'ue:read:one',
200+
])]
129201
private $info;
130202

131203
/**
132204
* The relation to all UEAnnals related to this UE.
133205
*
134206
* @ORM\OneToMany(targetEntity=UEAnnal::class, mappedBy="UE", orphanRemoval=true)
135207
*/
208+
#[Groups([
209+
'ue:read:one',
210+
])]
136211
private $annals;
137212

138213
/**
139214
* The relation to all UEComments related to this UE.
140215
*
141216
* @ORM\OneToMany(targetEntity=UEComment::class, mappedBy="UE", orphanRemoval=true)
142217
*/
218+
#[Groups([
219+
'ue:read:one',
220+
])]
143221
private $comments;
144222

145223
/**
146224
* The relation to all UECourses of this UE.
147225
*
148226
* @ORM\OneToMany(targetEntity=UECourse::class, mappedBy="UE")
149227
*/
228+
#[Groups([
229+
'ue:read:one',
230+
])]
150231
private $courses;
151232

152233
public function __construct()
@@ -354,6 +435,17 @@ public function removeOpenSemester(Semester $openSemester): self
354435
return $this;
355436
}
356437

438+
public function getIsClosed(): bool
439+
{
440+
return $this->isClosed;
441+
}
442+
443+
public function close(): self
444+
{
445+
$this->isClosed = false;
446+
return $this;
447+
}
448+
357449
public function getWorkTime(): ?UEWorkTime
358450
{
359451
return $this->workTime;

src/Entity/UEAnnal.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\Common\Collections\Collection;
99
use Doctrine\ORM\Mapping as ORM;
1010
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
11+
use Symfony\Component\Serializer\Annotation\Groups;
1112
use Symfony\Component\Uid\Uuid;
1213
use Symfony\Component\Validator\Constraints as Assert;
1314

@@ -41,6 +42,9 @@ class UEAnnal
4142
* @ORM\ManyToOne(targetEntity=User::class)
4243
* @ORM\JoinColumn(nullable=false)
4344
*/
45+
#[Groups([
46+
'ue:read:one',
47+
])]
4448
private $sender;
4549

4650
/**
@@ -49,6 +53,9 @@ class UEAnnal
4953
* @ORM\ManyToOne(targetEntity=Semester::class)
5054
* @ORM\JoinColumn(name="semester_code", referencedColumnName="code")
5155
*/
56+
#[Groups([
57+
'ue:read:one',
58+
])]
5259
private $semester;
5360

5461
/**
@@ -57,6 +64,9 @@ class UEAnnal
5764
* @ORM\ManyToOne(targetEntity=UEAnnalType::class)
5865
* @ORM\JoinColumn(name="type_name", referencedColumnName="name")
5966
*/
67+
#[Groups([
68+
'ue:read:one',
69+
])]
6070
private $type;
6171

6272
/**
@@ -67,6 +77,9 @@ class UEAnnal
6777
* @Assert\Type("string")
6878
* @Assert\Length(min=1, max=255)
6979
*/
80+
#[Groups([
81+
'ue:read:one',
82+
])]
7083
private $filename;
7184

7285
/**

src/Entity/UEComment.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\Common\Collections\Collection;
1010
use Doctrine\ORM\Mapping as ORM;
1111
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
12+
use Symfony\Component\Serializer\Annotation\Groups;
1213
use Symfony\Component\Uid\Uuid;
1314
use Symfony\Component\Validator\Constraints as Assert;
1415

@@ -44,6 +45,9 @@ class UEComment
4445
* @ORM\ManyToOne(targetEntity=User::class)
4546
* @ORM\JoinColumn(nullable=false)
4647
*/
48+
#[Groups([
49+
'ue:read:one',
50+
])]
4751
private $author;
4852

4953
/**
@@ -53,6 +57,9 @@ class UEComment
5357
*
5458
* @Assert\Type("string")
5559
*/
60+
#[Groups([
61+
'ue:read:one',
62+
])]
5663
private $body;
5764

5865
/**
@@ -70,13 +77,19 @@ class UEComment
7077
* @ORM\ManyToOne(targetEntity=Semester::class)
7178
* @ORM\JoinColumn(name="semester_code", referencedColumnName="code")
7279
*/
80+
#[Groups([
81+
'ue:read:one',
82+
])]
7383
private $semester;
7484

7585
/**
7686
* The relation to all UECommentReply that are answering to this Comment.
7787
*
7888
* @ORM\OneToMany(targetEntity=UECommentReply::class, mappedBy="comment")
7989
*/
90+
#[Groups([
91+
'ue:read:one',
92+
])]
8093
private $answers;
8194

8295
/**

0 commit comments

Comments
 (0)