Skip to content

Commit df7afa3

Browse files
committed
Ask the user if they want the inverse side of a relation
1 parent eeaf223 commit df7afa3

File tree

4 files changed

+126
-19
lines changed

4 files changed

+126
-19
lines changed

src/Maker/MakeEntity.php

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,6 @@ public function afterGenerate(ConsoleStyle $io, array $params)
252252
// save the inverse side if it's being mapped
253253
if ($newField->getMapInverseRelation()) {
254254
$fileManagerOperations[$otherManipulatorFilename] = $otherManipulator;
255-
} else {
256-
// print message about it not being saved
257-
$fileManagerOperations[] = sprintf(
258-
'The inverse side of the relation was not mapped in "%s" because it lives in the vendor/ directory.',
259-
$newField->getInverseClass()
260-
);
261255
}
262256
$currentFields[] = $newFieldName;
263257
} else {
@@ -579,8 +573,30 @@ function ($name) use ($targetClass) {
579573
return $io->confirm(sprintf('Do you want to automatically delete orphaned <comment>%s</comment> objects (orphanRemoval)?', $owningClass), false);
580574
};
581575

582-
$setMapInverseSide = function (EntityRelation $relation) {
583-
$relation->setMapInverseRelation(!$this->isClassInVendor($relation->getInverseClass()));
576+
$askInverseSide = function (EntityRelation $relation) use ($io) {
577+
if ($this->isClassInVendor($relation->getInverseClass())) {
578+
$relation->setMapInverseRelation(false);
579+
}
580+
581+
// recommend an inverse side, except for OneToOne, where it's inefficient
582+
$recommendMappingInverse = EntityRelation::ONE_TO_ONE === $relation->getType() ? false : true;
583+
584+
$getterMethodName = 'get'.Str::asCamelCase(Str::getShortClassName($relation->getOwningClass()));
585+
if (EntityRelation::ONE_TO_ONE !== $relation->getType()) {
586+
// pluralize!
587+
$getterMethodName = Str::singularCamelCaseToPluralCamelCase($getterMethodName);
588+
}
589+
$mapInverse = $io->confirm(
590+
sprintf(
591+
'Do you want to add a new property to <comment>%s</comment> so that you can access/update <comment>%s</comment> objects from it - e.g. <comment>$%s->%s()</comment>?',
592+
Str::getShortClassName($relation->getInverseClass()),
593+
Str::getShortClassName($relation->getOwningClass()),
594+
Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass())),
595+
$getterMethodName
596+
),
597+
$recommendMappingInverse
598+
);
599+
$relation->setMapInverseRelation($mapInverse);
584600
};
585601

586602
switch ($type) {
@@ -597,7 +613,7 @@ function ($name) use ($targetClass) {
597613
$relation->getOwningClass()
598614
));
599615

600-
$setMapInverseSide($relation);
616+
$askInverseSide($relation);
601617
if ($relation->getMapInverseRelation()) {
602618
$io->comment(sprintf(
603619
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.',
@@ -608,13 +624,14 @@ function ($name) use ($targetClass) {
608624
$relation->getInverseClass(),
609625
Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass()))
610626
));
611-
}
612627

613-
if (!$relation->isNullable()) {
614-
$relation->setOrphanRemoval($askOrphanRemoval(
615-
$relation->getOwningClass(),
616-
$relation->getInverseClass()
617-
));
628+
// orphan removal only applies of the inverse relation is set
629+
if (!$relation->isNullable()) {
630+
$relation->setOrphanRemoval($askOrphanRemoval(
631+
$relation->getOwningClass(),
632+
$relation->getInverseClass()
633+
));
634+
}
618635
}
619636

620637
break;
@@ -658,7 +675,7 @@ function ($name) use ($targetClass) {
658675
);
659676
$relation->setOwningProperty($newFieldName);
660677

661-
$setMapInverseSide($relation);
678+
$askInverseSide($relation);
662679
if ($relation->getMapInverseRelation()) {
663680
$io->comment(sprintf(
664681
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.',
@@ -685,7 +702,7 @@ function ($name) use ($targetClass) {
685702
$relation->getOwningClass()
686703
));
687704

688-
$setMapInverseSide($relation);
705+
$askInverseSide($relation);
689706
if ($relation->getMapInverseRelation()) {
690707
$io->comment(sprintf(
691708
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> object from it.',

tests/Maker/FunctionalTest.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public function getCommandTests()
139139
'ManyToOne',
140140
// nullable
141141
'n',
142+
// do you want to generate an inverse relation? (default to yes)
143+
'',
142144
// field name on opposite side - use default 'userAvatarPhotos'
143145
'',
144146
// orphanRemoval (default to no)
@@ -151,6 +153,31 @@ public function getCommandTests()
151153
->updateSchemaAfterCommand()
152154
];
153155

156+
yield 'entity_many_to_one_simple_no_inverse' => [MakerTestDetails::createTest(
157+
$this->getMakerInstance(MakeEntity::class),
158+
[
159+
// entity class name
160+
'UserAvatarPhoto',
161+
// field name
162+
'user',
163+
// add a relationship field
164+
'relation',
165+
// the target entity
166+
'User',
167+
// relation type
168+
'ManyToOne',
169+
// nullable
170+
'n',
171+
// do you want to generate an inverse relation? (default to yes)
172+
'n',
173+
// finish adding fields
174+
''
175+
])
176+
->setFixtureFilesPath(__DIR__.'/../fixtures/MakeEntityManyToOneNoInverse')
177+
->configureDatabase()
178+
->updateSchemaAfterCommand()
179+
];
180+
154181
yield 'entity_one_to_many_simple' => [MakerTestDetails::createTest(
155182
$this->getMakerInstance(MakeEntity::class),
156183
[
@@ -164,6 +191,8 @@ public function getCommandTests()
164191
'UserAvatarPhoto',
165192
// relation type
166193
'OneToMany',
194+
// inverse side?
195+
'y',
167196
// field name on opposite side - use default 'user'
168197
'',
169198
// nullable
@@ -191,6 +220,8 @@ public function getCommandTests()
191220
'User',
192221
// relation type
193222
'ManyToMany',
223+
// inverse side?
224+
'y',
194225
// field name on opposite side - use default 'courses'
195226
'',
196227
// finish adding fields
@@ -216,6 +247,8 @@ public function getCommandTests()
216247
'OneToOne',
217248
// nullable
218249
'n',
250+
// inverse side?
251+
'y',
219252
// field name on opposite side - use default 'userProfile'
220253
'',
221254
// finish adding fields
@@ -243,8 +276,6 @@ public function getCommandTests()
243276
* normally, we ask for the field on the *other* side, but we
244277
* do not here, since the other side won't be mapped.
245278
*/
246-
// orphanRemoval (default to no)
247-
'',
248279
// finish adding fields
249280
''
250281
])
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace App\Entity;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
/**
8+
* @ORM\Entity()
9+
*/
10+
class User
11+
{
12+
/**
13+
* @ORM\Id
14+
* @ORM\GeneratedValue
15+
* @ORM\Column(type="integer")
16+
*/
17+
private $id;
18+
19+
public function getId()
20+
{
21+
return $this->id;
22+
}
23+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace App\Tests;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
7+
use Doctrine\ORM\EntityManager;
8+
use App\Entity\User;
9+
use App\Entity\UserAvatarPhoto;
10+
11+
class GeneratedEntityTest extends KernelTestCase
12+
{
13+
public function testGeneratedEntity()
14+
{
15+
self::bootKernel();
16+
/** @var EntityManager $em */
17+
$em = self::$kernel->getContainer()
18+
->get('doctrine')
19+
->getManager();
20+
21+
$em->createQuery('DELETE FROM App\\Entity\\User u')->execute();
22+
$em->createQuery('DELETE FROM App\\Entity\\UserAvatarPhoto u')->execute();
23+
24+
$user = new User();
25+
$em->persist($user);
26+
27+
$photo = new UserAvatarPhoto();
28+
$photo->setUser($user);
29+
$em->persist($photo);
30+
31+
$em->flush();
32+
$em->refresh($photo);
33+
34+
$this->assertSame($photo->getUser(), $user);
35+
}
36+
}

0 commit comments

Comments
 (0)