Nested Parameters/Values in POST Requests

1.2k views Asked by At

I have been thinking about a good way to handle nested/complex values in POST requests to a apigility resource.

For example, an order might contain a collection of order items in a single POST requested that is used to create an order. Both, order and order-item do exist as a resource. However, I would very much like to have only one request that would create order and order item entities. Handling that in the resource is not a problem, but I wonder how you would configure that resource (let´s call it order-place) using the apigiliy UI - or, if at all impossible, using the configuration. Applying validators and filters is one of the key features of apigility, and i´d like to keep using that, even for complex request data.

And before you ask, using an underscore to separate the values scopes, for example order_comment and order_item_comment should not be an option.

Any ideas?:)

Addition: A sample json request payload could look like this:

{
"created_at": "2000-01-01",
"amount" : "5000.00",
"address" : {
    "name": "some name",
    "street": "some street"
    ...
},
"items" : [
    {"productId":99,"qty":1}
    ...
]
}
2

There are 2 answers

1
Wilt On

If you are using the ContentValidation module then you can configure an input filter for the nested resources by assigning it to a variable. Then you have to add a type key (essential otherwise reusing the filter won't work). Now you are able to use this variable in your input_filter_specs and you can reuse the whole filter inside another filter. So something like this in your config.php:

<?php

namespace Application;

// Your address config as if it was used independently
$addressInputFilter => array(
    'name' => array(
        'name' => 'name',
        'required' => true,
        'filters' => array(
            //...
        )
        'validators' => array(
            //...
        )
    ),
    'street' => array(
        'name' => 'street',
        'required' => true,
        'filters' => array(
            //...
        )
        'validators' => array(
            //...
        )
    ),
    // 'type' key necessary for reusing this input filter in other configs
    'type' => 'Zend\InputFilter\InputFilter'
),


'input_filter_specs' => array(
    // The key for your address if you also want to be able to use it independently
    'Application\InputFilter\Address'=> $addressInputFilter,
    // The key and config for your other resource containing a nested address
    'Application\InputFilter\ItemContainingAddress'=> array(
        'address' => $addressInputFilter,
        'amount' => array(
            'name' => 'amount',
            'required' => true,
            'filters' => array(
                //...
            ),
            'validators' => array(
                //...
            )
        )
        //... your other fields
    )
)
0
Maurizio On

Starting from Wilt's answer, I've found that the following code works as well:

# file path: /module/MyApi/config/module.config.php

// some other stuff

  'MyApi\\V1\\Rest\\MyRestService\\Validator' => array(
       'address' => array(
            0 => array(
                'name'     => 'name',
                'required' => true,
                'filters'  => array(),
                'validators' => array(),
            ),
            1 => array(
                'name'     => 'street',
                'required' => true,
                'filters'  => array(),
                'validators' => array(),
            ),
            'type' => 'Zend\InputFilter\InputFilter'
        ),
        'amount' => array(
            'name'       => 'amount',
            'required'   => true,
            'filters'    => array(),
            'validators' => array()
        )

The only problem I get is when address is passed as a field (string or numeric) rather then an array or object. In this case Apigility throws an exception:

Zend\InputFilter\Exception\InvalidArgumentException: Zend\InputFilter\BaseInputFilter::setData expects an array or Traversable argument; received string in /var/www/api/vendor/zendframework/zendframework/library/Zend/InputFilter/BaseInputFilter.php on line 175

Adding address as a further simple (required) field avoids the exception, but then Apigility doesn't see any difference whether we pass address as an array of name and street or a dummy string.