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}
...
]
}
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.
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
)
)
Related
How can I get validated json value using Apigility. For example, I need to get validated user_id under users collection in the following json data.
{
"log_type": "split food",
"meal_type": "Break Fast",
"meal_date": "12-2-2015",
"users": [
{
"user_id": 1,
"food_details": [
{
"food_id":101
}
]
}
]
}
I know fields can be validated through apigility but here is from json.
Thank you
You should look into the documentation of ZF2 validation for validating (form) collections. Some documentation on this can be found here.
You should set the type field like this:
'type' => 'Zend\InputFilter\CollectionInputFilter',
for validation of nested objects (or form field sets) you need to set the type field as follows:
'type' => 'Zend\InputFilter\InputFilter'
You use it like this:
'input_filter' => array(
'log_type' => array(
'validators' => array(
// ... validators ...
),
'filters' => array(
// ... filters ...
),
),
'meal_type' => array(
'validators' => array(
// ... validators ...
),
'filters' => array(
// ... filters ...
),
),
'meal_date' => array(
'validators' => array(
// ... validators ...
),
'filters' => array(
// ... filters ...
),
),
'users' => array(
'required' => true,
'count' => ... optional count ...
'input_filter' => ... input filter or input filter config to use for each element ...
'type' => 'Zend\InputFilter\CollectionInputFilter',
),
'some_complex_element' => array(
'property_of_complex_element' => array(
'name' => 'property_of_complex_element',
'required' => false,
'validators' => array(
// ... validators ...
),
'filters' => array(
// ... filters ...
),
),
'type' => 'Zend\InputFilter\InputFilter',
)
),
An example on how to use this can be found here on stackoverflow
To achieve what you want you most likely have to combine those two solutions. Not sure if it is the easiest way to do it, but it is definitely possible!
EDIT
For people who haven't setup validation at all yet:
For content validation in Apigility You have to use the zfcampus/zf-content-validation module and follow the documentation for configuration. This module allows you to configure your input-filters and validators in a input_filter_spec like you would normally do for form validation in ZF2. Here inside these input-filter config arrays you can use the configs that I referenced above.
So first properly install that module and once set-up you will be able to use these validation types in Apigility.
I have a project to create a flexible information system. In this project I use a relational database (MySQL) and the Yii framework. Sometimes I have objects that can collect different information according to the id of the objecttype.
Basically, I have an object, which has an objecttype. In the objecttype table i have an info field which will contain a jSON with the description of the additional fields that I will require from the user at the moment of the new object creation.
http://www.bedoya.co/screenshots/object-objecttype-relation.png
Ok, now, with PHP I will collect the contents from objecttype_info (stored there as a jSON) and I will generate an array similar to this one:
<?php
$x = array(
'name' => 'Cool name to put in the fieldset label of this object type',
'fields' => array( // List of additional fields that describe this object
'field1' => array(
'label' => 'Field1 label',
'type' => 'text',
'htmlOptions' => array(
'class' => 'field1-class'
'id' => 'field1-id'
'required' => true
)
),
'campo2' => array(
'label' => 'Field2 label',
'type' => 'number',
'htmlOptions' => array(
'class' => 'field2-class'
'id' => 'field2-id'
'required' => false
)
),
)
);
Now, I get the required values with the easy function:
<?php
$x = ObjectType::model()->findByPk( $objecttype_id )->attributes[ 'objecttype_info' ];
?>
I don't know how to set the validation rules for the fields obtained from the objecttype. Any ideas? Am I doing this right?
I've got a (hopefuly) simple task, and my google fu has failed me. Basically, I've got a form with a select which contains an empty value and then number of ids given content can belong to. What I want to do is - validate if the given ids exist, but only if a value is set. This:
$field = new Zend_Form_Element_Select('field');
$field->addValidator(
new Zend_Validate_Db_RecordExists(
array(
'table' => 'categories',
'field' => 'id'
)
));
takes care of the checking if the given id exists, but I'm not able to find any way to omit this if value is empty. One way to do this would be to move this logic to isValid method, but I'm hoping there's nicer way to accomplish this task.
Try to set this form element as not required:
$field->setRequired(false);
When element is not required and is not filled, validators queue won't be run.
Quick example which works for me:
// Zend_Form form body
$this->addElement('select', 'category', array(
'label' => 'Choose category',
'required' => false,
'multiOptions' => array(
null => 'No category selected',
'1' => 'One',
'2' => 'Two',
),
'validators' => array(
array('Db_NoRecordExists', false, array(
'schema' => 'public',
'table' => 'category',
'field' => 'id',
)),
),
));
I am currently validating a URL using the Regex Pattern and it appears to be working correctly. However, if I leave the URL field blank, it should not check the Regex Validation or perhaps just return a message like "No URL given".
Here is an example of my current code I'm working with:
array(
'name' => 'programurl1',
'attributes' => array(
'type' => 'text',
'error_msg' => 'Enter Valid Program URL 1',
'label_msg' => 'Program URL 1 *'
),
'validation' => array(
'required' => true,
'filters' => array(
array(
'name' => 'StripTags'
),
array(
'name' => 'StringTrim'
)
),
'validators' => array(
array(
'name' => 'Regex',
'options' => array(
'pattern' => '/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/'
)
)
)
)
)
I'm not certain how to accomplish what I am looking for when the URL field is blank.
Instead of a type text, you can use the url type. That is specifically meant to enter url values:
$this->add(array(
'name' => 'programurl',
'type' => 'Zend\Form\Element\Url',
'options' => array(
'label' => 'Program URL 1'
),
'attributes' => array(
'required' => 'required'
)
));
The url element is a special HTML5 element, see also the docs.
Zend\Form\Element\Url is meant to be paired with the Zend\Form\View\Helper\FormUrl for HTML5 inputs with type url. This element adds filters and a Zend\Validator\Uri validator to it’s input filter specification for validating HTML5 URL input values on the server.
Afaik if the browser cannot render the url input element, it just shows the text input as a fallback.
Is anyone familiar with the use of ZF2 regex validators within the factory pattern?
I have taken this code from various blogs and other stackoverflow questions, but it does not seem to work.
The addition of the regex validator blocks all changes to my form from updating the database - so the validator must be failing even when I insert a number.
However, when I check
$form -> getMessages();
I get an empty array. Any insight would be appreciated.
To illustrate I use a very simple regex that, as I understand it, would block any entry character that is not a number.
$inputFilter->add($factory->createInput(array(
'name' => 'Number',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'max' => 20,
),
),
),
array(
'name' => 'Regex',
'options' => array(
'pattern' => '/^[0-9]+$',
'messages' => array(
'Invalid input, only 0-9 characters allowed'
),
),
),
)));
At a glance, Regex validator should sit inside "validators" array...