Skip to content

Commit 87d26e7

Browse files
Copilotjbtronics
andcommitted
Refactor Category, Footprint, and StorageLocation to use trait composition
- Remove inheritance from AbstractPartsContainingDBElement - Add explicit trait usage: DBElementTrait, NamedElementTrait, TimestampTrait, AttachmentsTrait, MasterAttachmentTrait, StructuralElementTrait, ParametersTrait - Implement all required interfaces directly - Initialize traits in constructor - Add custom __clone and jsonSerialize methods Co-authored-by: jbtronics <[email protected]>
1 parent e8fbea7 commit 87d26e7

File tree

3 files changed

+168
-20
lines changed

3 files changed

+168
-20
lines changed

src/Entity/Parts/Category.php

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,44 @@
3939
use ApiPlatform\Serializer\Filter\PropertyFilter;
4040
use App\ApiPlatform\Filter\LikeFilter;
4141
use App\Entity\Attachments\Attachment;
42+
use App\Entity\Base\AttachmentsTrait;
43+
use App\Entity\Base\DBElementTrait;
44+
use App\Entity\Base\MasterAttachmentTrait;
45+
use App\Entity\Base\NamedElementTrait;
46+
use App\Entity\Base\StructuralElementTrait;
47+
use App\Entity\Base\TimestampTrait;
48+
use App\Entity\Contracts\DBElementInterface;
49+
use App\Entity\Contracts\HasAttachmentsInterface;
50+
use App\Entity\Contracts\HasMasterAttachmentInterface;
51+
use App\Entity\Contracts\HasParametersInterface;
52+
use App\Entity\Contracts\NamedElementInterface;
53+
use App\Entity\Contracts\StructuralElementInterface;
54+
use App\Entity\Contracts\TimeStampableInterface;
4255
use App\Entity\EDA\EDACategoryInfo;
56+
use App\Entity\Parameters\ParametersTrait;
57+
use App\EntityListeners\TreeCacheInvalidationListener;
4358
use App\Repository\Parts\CategoryRepository;
59+
use App\Validator\Constraints\UniqueObjectCollection;
4460
use Doctrine\DBAL\Types\Types;
4561
use Doctrine\Common\Collections\ArrayCollection;
4662
use App\Entity\Attachments\CategoryAttachment;
47-
use App\Entity\Base\AbstractPartsContainingDBElement;
48-
use App\Entity\Base\AbstractStructuralDBElement;
4963
use App\Entity\Parameters\CategoryParameter;
5064
use Doctrine\Common\Collections\Collection;
5165
use Doctrine\ORM\Mapping as ORM;
66+
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
5267
use Symfony\Component\Serializer\Annotation\Groups;
5368
use Symfony\Component\Validator\Constraints as Assert;
5469

5570
/**
5671
* This entity describes a category, a part can belong to, which is used to group parts by their function.
57-
*
58-
* @extends AbstractPartsContainingDBElement<CategoryAttachment, CategoryParameter>
5972
*/
6073
#[ORM\Entity(repositoryClass: CategoryRepository::class)]
6174
#[ORM\Table(name: '`categories`')]
6275
#[ORM\Index(columns: ['name'], name: 'category_idx_name')]
6376
#[ORM\Index(columns: ['parent_id', 'name'], name: 'category_idx_parent_name')]
77+
#[ORM\HasLifecycleCallbacks]
78+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
79+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6480
#[ApiResource(
6581
operations: [
6682
new Get(security: 'is_granted("read", object)'),
@@ -89,8 +105,16 @@
89105
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
90106
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
91107
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
92-
class Category extends AbstractPartsContainingDBElement
108+
class Category implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, \Stringable, \JsonSerializable
93109
{
110+
use DBElementTrait;
111+
use NamedElementTrait;
112+
use TimestampTrait;
113+
use AttachmentsTrait;
114+
use MasterAttachmentTrait;
115+
use StructuralElementTrait;
116+
use ParametersTrait;
117+
94118
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
95119
#[ORM\OrderBy(['name' => Criteria::ASC])]
96120
protected Collection $children;
@@ -99,7 +123,7 @@ class Category extends AbstractPartsContainingDBElement
99123
#[ORM\JoinColumn(name: 'parent_id')]
100124
#[Groups(['category:read', 'category:write'])]
101125
#[ApiProperty(readableLink: false, writableLink: false)]
102-
protected ?AbstractStructuralDBElement $parent = null;
126+
protected ?self $parent = null;
103127

104128
#[Groups(['category:read', 'category:write'])]
105129
protected string $comment = '';
@@ -184,6 +208,7 @@ class Category extends AbstractPartsContainingDBElement
184208
/** @var Collection<int, CategoryParameter>
185209
*/
186210
#[Assert\Valid]
211+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
187212
#[Groups(['full', 'category:read', 'category:write'])]
188213
#[ORM\OneToMany(mappedBy: 'element', targetEntity: CategoryParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
189214
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
@@ -201,13 +226,37 @@ class Category extends AbstractPartsContainingDBElement
201226

202227
public function __construct()
203228
{
204-
parent::__construct();
229+
$this->initializeAttachments();
230+
$this->initializeStructuralElement();
205231
$this->children = new ArrayCollection();
206232
$this->attachments = new ArrayCollection();
207233
$this->parameters = new ArrayCollection();
208234
$this->eda_info = new EDACategoryInfo();
209235
}
210236

237+
public function __clone()
238+
{
239+
if ($this->id) {
240+
$this->cloneDBElement();
241+
$this->cloneAttachments();
242+
243+
// We create a new object, so give it a new creation date
244+
$this->addedDate = null;
245+
246+
//Deep clone parameters
247+
$parameters = $this->parameters;
248+
$this->parameters = new ArrayCollection();
249+
foreach ($parameters as $parameter) {
250+
$this->addParameter(clone $parameter);
251+
}
252+
}
253+
}
254+
255+
public function jsonSerialize(): array
256+
{
257+
return ['@id' => $this->getID()];
258+
}
259+
211260
public function getPartnameHint(): string
212261
{
213262
return $this->partname_hint;

src/Entity/Parts/Footprint.php

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,43 @@
3939
use ApiPlatform\Serializer\Filter\PropertyFilter;
4040
use App\ApiPlatform\Filter\LikeFilter;
4141
use App\Entity\Attachments\Attachment;
42+
use App\Entity\Base\AttachmentsTrait;
43+
use App\Entity\Base\DBElementTrait;
44+
use App\Entity\Base\MasterAttachmentTrait;
45+
use App\Entity\Base\NamedElementTrait;
46+
use App\Entity\Base\StructuralElementTrait;
47+
use App\Entity\Base\TimestampTrait;
48+
use App\Entity\Contracts\DBElementInterface;
49+
use App\Entity\Contracts\HasAttachmentsInterface;
50+
use App\Entity\Contracts\HasMasterAttachmentInterface;
51+
use App\Entity\Contracts\HasParametersInterface;
52+
use App\Entity\Contracts\NamedElementInterface;
53+
use App\Entity\Contracts\StructuralElementInterface;
54+
use App\Entity\Contracts\TimeStampableInterface;
4255
use App\Entity\EDA\EDAFootprintInfo;
56+
use App\Entity\Parameters\ParametersTrait;
57+
use App\EntityListeners\TreeCacheInvalidationListener;
4358
use App\Repository\Parts\FootprintRepository;
44-
use App\Entity\Base\AbstractStructuralDBElement;
59+
use App\Validator\Constraints\UniqueObjectCollection;
4560
use Doctrine\Common\Collections\ArrayCollection;
4661
use App\Entity\Attachments\FootprintAttachment;
47-
use App\Entity\Base\AbstractPartsContainingDBElement;
4862
use App\Entity\Parameters\FootprintParameter;
4963
use Doctrine\Common\Collections\Collection;
5064
use Doctrine\ORM\Mapping as ORM;
65+
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
5166
use Symfony\Component\Serializer\Annotation\Groups;
5267
use Symfony\Component\Validator\Constraints as Assert;
5368

5469
/**
5570
* This entity represents a footprint of a part (its physical dimensions and shape).
56-
*
57-
* @extends AbstractPartsContainingDBElement<FootprintAttachment, FootprintParameter>
5871
*/
5972
#[ORM\Entity(repositoryClass: FootprintRepository::class)]
6073
#[ORM\Table('`footprints`')]
6174
#[ORM\Index(columns: ['name'], name: 'footprint_idx_name')]
6275
#[ORM\Index(columns: ['parent_id', 'name'], name: 'footprint_idx_parent_name')]
76+
#[ORM\HasLifecycleCallbacks]
77+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
78+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6379
#[ApiResource(
6480
operations: [
6581
new Get(security: 'is_granted("read", object)'),
@@ -88,13 +104,21 @@
88104
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
89105
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
90106
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
91-
class Footprint extends AbstractPartsContainingDBElement
107+
class Footprint implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, \Stringable, \JsonSerializable
92108
{
109+
use DBElementTrait;
110+
use NamedElementTrait;
111+
use TimestampTrait;
112+
use AttachmentsTrait;
113+
use MasterAttachmentTrait;
114+
use StructuralElementTrait;
115+
use ParametersTrait;
116+
93117
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
94118
#[ORM\JoinColumn(name: 'parent_id')]
95119
#[Groups(['footprint:read', 'footprint:write'])]
96120
#[ApiProperty(readableLink: false, writableLink: false)]
97-
protected ?AbstractStructuralDBElement $parent = null;
121+
protected ?self $parent = null;
98122

99123
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
100124
#[ORM\OrderBy(['name' => Criteria::ASC])]
@@ -128,6 +152,7 @@ class Footprint extends AbstractPartsContainingDBElement
128152
/** @var Collection<int, FootprintParameter>
129153
*/
130154
#[Assert\Valid]
155+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
131156
#[ORM\OneToMany(mappedBy: 'element', targetEntity: FootprintParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
132157
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
133158
#[Groups(['footprint:read', 'footprint:write'])]
@@ -145,13 +170,37 @@ class Footprint extends AbstractPartsContainingDBElement
145170

146171
public function __construct()
147172
{
148-
parent::__construct();
173+
$this->initializeAttachments();
174+
$this->initializeStructuralElement();
149175
$this->children = new ArrayCollection();
150176
$this->attachments = new ArrayCollection();
151177
$this->parameters = new ArrayCollection();
152178
$this->eda_info = new EDAFootprintInfo();
153179
}
154180

181+
public function __clone()
182+
{
183+
if ($this->id) {
184+
$this->cloneDBElement();
185+
$this->cloneAttachments();
186+
187+
// We create a new object, so give it a new creation date
188+
$this->addedDate = null;
189+
190+
//Deep clone parameters
191+
$parameters = $this->parameters;
192+
$this->parameters = new ArrayCollection();
193+
foreach ($parameters as $parameter) {
194+
$this->addParameter(clone $parameter);
195+
}
196+
}
197+
}
198+
199+
public function jsonSerialize(): array
200+
{
201+
return ['@id' => $this->getID()];
202+
}
203+
155204
/****************************************
156205
* Getters
157206
****************************************/

src/Entity/Parts/StorageLocation.php

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,44 @@
3939
use ApiPlatform\Serializer\Filter\PropertyFilter;
4040
use App\ApiPlatform\Filter\LikeFilter;
4141
use App\Entity\Attachments\Attachment;
42+
use App\Entity\Base\AttachmentsTrait;
43+
use App\Entity\Base\DBElementTrait;
44+
use App\Entity\Base\MasterAttachmentTrait;
45+
use App\Entity\Base\NamedElementTrait;
46+
use App\Entity\Base\StructuralElementTrait;
47+
use App\Entity\Base\TimestampTrait;
48+
use App\Entity\Contracts\DBElementInterface;
49+
use App\Entity\Contracts\HasAttachmentsInterface;
50+
use App\Entity\Contracts\HasMasterAttachmentInterface;
51+
use App\Entity\Contracts\HasParametersInterface;
52+
use App\Entity\Contracts\NamedElementInterface;
53+
use App\Entity\Contracts\StructuralElementInterface;
54+
use App\Entity\Contracts\TimeStampableInterface;
55+
use App\Entity\Parameters\ParametersTrait;
56+
use App\EntityListeners\TreeCacheInvalidationListener;
4257
use App\Repository\Parts\StorelocationRepository;
58+
use App\Validator\Constraints\UniqueObjectCollection;
4359
use Doctrine\DBAL\Types\Types;
4460
use Doctrine\Common\Collections\ArrayCollection;
4561
use App\Entity\Attachments\StorageLocationAttachment;
46-
use App\Entity\Base\AbstractPartsContainingDBElement;
47-
use App\Entity\Base\AbstractStructuralDBElement;
4862
use App\Entity\Parameters\StorageLocationParameter;
4963
use App\Entity\UserSystem\User;
5064
use Doctrine\Common\Collections\Collection;
5165
use Doctrine\ORM\Mapping as ORM;
66+
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
5267
use Symfony\Component\Serializer\Annotation\Groups;
5368
use Symfony\Component\Validator\Constraints as Assert;
5469

5570
/**
5671
* This entity represents a storage location, where parts can be stored.
57-
* @extends AbstractPartsContainingDBElement<StorageLocationAttachment, StorageLocationParameter>
5872
*/
5973
#[ORM\Entity(repositoryClass: StorelocationRepository::class)]
6074
#[ORM\Table('`storelocations`')]
6175
#[ORM\Index(columns: ['name'], name: 'location_idx_name')]
6276
#[ORM\Index(columns: ['parent_id', 'name'], name: 'location_idx_parent_name')]
77+
#[ORM\HasLifecycleCallbacks]
78+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
79+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6380
#[ApiResource(
6481
operations: [
6582
new Get(security: 'is_granted("read", object)'),
@@ -88,8 +105,16 @@
88105
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
89106
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
90107
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
91-
class StorageLocation extends AbstractPartsContainingDBElement
108+
class StorageLocation implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, \Stringable, \JsonSerializable
92109
{
110+
use DBElementTrait;
111+
use NamedElementTrait;
112+
use TimestampTrait;
113+
use AttachmentsTrait;
114+
use MasterAttachmentTrait;
115+
use StructuralElementTrait;
116+
use ParametersTrait;
117+
93118
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
94119
#[ORM\OrderBy(['name' => Criteria::ASC])]
95120
protected Collection $children;
@@ -98,7 +123,7 @@ class StorageLocation extends AbstractPartsContainingDBElement
98123
#[ORM\JoinColumn(name: 'parent_id')]
99124
#[Groups(['location:read', 'location:write'])]
100125
#[ApiProperty(readableLink: false, writableLink: false)]
101-
protected ?AbstractStructuralDBElement $parent = null;
126+
protected ?self $parent = null;
102127

103128
#[Groups(['location:read', 'location:write'])]
104129
protected string $comment = '';
@@ -114,6 +139,7 @@ class StorageLocation extends AbstractPartsContainingDBElement
114139
/** @var Collection<int, StorageLocationParameter>
115140
*/
116141
#[Assert\Valid]
142+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
117143
#[ORM\OneToMany(mappedBy: 'element', targetEntity: StorageLocationParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
118144
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
119145
#[Groups(['location:read', 'location:write'])]
@@ -295,9 +321,33 @@ public function setIsFull(bool $new_is_full): self
295321
}
296322
public function __construct()
297323
{
298-
parent::__construct();
324+
$this->initializeAttachments();
325+
$this->initializeStructuralElement();
299326
$this->children = new ArrayCollection();
300327
$this->parameters = new ArrayCollection();
301328
$this->attachments = new ArrayCollection();
302329
}
330+
331+
public function __clone()
332+
{
333+
if ($this->id) {
334+
$this->cloneDBElement();
335+
$this->cloneAttachments();
336+
337+
// We create a new object, so give it a new creation date
338+
$this->addedDate = null;
339+
340+
//Deep clone parameters
341+
$parameters = $this->parameters;
342+
$this->parameters = new ArrayCollection();
343+
foreach ($parameters as $parameter) {
344+
$this->addParameter(clone $parameter);
345+
}
346+
}
347+
}
348+
349+
public function jsonSerialize(): array
350+
{
351+
return ['@id' => $this->getID()];
352+
}
303353
}

0 commit comments

Comments
 (0)