Skip to content

Commit fd6aa64

Browse files
committed
Enable WebAPI interface to handle parameters through constructor
DTO setter methods are no more required, parameters can be handled through constructor for immutable state approach A setter fallback is supported
1 parent 169b3eb commit fd6aa64

File tree

4 files changed

+124
-8
lines changed

4 files changed

+124
-8
lines changed

lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface
6565
*/
6666
private $serviceTypeToEntityTypeMap;
6767

68+
/**
69+
* @var \Magento\Framework\ObjectManager\ConfigInterface
70+
*/
71+
private $config;
72+
6873
/**
6974
* Initialize dependencies.
7075
*
@@ -73,6 +78,7 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface
7378
* @param AttributeValueFactory $attributeValueFactory
7479
* @param CustomAttributeTypeLocatorInterface $customAttributeTypeLocator
7580
* @param MethodsMap $methodsMap
81+
* @param \Magento\Framework\ObjectManager\ConfigInterface $config
7682
* @param ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap
7783
*/
7884
public function __construct(
@@ -81,6 +87,7 @@ public function __construct(
8187
AttributeValueFactory $attributeValueFactory,
8288
CustomAttributeTypeLocatorInterface $customAttributeTypeLocator,
8389
MethodsMap $methodsMap,
90+
\Magento\Framework\ObjectManager\ConfigInterface $config,
8491
ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap = null
8592
) {
8693
$this->typeProcessor = $typeProcessor;
@@ -90,6 +97,7 @@ public function __construct(
9097
$this->methodsMap = $methodsMap;
9198
$this->serviceTypeToEntityTypeMap = $serviceTypeToEntityTypeMap
9299
?: \Magento\Framework\App\ObjectManager::getInstance()->get(ServiceTypeToEntityTypeMap::class);
100+
$this->config = $config;
93101
}
94102

95103
/**
@@ -153,6 +161,33 @@ public function process($serviceClassName, $serviceMethodName, array $inputArray
153161
return $inputData;
154162
}
155163

164+
/**
165+
* @param string $className
166+
* @param array $data
167+
* @return array
168+
* @throws \ReflectionException
169+
*/
170+
private function getConstructorData(string $className, array $data): array
171+
{
172+
$preferenceClass = $this->config->getPreference($className);
173+
$class = new ClassReflection($preferenceClass ?: $className);
174+
175+
$constructor = $class->getConstructor();
176+
if ($constructor === null) {
177+
return [];
178+
}
179+
180+
$res = [];
181+
$parameters = $constructor->getParameters();
182+
foreach ($parameters as $parameter) {
183+
if (isset($data[$parameter->getName()])) {
184+
$res[$parameter->getName()] = $data[$parameter->getName()];
185+
}
186+
}
187+
188+
return $res;
189+
}
190+
156191
/**
157192
* Creates a new instance of the given class and populates it with the array of data. The data can
158193
* be in different forms depending on the adapter being used, REST vs. SOAP. For REST, the data is
@@ -173,9 +208,17 @@ protected function _createFromArray($className, $data)
173208
if (is_subclass_of($className, self::EXTENSION_ATTRIBUTES_TYPE)) {
174209
$className = substr($className, 0, -strlen('Interface'));
175210
}
176-
$object = $this->objectManager->create($className);
177211

212+
// Primary method: assign to constructor parameters
213+
$constructorArgs = $this->getConstructorData($className, $data);
214+
$object = $this->objectManager->create($className, $constructorArgs);
215+
216+
// Secondary method: fallback to setter methods
178217
foreach ($data as $propertyName => $value) {
218+
if (isset($constructorArgs[$propertyName])) {
219+
continue;
220+
}
221+
179222
// Converts snake_case to uppercase CamelCase to help form getter/setter method names
180223
// This use case is for REST only. SOAP request data is already camel cased
181224
$camelCaseProperty = SimpleDataObjectConverter::snakeCaseToUpperCamelCase($propertyName);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor;
7+
8+
class SimpleConstructor
9+
{
10+
/**
11+
* @var int
12+
*/
13+
private $entityId;
14+
15+
/**
16+
* @var string
17+
*/
18+
private $name;
19+
20+
public function __construct(
21+
int $entityId,
22+
string $name
23+
) {
24+
$this->entityId = $entityId;
25+
$this->name = $name;
26+
}
27+
28+
/**
29+
* @return int|null
30+
*/
31+
public function getEntityId()
32+
{
33+
return $this->entityId;
34+
}
35+
36+
/**
37+
* @return string|null
38+
*/
39+
public function getName()
40+
{
41+
return $this->name;
42+
}
43+
}

lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/TestService.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
*/
66
namespace Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor;
77

8-
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\AssociativeArray;
9-
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\DataArray;
10-
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested;
11-
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleArray;
12-
138
class TestService
149
{
1510
const DEFAULT_VALUE = 42;
@@ -25,6 +20,15 @@ public function simple($entityId, $name)
2520
return [$entityId, $name];
2621
}
2722

23+
/**
24+
* @param \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor $simpleConstructor
25+
* @return \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor
26+
*/
27+
public function simpleConstructor(SimpleConstructor $simpleConstructor)
28+
{
29+
return $simpleConstructor;
30+
}
31+
2832
/**
2933
* @param int $entityId
3034
* @return string[]
@@ -34,6 +38,15 @@ public function simpleDefaultValue($entityId = self::DEFAULT_VALUE)
3438
return [$entityId];
3539
}
3640

41+
/**
42+
* @param int $entityId
43+
* @return string[]
44+
*/
45+
public function constructorArguments($entityId = self::DEFAULT_VALUE)
46+
{
47+
return [$entityId];
48+
}
49+
3750
/**
3851
* @param \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested $nested
3952
* @return \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested

lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessorTest.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Magento\Framework\Serialize\SerializerInterface;
1010
use Magento\Framework\Webapi\ServiceInputProcessor;
1111
use Magento\Framework\Webapi\ServiceTypeToEntityTypeMap;
12+
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor;
1213
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\WebapiBuilderFactory;
1314
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\AssociativeArray;
1415
use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\DataArray;
@@ -59,8 +60,8 @@ protected function setUp()
5960
$this->objectManagerMock->expects($this->any())
6061
->method('create')
6162
->willReturnCallback(
62-
function ($className) use ($objectManager) {
63-
return $objectManager->getObject($className);
63+
function ($className, $arguments = []) use ($objectManager) {
64+
return $objectManager->getObject($className, $arguments);
6465
}
6566
);
6667

@@ -202,6 +203,22 @@ public function testNestedDataProperties()
202203
$this->assertEquals('Test', $details->getName());
203204
}
204205

206+
public function testSimpleConstructorProperties()
207+
{
208+
$data = ['simpleConstructor' => ['entityId' => 15, 'name' => 'Test']];
209+
$result = $this->serviceInputProcessor->process(
210+
\Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\TestService::class,
211+
'simpleConstructor',
212+
$data
213+
);
214+
$this->assertNotNull($result);
215+
$arg = $result[0];
216+
217+
$this->assertTrue($arg instanceof SimpleConstructor);
218+
$this->assertEquals(15, $arg->getEntityId());
219+
$this->assertEquals('Test', $arg->getName());
220+
}
221+
205222
public function testSimpleArrayProperties()
206223
{
207224
$data = ['ids' => [1, 2, 3, 4]];

0 commit comments

Comments
 (0)