Skip to content

Commit 75b2232

Browse files
committed
ACP2E-4281: Validation errors on deleted customer attributes when changing customer data.
1 parent 6f8d279 commit 75b2232

File tree

2 files changed

+237
-41
lines changed

2 files changed

+237
-41
lines changed

app/code/Magento/Customer/Model/ResourceModel/Address.php

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ class Address extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
3434
*/
3535
private $orphanedMultiselectCleaner;
3636

37+
/**
38+
* @var DeleteRelation
39+
*/
40+
private $deleteRelation;
41+
42+
/**
43+
* @var CustomerRegistry
44+
*/
45+
private $customerRegistry;
46+
3747
/**
3848
* @param \Magento\Eav\Model\Entity\Context $context
3949
* @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot
@@ -42,6 +52,8 @@ class Address extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
4252
* @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
4353
* @param array $data
4454
* @param OrphanedMultiselectCleaner|null $orphanedMultiselectCleaner
55+
* @param DeleteRelation|null $deleteRelation
56+
* @param CustomerRegistry|null $customerRegistry
4557
*/
4658
public function __construct(
4759
\Magento\Eav\Model\Entity\Context $context,
@@ -50,12 +62,18 @@ public function __construct(
5062
\Magento\Framework\Validator\Factory $validatorFactory,
5163
\Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
5264
$data = [],
53-
?OrphanedMultiselectCleaner $orphanedMultiselectCleaner = null
65+
?OrphanedMultiselectCleaner $orphanedMultiselectCleaner = null,
66+
?DeleteRelation $deleteRelation = null,
67+
?CustomerRegistry $customerRegistry = null
5468
) {
5569
$this->customerRepository = $customerRepository;
5670
$this->_validatorFactory = $validatorFactory;
5771
$this->orphanedMultiselectCleaner = $orphanedMultiselectCleaner
5872
?? ObjectManager::getInstance()->get(OrphanedMultiselectCleaner::class);
73+
$this->deleteRelation = $deleteRelation
74+
?? ObjectManager::getInstance()->get(DeleteRelation::class);
75+
$this->customerRegistry = $customerRegistry
76+
?? ObjectManager::getInstance()->get(CustomerRegistry::class);
5977
parent::__construct($context, $entitySnapshot, $entityRelationComposite, $data);
6078
}
6179

@@ -135,30 +153,6 @@ public function delete($object)
135153
return $result;
136154
}
137155

138-
/**
139-
* Get instance of DeleteRelation class
140-
*
141-
* @deprecated 101.0.0 Use dependency injection instead.
142-
* @see DeleteRelation
143-
* @return DeleteRelation
144-
*/
145-
private function getDeleteRelation()
146-
{
147-
return ObjectManager::getInstance()->get(DeleteRelation::class);
148-
}
149-
150-
/**
151-
* Get instance of CustomerRegistry class
152-
*
153-
* @deprecated 101.0.0 Use dependency injection instead.
154-
* @see CustomerRegistry
155-
* @return CustomerRegistry
156-
*/
157-
private function getCustomerRegistry()
158-
{
159-
return ObjectManager::getInstance()->get(CustomerRegistry::class);
160-
}
161-
162156
/**
163157
* Clean up orphaned multiselect attribute values before validation
164158
*
@@ -178,9 +172,8 @@ private function cleanOrphanedMultiselectValues(DataObject $address): void
178172
*/
179173
protected function _afterDelete(DataObject $address)
180174
{
181-
$customer = $this->getCustomerRegistry()->retrieve($address->getCustomerId());
182-
183-
$this->getDeleteRelation()->deleteRelation($address, $customer);
175+
$customer = $this->customerRegistry->retrieve($address->getCustomerId());
176+
$this->deleteRelation->deleteRelation($address, $customer);
184177
return parent::_afterDelete($address);
185178
}
186179
}

app/code/Magento/Customer/Test/Unit/Model/ResourceModel/AddressTest.php

Lines changed: 216 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,32 @@
77

88
namespace Magento\Customer\Test\Unit\Model\ResourceModel;
99

10+
use Magento\Customer\Api\CustomerRepositoryInterface;
1011
use Magento\Customer\Model\Address;
1112
use Magento\Customer\Model\CustomerFactory;
13+
use Magento\Customer\Model\CustomerRegistry;
14+
use Magento\Customer\Model\ResourceModel\Address\DeleteRelation;
1215
use Magento\Eav\Model\Config;
1316
use Magento\Eav\Model\Entity\AbstractEntity;
1417
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
1518
use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend;
19+
use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface;
1620
use Magento\Eav\Model\Entity\AttributeLoaderInterface;
21+
use Magento\Eav\Model\Entity\Context;
1722
use Magento\Eav\Model\Entity\Type;
23+
use Magento\Eav\Model\ResourceModel\Helper;
24+
use Magento\Eav\Model\ResourceModel\OrphanedMultiselectCleaner;
1825
use Magento\Framework\App\ResourceConnection;
1926
use Magento\Framework\DB\Adapter\Pdo\Mysql;
2027
use Magento\Framework\DB\Select;
28+
use Magento\Framework\Locale\FormatInterface;
29+
use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor;
30+
use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface;
2131
use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite;
2232
use Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot;
23-
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
2433
use Magento\Framework\Validator;
2534
use Magento\Framework\Validator\Factory;
35+
use Magento\Framework\Validator\UniversalFactory;
2636
use PHPUnit\Framework\MockObject\MockObject;
2737
use PHPUnit\Framework\TestCase;
2838

@@ -31,7 +41,7 @@
3141
*/
3242
class AddressTest extends TestCase
3343
{
34-
/** @var \Magento\Customer\Test\Unit\Model\ResourceModel\SubResourceModelAddress */
44+
/** @var SubResourceModelAddress */
3545
protected $addressResource;
3646

3747
/** @var CustomerFactory|MockObject */
@@ -46,6 +56,15 @@ class AddressTest extends TestCase
4656
/** @var RelationComposite|MockObject */
4757
protected $entityRelationCompositeMock;
4858

59+
/** @var OrphanedMultiselectCleaner|MockObject */
60+
protected $orphanedMultiselectCleanerMock;
61+
62+
/** @var DeleteRelation|MockObject */
63+
protected $deleteRelationMock;
64+
65+
/** @var CustomerRegistry|MockObject */
66+
protected $customerRegistryMock;
67+
4968
protected function setUp(): void
5069
{
5170
$this->entitySnapshotMock = $this->createMock(
@@ -56,16 +75,35 @@ protected function setUp(): void
5675
RelationComposite::class
5776
);
5877

59-
$this->addressResource = (new ObjectManagerHelper($this))->getObject(
60-
\Magento\Customer\Test\Unit\Model\ResourceModel\SubResourceModelAddress::class,
61-
[
62-
'resource' => $this->prepareResource(),
63-
'entitySnapshot' => $this->entitySnapshotMock,
64-
'entityRelationComposite' => $this->entityRelationCompositeMock,
65-
'eavConfig' => $this->prepareEavConfig(),
66-
'validatorFactory' => $this->prepareValidatorFactory(),
67-
'customerFactory' => $this->prepareCustomerFactory()
68-
]
78+
$this->orphanedMultiselectCleanerMock = $this->createMock(
79+
OrphanedMultiselectCleaner::class
80+
);
81+
82+
$this->deleteRelationMock = $this->createMock(
83+
DeleteRelation::class
84+
);
85+
86+
$this->customerRegistryMock = $this->createMock(
87+
CustomerRegistry::class
88+
);
89+
90+
$contextMock = $this->prepareContext();
91+
92+
$uniqueValidatorMock = $this->createMock(UniqueValidationInterface::class);
93+
$attributeLoaderMock = $this->createMock(AttributeLoaderInterface::class);
94+
95+
$this->addressResource = new SubResourceModelAddress(
96+
$contextMock,
97+
$this->entitySnapshotMock,
98+
$this->entityRelationCompositeMock,
99+
$this->prepareValidatorFactory(),
100+
$this->prepareCustomerRepository(),
101+
[],
102+
$this->orphanedMultiselectCleanerMock,
103+
$this->deleteRelationMock,
104+
$this->customerRegistryMock,
105+
$uniqueValidatorMock,
106+
$attributeLoaderMock
69107
);
70108
}
71109

@@ -131,6 +169,52 @@ public static function getSaveDataProvider()
131169
];
132170
}
133171

172+
/**
173+
* Prepare context mock object
174+
*
175+
* @return Context|MockObject
176+
*/
177+
protected function prepareContext()
178+
{
179+
$contextMock = $this->getMockBuilder(Context::class)
180+
->disableOriginalConstructor()
181+
->getMock();
182+
183+
$contextMock->expects($this->any())
184+
->method('getEavConfig')
185+
->willReturn($this->prepareEavConfig());
186+
187+
$contextMock->expects($this->any())
188+
->method('getResource')
189+
->willReturn($this->prepareResource());
190+
191+
$contextMock->expects($this->any())
192+
->method('getAttributeSetEntity')
193+
->willReturn(null);
194+
195+
$contextMock->expects($this->any())
196+
->method('getLocaleFormat')
197+
->willReturn($this->createMock(FormatInterface::class));
198+
199+
$contextMock->expects($this->any())
200+
->method('getResourceHelper')
201+
->willReturn($this->createMock(Helper::class));
202+
203+
$contextMock->expects($this->any())
204+
->method('getUniversalFactory')
205+
->willReturn($this->createMock(UniversalFactory::class));
206+
207+
$contextMock->expects($this->any())
208+
->method('getTransactionManager')
209+
->willReturn($this->createMock(TransactionManagerInterface::class));
210+
211+
$contextMock->expects($this->any())
212+
->method('getObjectRelationProcessor')
213+
->willReturn($this->createMock(ObjectRelationProcessor::class));
214+
215+
return $contextMock;
216+
}
217+
134218
/**
135219
* Prepare resource mock object
136220
*
@@ -270,6 +354,17 @@ protected function prepareCustomerFactory()
270354
return $this->customerFactory;
271355
}
272356

357+
/**
358+
* @return CustomerRepositoryInterface|MockObject
359+
*/
360+
protected function prepareCustomerRepository()
361+
{
362+
$customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class)
363+
->disableOriginalConstructor()
364+
->getMockForAbstractClass();
365+
return $customerRepositoryMock;
366+
}
367+
273368
public function testGetType()
274369
{
275370
$this->assertSame($this->eavConfigType, $this->addressResource->getEntityType());
@@ -284,9 +379,117 @@ class SubResourceModelAddress extends \Magento\Customer\Model\ResourceModel\Addr
284379
{
285380
protected $attributeLoader;
286381

382+
/**
383+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
384+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
385+
*/
386+
public function __construct(
387+
Context $context,
388+
Snapshot $entitySnapshot,
389+
RelationComposite $entityRelationComposite,
390+
Factory $validatorFactory,
391+
CustomerRepositoryInterface $customerRepository,
392+
$data = [],
393+
?OrphanedMultiselectCleaner $orphanedMultiselectCleaner = null,
394+
?DeleteRelation $deleteRelation = null,
395+
?CustomerRegistry $customerRegistry = null,
396+
?UniqueValidationInterface $uniqueValidator = null,
397+
?AttributeLoaderInterface $attributeLoader = null
398+
) {
399+
$this->attributeLoader = $attributeLoader;
400+
401+
$this->initializeParentProperties(
402+
$context,
403+
$entitySnapshot,
404+
$entityRelationComposite,
405+
$validatorFactory,
406+
$customerRepository,
407+
$orphanedMultiselectCleaner,
408+
$deleteRelation,
409+
$customerRegistry,
410+
$uniqueValidator,
411+
$attributeLoader
412+
);
413+
}
414+
415+
/**
416+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
417+
*/
418+
private function initializeParentProperties(
419+
$context,
420+
$entitySnapshot,
421+
$entityRelationComposite,
422+
$validatorFactory,
423+
$customerRepository,
424+
$orphanedMultiselectCleaner,
425+
$deleteRelation,
426+
$customerRegistry,
427+
$uniqueValidator,
428+
$attributeLoader
429+
) {
430+
$reflection = new \ReflectionClass(\Magento\Customer\Model\ResourceModel\Address::class);
431+
432+
$prop = $reflection->getProperty('customerRepository');
433+
$prop->setValue($this, $customerRepository);
434+
435+
$prop = $reflection->getProperty('_validatorFactory');
436+
$prop->setValue($this, $validatorFactory);
437+
438+
$prop = $reflection->getProperty('orphanedMultiselectCleaner');
439+
$prop->setValue($this, $orphanedMultiselectCleaner);
440+
441+
$prop = $reflection->getProperty('deleteRelation');
442+
$prop->setValue($this, $deleteRelation);
443+
444+
$prop = $reflection->getProperty('customerRegistry');
445+
$prop->setValue($this, $customerRegistry);
446+
447+
$reflection = new \ReflectionClass(\Magento\Eav\Model\Entity\VersionControl\AbstractEntity::class);
448+
449+
$prop = $reflection->getProperty('entitySnapshot');
450+
$prop->setValue($this, $entitySnapshot);
451+
452+
$prop = $reflection->getProperty('entityRelationComposite');
453+
$prop->setValue($this, $entityRelationComposite);
454+
455+
$reflection = new \ReflectionClass(AbstractEntity::class);
456+
457+
$prop = $reflection->getProperty('_eavConfig');
458+
$prop->setValue($this, $context->getEavConfig());
459+
460+
$prop = $reflection->getProperty('_resource');
461+
$prop->setValue($this, $context->getResource());
462+
463+
$prop = $reflection->getProperty('_localeFormat');
464+
$prop->setValue($this, $context->getLocaleFormat());
465+
466+
$prop = $reflection->getProperty('_resourceHelper');
467+
$prop->setValue($this, $context->getResourceHelper());
468+
469+
$prop = $reflection->getProperty('_universalFactory');
470+
$prop->setValue($this, $context->getUniversalFactory());
471+
472+
$prop = $reflection->getProperty('transactionManager');
473+
$prop->setValue($this, $context->getTransactionManager());
474+
475+
$prop = $reflection->getProperty('objectRelationProcessor');
476+
$prop->setValue($this, $context->getObjectRelationProcessor());
477+
478+
$prop = $reflection->getProperty('uniqueValidator');
479+
$prop->setValue($this, $uniqueValidator);
480+
481+
$prop = $reflection->getProperty('attributeLoader');
482+
$prop->setValue($this, $attributeLoader);
483+
484+
$prop = $reflection->getProperty('_type');
485+
$prop->setValue($this, null);
486+
487+
$this->connectionName = 'customer';
488+
}
489+
287490
/**
288491
* @param null $object
289-
* @return \Magento\Customer\Model\ResourceModel\Address|AbstractEntity
492+
* @return AbstractEntity
290493
*/
291494
public function loadAllAttributes($object = null)
292495
{

0 commit comments

Comments
 (0)