I'm trying to use DTOs for my first time.
I am using Yii2 and PHP 7.1 .
For simplicity my use case can be seen as the following DTOs:
use yii\base\Model;
class OrderDto extends Model
{
/** @var OrderedProductDto[] $orderedProducts */
protected $orderedProducts;
/** @var integer $orderTaxAmount */
protected $orderTaxAmount;
/** @var integer $orderTotalAmount */
protected $orderTotalAmount;
public function rules()
{
//my validation rules go here
}
}
class OrderedProductDto extends Model
{
/** @var integer $quantity */
protected $quantity;
/** @var integer $orderedProductName */
protected $orderedProductName ;
public function rules()
{
//my validation rules go here
}
}
Now, in my hydrator which converts from an internal Order
object into an OrderDto
, I would ideally like to do something like:
class ExternalOrderHydrator
{
public static function getOrderDtoFromOrder(Order $order)
{
/** do stuff to get an $array from $order which looks like
* ['orderTaxAmount' => 5, 'orderTotalAmount' => 100,
* 'orderedProducts' => [
* ['quantity' => 5, 'orderedProductName' => 'Product 1'],
* ],]
* and then do:
*/
$orderDto = new OrderDto();
if (!($orderDto->load($array) and $orderDto->validate())) {
throw new Exception('Something is wrong with provided data.');
}
return $orderDto;
}
}
The problem with this approach is that I cannot simply pass an array with nested subarrays to $orderDto->load()
, because the orderedProducts
field will expect an array of OrderedProductDto
. But it feels counterproductive to construct such an array manually before passing it to the DTO.
So what I'm asking is, is there a way to just pass an array with simple data in nested subarrays to the highest-level DTO, then let all the lower-level DTOs which appear inside the highest level one do the conversion from array to DTO automatically?
Please bear in mind that I am using Yii2 and PHP 7.1
I'm trying to use DTOs for my first time.
I am using Yii2 and PHP 7.1 .
For simplicity my use case can be seen as the following DTOs:
use yii\base\Model;
class OrderDto extends Model
{
/** @var OrderedProductDto[] $orderedProducts */
protected $orderedProducts;
/** @var integer $orderTaxAmount */
protected $orderTaxAmount;
/** @var integer $orderTotalAmount */
protected $orderTotalAmount;
public function rules()
{
//my validation rules go here
}
}
class OrderedProductDto extends Model
{
/** @var integer $quantity */
protected $quantity;
/** @var integer $orderedProductName */
protected $orderedProductName ;
public function rules()
{
//my validation rules go here
}
}
Now, in my hydrator which converts from an internal Order
object into an OrderDto
, I would ideally like to do something like:
class ExternalOrderHydrator
{
public static function getOrderDtoFromOrder(Order $order)
{
/** do stuff to get an $array from $order which looks like
* ['orderTaxAmount' => 5, 'orderTotalAmount' => 100,
* 'orderedProducts' => [
* ['quantity' => 5, 'orderedProductName' => 'Product 1'],
* ],]
* and then do:
*/
$orderDto = new OrderDto();
if (!($orderDto->load($array) and $orderDto->validate())) {
throw new Exception('Something is wrong with provided data.');
}
return $orderDto;
}
}
The problem with this approach is that I cannot simply pass an array with nested subarrays to $orderDto->load()
, because the orderedProducts
field will expect an array of OrderedProductDto
. But it feels counterproductive to construct such an array manually before passing it to the DTO.
So what I'm asking is, is there a way to just pass an array with simple data in nested subarrays to the highest-level DTO, then let all the lower-level DTOs which appear inside the highest level one do the conversion from array to DTO automatically?
Please bear in mind that I am using Yii2 and PHP 7.1
Share Improve this question asked Feb 16 at 19:07 rosecabbagedragonrosecabbagedragon 1706 bronze badges1 Answer
Reset to default 0There is no readily available method to automatically convert data passed to load()
method. Your options are:
- Convert the data in array before you pass them to
load()
method. - Override
load()
method in your top level DTO to do the conversion before passing the data toparent::load()
call. - Force the access to your
OrderDto::$orderedProducts
to go through getter/setter and do the conversion in the setter.
Overriding load()
method might look like this:
class OrderDto extends Model
{
public function load($data, $formName = null)
{
$scope = $formName === null ? $this->formName() : $formName;
if ($scope === '' && !empty($data)) {
$data = $this->transformData($data);
} elseif (isset($data[$scope])) {
$data[$scope] = $this-transformData($data[$scope]);
}
return parent::load($data, $formName);
}
private function transformData($data)
{
// ... logic to transform array data to lower level DTOs
}
//... other OrderDto stuff
}
Forcing access through getters/setters might look like this:
class OrderDto extends Model
{
private $_orderedProduct;
public function getOrderedProduct()
{
return $this->_orderedProduct;
}
public function setOrderedProduct($data)
{
// ... logic to convert array data to lower level DTOs
$this->_orderedProduct = $data;
}
// ... other OrderDto stuff
}
Making sure that setOrderedProduct()
method exists but $orderedProduct
property doesn't exist will make sure that setAttribute()
method called inside Model::load()
will use setter instead of directly assigning value to property.