Skip to content

Commit ad77dea

Browse files
Copilotjbtronics
andcommitted
Complete refactoring: replace inheritance with trait composition
Refactored remaining entities to use trait composition instead of inheritance: - MeasurementUnit - uses all structural traits directly - PartCustomState - uses all structural traits directly - Manufacturer - uses all structural traits + CompanyTrait - Supplier - uses all structural traits + CompanyTrait - AttachmentType - uses all structural traits directly All entities now use explicit trait composition with: - DBElementTrait, NamedElementTrait, TimestampTrait - AttachmentsTrait, MasterAttachmentTrait - StructuralElementTrait, ParametersTrait - CompanyTrait (for Manufacturer and Supplier) All entities implement required interfaces directly instead of inheriting them. Co-authored-by: jbtronics <[email protected]>
1 parent 87d26e7 commit ad77dea

File tree

5 files changed

+287
-32
lines changed

5 files changed

+287
-32
lines changed

src/Entity/Attachments/AttachmentType.php

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@
5252
/**
5353
* Class AttachmentType.
5454
* @see \App\Tests\Entity\Attachments\AttachmentTypeTest
55-
* @extends AbstractStructuralDBElement<AttachmentTypeAttachment, AttachmentTypeParameter>
5655
*/
5756
#[ORM\Entity(repositoryClass: StructuralDBElementRepository::class)]
5857
#[ORM\Table(name: '`attachment_types`')]
5958
#[ORM\Index(columns: ['name'], name: 'attachment_types_idx_name')]
6059
#[ORM\Index(columns: ['parent_id', 'name'], name: 'attachment_types_idx_parent_name')]
60+
#[ORM\HasLifecycleCallbacks]
61+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
62+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6163
#[ApiResource(
6264
operations: [
6365
new Get(security: 'is_granted("read", object)'),
@@ -84,8 +86,16 @@
8486
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
8587
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
8688
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
87-
class AttachmentType extends AbstractStructuralDBElement
89+
class AttachmentType implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, \Stringable, \JsonSerializable
8890
{
91+
use DBElementTrait;
92+
use NamedElementTrait;
93+
use TimestampTrait;
94+
use AttachmentsTrait;
95+
use MasterAttachmentTrait;
96+
use StructuralElementTrait;
97+
use ParametersTrait;
98+
8999
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: AttachmentType::class, cascade: ['persist'])]
90100
#[ORM\OrderBy(['name' => Criteria::ASC])]
91101
protected Collection $children;
@@ -94,7 +104,10 @@ class AttachmentType extends AbstractStructuralDBElement
94104
#[ORM\JoinColumn(name: 'parent_id')]
95105
#[Groups(['attachment_type:read', 'attachment_type:write'])]
96106
#[ApiProperty(readableLink: true, writableLink: false)]
97-
protected ?AbstractStructuralDBElement $parent = null;
107+
protected ?self $parent = null;
108+
109+
#[Groups(['attachment_type:read', 'attachment_type:write'])]
110+
protected string $comment = '';
98111

99112
/**
100113
* @var string A comma separated list of file types, which are allowed for attachment files.
@@ -123,6 +136,7 @@ class AttachmentType extends AbstractStructuralDBElement
123136
/** @var Collection<int, AttachmentTypeParameter>
124137
*/
125138
#[Assert\Valid]
139+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
126140
#[ORM\OneToMany(mappedBy: 'element', targetEntity: AttachmentTypeParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
127141
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
128142
#[Groups(['attachment_type:read', 'attachment_type:write', 'import', 'full'])]
@@ -142,13 +156,37 @@ class AttachmentType extends AbstractStructuralDBElement
142156

143157
public function __construct()
144158
{
159+
$this->initializeAttachments();
160+
$this->initializeStructuralElement();
145161
$this->children = new ArrayCollection();
146162
$this->parameters = new ArrayCollection();
147-
parent::__construct();
148163
$this->attachments = new ArrayCollection();
149164
$this->attachments_with_type = new ArrayCollection();
150165
}
151166

167+
public function __clone()
168+
{
169+
if ($this->id) {
170+
$this->cloneDBElement();
171+
$this->cloneAttachments();
172+
173+
// We create a new object, so give it a new creation date
174+
$this->addedDate = null;
175+
176+
//Deep clone parameters
177+
$parameters = $this->parameters;
178+
$this->parameters = new ArrayCollection();
179+
foreach ($parameters as $parameter) {
180+
$this->addParameter(clone $parameter);
181+
}
182+
}
183+
}
184+
185+
public function jsonSerialize(): array
186+
{
187+
return ['@id' => $this->getID()];
188+
}
189+
152190
/**
153191
* Get all attachments ("Attachment" objects) with this type.
154192
*

src/Entity/Parts/Manufacturer.php

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +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\CompanyTrait;
44+
use App\Entity\Base\DBElementTrait;
45+
use App\Entity\Base\MasterAttachmentTrait;
46+
use App\Entity\Base\NamedElementTrait;
47+
use App\Entity\Base\StructuralElementTrait;
48+
use App\Entity\Base\TimestampTrait;
49+
use App\Entity\Contracts\CompanyInterface;
50+
use App\Entity\Contracts\DBElementInterface;
51+
use App\Entity\Contracts\HasAttachmentsInterface;
52+
use App\Entity\Contracts\HasMasterAttachmentInterface;
53+
use App\Entity\Contracts\HasParametersInterface;
54+
use App\Entity\Contracts\NamedElementInterface;
55+
use App\Entity\Contracts\StructuralElementInterface;
56+
use App\Entity\Contracts\TimeStampableInterface;
57+
use App\Entity\Parameters\ParametersTrait;
58+
use App\EntityListeners\TreeCacheInvalidationListener;
4259
use App\Repository\Parts\ManufacturerRepository;
43-
use App\Entity\Base\AbstractStructuralDBElement;
60+
use App\Validator\Constraints\UniqueObjectCollection;
4461
use Doctrine\Common\Collections\ArrayCollection;
4562
use App\Entity\Attachments\ManufacturerAttachment;
46-
use App\Entity\Base\AbstractCompany;
4763
use App\Entity\Parameters\ManufacturerParameter;
4864
use Doctrine\Common\Collections\Collection;
4965
use Doctrine\ORM\Mapping as ORM;
66+
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
5067
use Symfony\Component\Serializer\Annotation\Groups;
5168
use Symfony\Component\Validator\Constraints as Assert;
5269

5370
/**
5471
* This entity represents a manufacturer of a part (The company that produces the part).
55-
*
56-
* @extends AbstractCompany<ManufacturerAttachment, ManufacturerParameter>
5772
*/
5873
#[ORM\Entity(repositoryClass: ManufacturerRepository::class)]
5974
#[ORM\Table('`manufacturers`')]
6075
#[ORM\Index(columns: ['name'], name: 'manufacturer_name')]
6176
#[ORM\Index(columns: ['parent_id', 'name'], name: 'manufacturer_idx_parent_name')]
77+
#[ORM\HasLifecycleCallbacks]
78+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
79+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6280
#[ApiResource(
6381
operations: [
6482
new Get(security: 'is_granted("read", object)'),
@@ -87,13 +105,22 @@
87105
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
88106
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
89107
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
90-
class Manufacturer extends AbstractCompany
108+
class Manufacturer implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, CompanyInterface, \Stringable, \JsonSerializable
91109
{
110+
use DBElementTrait;
111+
use NamedElementTrait;
112+
use TimestampTrait;
113+
use AttachmentsTrait;
114+
use MasterAttachmentTrait;
115+
use StructuralElementTrait;
116+
use ParametersTrait;
117+
use CompanyTrait;
118+
92119
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
93120
#[ORM\JoinColumn(name: 'parent_id')]
94121
#[Groups(['manufacturer:read', 'manufacturer:write'])]
95122
#[ApiProperty(readableLink: false, writableLink: false)]
96-
protected ?AbstractStructuralDBElement $parent = null;
123+
protected ?self $parent = null;
97124

98125
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
99126
#[ORM\OrderBy(['name' => Criteria::ASC])]
@@ -118,16 +145,50 @@ class Manufacturer extends AbstractCompany
118145
/** @var Collection<int, ManufacturerParameter>
119146
*/
120147
#[Assert\Valid]
148+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
121149
#[ORM\OneToMany(mappedBy: 'element', targetEntity: ManufacturerParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
122150
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
123151
#[Groups(['manufacturer:read', 'manufacturer:write'])]
124152
#[ApiProperty(readableLink: false, writableLink: true)]
125153
protected Collection $parameters;
154+
155+
#[Groups(['manufacturer:read', 'manufacturer:write'])]
156+
protected string $comment = '';
157+
158+
#[Groups(['manufacturer:read'])]
159+
protected ?\DateTimeImmutable $addedDate = null;
160+
#[Groups(['manufacturer:read'])]
161+
protected ?\DateTimeImmutable $lastModified = null;
162+
126163
public function __construct()
127164
{
128-
parent::__construct();
165+
$this->initializeAttachments();
166+
$this->initializeStructuralElement();
129167
$this->children = new ArrayCollection();
130168
$this->attachments = new ArrayCollection();
131169
$this->parameters = new ArrayCollection();
132170
}
171+
172+
public function __clone()
173+
{
174+
if ($this->id) {
175+
$this->cloneDBElement();
176+
$this->cloneAttachments();
177+
178+
// We create a new object, so give it a new creation date
179+
$this->addedDate = null;
180+
181+
//Deep clone parameters
182+
$parameters = $this->parameters;
183+
$this->parameters = new ArrayCollection();
184+
foreach ($parameters as $parameter) {
185+
$this->addParameter(clone $parameter);
186+
}
187+
}
188+
}
189+
190+
public function jsonSerialize(): array
191+
{
192+
return ['@id' => $this->getID()];
193+
}
133194
}

src/Entity/Parts/MeasurementUnit.php

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,26 @@
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\MeasurementUnitRepository;
58+
use App\Validator\Constraints\UniqueObjectCollection;
4359
use Doctrine\DBAL\Types\Types;
44-
use App\Entity\Base\AbstractStructuralDBElement;
4560
use Doctrine\Common\Collections\ArrayCollection;
4661
use App\Entity\Attachments\MeasurementUnitAttachment;
47-
use App\Entity\Base\AbstractPartsContainingDBElement;
4862
use App\Entity\Parameters\MeasurementUnitParameter;
4963
use Doctrine\Common\Collections\Collection;
5064
use Doctrine\ORM\Mapping as ORM;
@@ -56,14 +70,15 @@
5670
/**
5771
* This unit represents the unit in which the amount of parts in stock are measured.
5872
* This could be something like N, grams, meters, etc...
59-
*
60-
* @extends AbstractPartsContainingDBElement<MeasurementUnitAttachment,MeasurementUnitParameter>
6173
*/
6274
#[UniqueEntity('unit')]
6375
#[ORM\Entity(repositoryClass: MeasurementUnitRepository::class)]
6476
#[ORM\Table(name: '`measurement_units`')]
6577
#[ORM\Index(columns: ['name'], name: 'unit_idx_name')]
6678
#[ORM\Index(columns: ['parent_id', 'name'], name: 'unit_idx_parent_name')]
79+
#[ORM\HasLifecycleCallbacks]
80+
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
81+
#[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
6782
#[ApiResource(
6883
operations: [
6984
new Get(security: 'is_granted("read", object)'),
@@ -92,8 +107,15 @@
92107
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "unit"])]
93108
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
94109
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
95-
class MeasurementUnit extends AbstractPartsContainingDBElement
110+
class MeasurementUnit implements DBElementInterface, NamedElementInterface, TimeStampableInterface, HasAttachmentsInterface, HasMasterAttachmentInterface, StructuralElementInterface, HasParametersInterface, \Stringable, \JsonSerializable
96111
{
112+
use DBElementTrait;
113+
use NamedElementTrait;
114+
use TimestampTrait;
115+
use AttachmentsTrait;
116+
use MasterAttachmentTrait;
117+
use StructuralElementTrait;
118+
use ParametersTrait;
97119
/**
98120
* @var string The unit symbol that should be used for the Unit. This could be something like "", g (for grams)
99121
* or m (for meters).
@@ -131,7 +153,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
131153
#[ORM\JoinColumn(name: 'parent_id')]
132154
#[Groups(['measurement_unit:read', 'measurement_unit:write'])]
133155
#[ApiProperty(readableLink: false, writableLink: false)]
134-
protected ?AbstractStructuralDBElement $parent = null;
156+
protected ?self $parent = null;
135157

136158
/**
137159
* @var Collection<int, MeasurementUnitAttachment>
@@ -150,6 +172,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
150172
/** @var Collection<int, MeasurementUnitParameter>
151173
*/
152174
#[Assert\Valid]
175+
#[UniqueObjectCollection(fields: ['name', 'group', 'element'])]
153176
#[ORM\OneToMany(mappedBy: 'element', targetEntity: MeasurementUnitParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
154177
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
155178
#[Groups(['measurement_unit:read', 'measurement_unit:write'])]
@@ -201,9 +224,33 @@ public function setUseSIPrefix(bool $usesSIPrefixes): self
201224
}
202225
public function __construct()
203226
{
204-
parent::__construct();
227+
$this->initializeAttachments();
228+
$this->initializeStructuralElement();
205229
$this->children = new ArrayCollection();
206230
$this->attachments = new ArrayCollection();
207231
$this->parameters = new ArrayCollection();
208232
}
233+
234+
public function __clone()
235+
{
236+
if ($this->id) {
237+
$this->cloneDBElement();
238+
$this->cloneAttachments();
239+
240+
// We create a new object, so give it a new creation date
241+
$this->addedDate = null;
242+
243+
//Deep clone parameters
244+
$parameters = $this->parameters;
245+
$this->parameters = new ArrayCollection();
246+
foreach ($parameters as $parameter) {
247+
$this->addParameter(clone $parameter);
248+
}
249+
}
250+
}
251+
252+
public function jsonSerialize(): array
253+
{
254+
return ['@id' => $this->getID()];
255+
}
209256
}

0 commit comments

Comments
 (0)