Skip to content

Commit c3ccf5f

Browse files
committed
Ask the user if they want the inverse side of a relation
1 parent 2fd2e0c commit c3ccf5f

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
@@ -235,12 +235,6 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
235235
// save the inverse side if it's being mapped
236236
if ($newField->getMapInverseRelation()) {
237237
$fileManagerOperations[$otherManipulatorFilename] = $otherManipulator;
238-
} else {
239-
// print message about it not being saved
240-
$fileManagerOperations[] = sprintf(
241-
'The inverse side of the relation was not mapped in "%s" because it lives in the vendor/ directory.',
242-
$newField->getInverseClass()
243-
);
244238
}
245239
$currentFields[] = $newFieldName;
246240
} else {
@@ -554,8 +548,30 @@ function ($name) use ($targetClass) {
554548
return $io->confirm(sprintf('Do you want to automatically delete orphaned <comment>%s</comment> objects (orphanRemoval)?', $owningClass), false);
555549
};
556550

557-
$setMapInverseSide = function (EntityRelation $relation) {
558-
$relation->setMapInverseRelation(!$this->isClassInVendor($relation->getInverseClass()));
551+
$askInverseSide = function (EntityRelation $relation) use ($io) {
552+
if ($this->isClassInVendor($relation->getInverseClass())) {
553+
$relation->setMapInverseRelation(false);
554+
}
555+
556+
// recommend an inverse side, except for OneToOne, where it's inefficient
557+
$recommendMappingInverse = EntityRelation::ONE_TO_ONE === $relation->getType() ? false : true;
558+
559+
$getterMethodName = 'get'.Str::asCamelCase(Str::getShortClassName($relation->getOwningClass()));
560+
if (EntityRelation::ONE_TO_ONE !== $relation->getType()) {
561+
// pluralize!
562+
$getterMethodName = Str::singularCamelCaseToPluralCamelCase($getterMethodName);
563+
}
564+
$mapInverse = $io->confirm(
565+
sprintf(
566+
'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>?',
567+
Str::getShortClassName($relation->getInverseClass()),
568+
Str::getShortClassName($relation->getOwningClass()),
569+
Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass())),
570+
$getterMethodName
571+
),
572+
$recommendMappingInverse
573+
);
574+
$relation->setMapInverseRelation($mapInverse);
559575
};
560576

561577
switch ($type) {
@@ -572,7 +588,7 @@ function ($name) use ($targetClass) {
572588
$relation->getOwningClass()
573589
));
574590

575-
$setMapInverseSide($relation);
591+
$askInverseSide($relation);
576592
if ($relation->getMapInverseRelation()) {
577593
$io->comment(sprintf(
578594
'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.',
@@ -583,13 +599,14 @@ function ($name) use ($targetClass) {
583599
$relation->getInverseClass(),
584600
Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass()))
585601
));
586-
}
587602

588-
if (!$relation->isNullable()) {
589-
$relation->setOrphanRemoval($askOrphanRemoval(
590-
$relation->getOwningClass(),
591-
$relation->getInverseClass()
592-
));
603+
// orphan removal only applies of the inverse relation is set
604+
if (!$relation->isNullable()) {
605+
$relation->setOrphanRemoval($askOrphanRemoval(
606+
$relation->getOwningClass(),
607+
$relation->getInverseClass()
608+
));
609+
}
593610
}
594611

595612
break;
@@ -633,7 +650,7 @@ function ($name) use ($targetClass) {
633650
);
634651
$relation->setOwningProperty($newFieldName);
635652

636-
$setMapInverseSide($relation);
653+
$askInverseSide($relation);
637654
if ($relation->getMapInverseRelation()) {
638655
$io->comment(sprintf(
639656
'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.',
@@ -660,7 +677,7 @@ function ($name) use ($targetClass) {
660677
$relation->getOwningClass()
661678
));
662679

663-
$setMapInverseSide($relation);
680+
$askInverseSide($relation);
664681
if ($relation->getMapInverseRelation()) {
665682
$io->comment(sprintf(
666683
'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
@@ -206,6 +206,8 @@ public function getCommandTests()
206206
'ManyToOne',
207207
// nullable
208208
'n',
209+
// do you want to generate an inverse relation? (default to yes)
210+
'',
209211
// field name on opposite side - use default 'userAvatarPhotos'
210212
'',
211213
// orphanRemoval (default to no)
@@ -218,6 +220,31 @@ public function getCommandTests()
218220
->updateSchemaAfterCommand()
219221
];
220222

223+
yield 'entity_many_to_one_simple_no_inverse' => [MakerTestDetails::createTest(
224+
$this->getMakerInstance(MakeEntity::class),
225+
[
226+
// entity class name
227+
'UserAvatarPhoto',
228+
// field name
229+
'user',
230+
// add a relationship field
231+
'relation',
232+
// the target entity
233+
'User',
234+
// relation type
235+
'ManyToOne',
236+
// nullable
237+
'n',
238+
// do you want to generate an inverse relation? (default to yes)
239+
'n',
240+
// finish adding fields
241+
''
242+
])
243+
->setFixtureFilesPath(__DIR__.'/../fixtures/MakeEntityManyToOneNoInverse')
244+
->configureDatabase()
245+
->updateSchemaAfterCommand()
246+
];
247+
221248
yield 'entity_one_to_many_simple' => [MakerTestDetails::createTest(
222249
$this->getMakerInstance(MakeEntity::class),
223250
[
@@ -231,6 +258,8 @@ public function getCommandTests()
231258
'UserAvatarPhoto',
232259
// relation type
233260
'OneToMany',
261+
// inverse side?
262+
'y',
234263
// field name on opposite side - use default 'user'
235264
'',
236265
// nullable
@@ -258,6 +287,8 @@ public function getCommandTests()
258287
'User',
259288
// relation type
260289
'ManyToMany',
290+
// inverse side?
291+
'y',
261292
// field name on opposite side - use default 'courses'
262293
'',
263294
// finish adding fields
@@ -283,6 +314,8 @@ public function getCommandTests()
283314
'OneToOne',
284315
// nullable
285316
'n',
317+
// inverse side?
318+
'y',
286319
// field name on opposite side - use default 'userProfile'
287320
'',
288321
// finish adding fields
@@ -310,8 +343,6 @@ public function getCommandTests()
310343
* normally, we ask for the field on the *other* side, but we
311344
* do not here, since the other side won't be mapped.
312345
*/
313-
// orphanRemoval (default to no)
314-
'',
315346
// finish adding fields
316347
''
317348
])
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)