Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

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)