symfony assertions raw data validation with if/else statement - php

I have a such json:
{
"id":1,
"name":"some name",
"decorations":[
{
"id":1,
"options":[
{
"type":"color",
"label":"Color",
"items":[
{
"label":"Green shades",
"color_group":{
"shade":"green",
"name":"Green"
},
"colors":[
{
"rgb":"249,249,249",
"hex":"#FBFBFB"
},
{
"rgb":"249,249,250",
"hex":"#FBFBFF"
}
]
},
{
"label":"White shades",
"color_group":{
"shade":"white",
"name":"White"
},
"colors":[
{
"rgb":"255,255,255",
"hex":"#FFFFFF"
}
]
}
]
},
{
"type":"position",
"label":"Position",
"items":[
{
"label":"Front"
},
{
"label":"Back"
}
]
}
]
}
]
}
which I want to validate, rules are:
id, name, decorators required
each decorator should have id and options
each option should have type, label and items. each option has few types, all of them has same structure of each item except of a color type
so each item should have label, and in case of color option each item should have color_group, colors
- where color_group should have 'shade' and name
- each color in colors should have rgb and hex
I want to use symfony assertions to validate that json, and all is more or less straightforward except that rule where depends on a type I have different rules... I do not know how to do it right, I found that I can use expression or callback assertion but in both case it showing how to validate object but not a array.
Here is what I have:
new Assert\Collection(
[
'allowExtraFields' => true,
'fields' => [
'id' => [
new Assert\Required(),
new Assert\Type(['type' => 'integer']),
],
'decorations' => new Assert\All(
[
new Assert\Collection(
[
'allowExtraFields' => true,
'fields' => [
'id' => [
new Assert\Required(),
new Assert\Type(['type' => 'integer']),
],
'options' => new Assert\All(
[
'type' => [
new Assert\Required(),
new Assert\Type(['type' => 'string']),
],
'label' => [
new Assert\Required(),
new Assert\Type(['type' => 'string']),
],
'items' => new Assert\All(
[
new Assert\Collection(
[
'allowExtraFields' => true,
'fields' => [
// label is required for all types
'label' => [
new Assert\Required(),
new Assert\Type(['type' => 'string']),
]
// here I should add constraints for colors, if type equal to "color", but do not know how to make that if/else statement...
]
]
)
]
)
]
)
]
]
)
]
)
]
]
);

Related

how can i use the array in where statement in laravel 9

here is the data
req[
{
"_id": "123",
"_ar_id": "111",
"_val": "10",
"ar_date": "2023-02-02"
},
{
"_id": "1234",
"_ar_id": null,
"_val": "0",
"ar_date": "2023-02-02"
}
]
$validator = Validator::make($req->all(), [
'req.*._id' => 'required',
'req.*._val' => 'required',
'req.*.ar_date' => 'required',
]);
if ($validator->fails()) {
return response()->json(['stat' => 0,'msg' => $validator->errors(), 'data' => "Please input required details" ]);
}
since i validate the request, how can i use it in where to ?
here's the code
$record = ArRecordCount::where('ar_list_id', $list_id)->where('ar_date', $ar_date)->first();
and if has record then it will update, if no record then delete

Laravel: Is it possible to apply different form validation rules to objects in an array

I have a Laravel 9 API.
I am posting the following json to an endpoint.
{
"shapes": [
{
"type": "Point",
"coordinates": [1, 2]
},
{
"type": "MultiPolygon",
"coordinates": [
[
[
[1, 2],
[3, 4],
[5, 6],
]
]
]
}
]
}
I have some form validation (in a form request class) that needs to validate the coordinates given differently based on the type.
I.e. Apply the PointCoordinatesValidator rule if the shape's type is Point or apply the MultiPolygonCoordinatesValidator rule if the shape's type is MultiPolygon.
public function rules()
{
return [
'shapes' => 'required|array|min:1',
'shapes.*.type' => [
'required',
'string',
'in:Point,MultiPolygon',
],
'shapes.*.coordinates' => [
'required',
request()->input('shapes.*.type') == 'Point' ?
new PointCoordinatesValidator :
new MultiPolygonCoordinatesValidator,
],
];
}
However, when running this, the custom MultiPolygonCoordinatesValidator rule is being applied to both shapes, not just the shape where it's type == MultiPolygon.
I can see that
request()->input('shapes.*.type') is Point for shapes.0 and MultiPolygon for shapes.1
Am I expecting too much from the validation? Is it possible to validate the different items in the array differently based on a value in that array?
Just pass the array element in and then implement the type check in your rule:
'shapes.*' => [
'required',
new CoordinatesValidator(),
],
Then, in the rule, the $value will contain the type and the coordinates.
It definitely is possible, but not by checking request()->input('shapes.*.type') == 'Point'; that * is a wildcard, and isn't really checked on each iteration.
You can construct the rules from the input, but it's a bit of an anti-pattern, and might have issues if type is null, or similar.
Give this a shot:
public function rules() {
$rules = [
'shapes' => 'required|array|min:1',
'shapes.*.type' => [
'required',
'string',
'in:Point,MultiPolygon',
]
];
foreach(request()->input('shapes', []) as $index => $shape) {
$rules["shapes.{$index}.coordinates"] = [
'required',
$shape['type'] == 'Point' ? new PointCoordinatesValidator() : new MultiPolygonCoordinatesValidator()
];
}
return $rules;
}
Using that, you'll get something like this:
[
'shapes' => 'required|array|min:1',
'shapes.*.type' => [
'required',
'string',
'in:Point,MultiPolygon'
],
'shapes.0.coordinates' => [
'required',
new PointCoordinatesValidator()
],
'shapes.1.coordinates' => [
'required',
new MultiPolygonCoordinatesValidator()
]
// And so on...
]
Your $rules array can get pretty large, depending on the number of shapes being uploaded, but this should allow you to explicitly target certain validation rules for each shape, depending on the type supplied.

Elasticsearch multi_match in combination with normal match

I have the following function which searches for results in elasticsearch.
I want to do the following request with PHP and Guzzle.
/**
* {#inheritdoc}
*/
public function sendSearchRequest($es_node, $request) {
try {
if (isset($es_node)) {
$ssl = $es_node->get('field_ess_verify_ssl')->value;
$ssl_val = $ssl ? 'https://' : 'http://';
$body = [
'json' => [
'query' => [
'bool' => [
'should' => [
[
'multi_match' => [
'query' => $request->get('search'),
'fields' => ['message', 'event.type'],
'operator' => 'AND',
],
],
[
'match' => [
'event.type' => $request->get('type'),
],
],
[
'match' => [
'event.labels' => $request->get('label'),
],
],
],
],
],
],
];
$response = $this->httpClient->request('POST', $ssl_val . $es_node->get('field_ess_host')->value . ':' . $es_node->get('field_ess_port')->value . '/***/***/_search?pretty', $body)
->getBody()
->getContents();
return json_decode($response, TRUE)['hits']['hits'] ?? '';
}
} catch (Exception $exception) {
\Drupal::logger(__METHOD__)->error('No ES node has been found.');
return FALSE;
}
}
But with this i get a parsing exception which means that the multi_match doesn't work this way. If i use a 'normal' match in that place it's working fine but then i am limited to 1 field.
The other match fields uses other form fields for input and that has always one value.
But with form field 'Description search' i want to look in multiple fields with the AND operator.
Anyone knows how to fix this?
Screenshot of the form:
Your query looks good to me and ran in my test. It should work as is.
But in the interest of future readers, this is how multi-match queries work with elastic search.
GET /_search
{
"query": {
"multi_match" : {
"query": "Will Smith",
"type": "best_fields",
"fields": [ "first_name", "last_name" ],
"operator": "and"
}
}
}
Note that this example from the elastic search documentation is identical in nature to your section of the code above:
...
'multi_match' => [
'query' => $request->get('search'),
'fields' => ['message', 'event.type'],
'operator' => 'AND',
],
...
This is how multi-match queries are formed, and you did it.

Search by Date range in Elastic search malformed query

I am trying to integrate search by date range with PHP and elastic search
$params1 = [
'index' => 'joborders',
'type' => 'joborder',
'from' =>0,
'size' => 50,
'body' => [
'query' => [
'query_string' => [
'query' => $wildCardString,
'fields' => ['description'],
]
]
]
];
$filter_date=array();
$filter_date['range']['datecreatedsort']['gte']='2015-11-27';
$filter_date['range']['datecreatedsort']['lte']='2017-11-27';
$params1['body']['query']['filtered']['filter']=$filter_date;
$params1['body']['sort']['datecreatedsort']['order'] = 'desc';
try {
$results = $client->search($params1);
//print_r($results);
}
catch (Exception $e) {
$last = $client->transport->getLastConnection()->getLastRequestInfo();
$last['results']['error'] = [];
print_r($last);
}
When I am running above query I am getting following error
[query_string] malformed query, expected [END_OBJECT] but found
[FIELD_NAME]","line":1,"col":78},"status":400}
datecreatedsort filed mapping is date type is date and value in
elastic search db is "datecreatedsort":"2016-05-30T09:39:40.000Z"
please help where is the issue in elastic query.
It's the native elasticsearch request.
In PHP you need to create array query with the same structure.
GET /joborders/_search?pretty=true
{
"query": {
"bool": {
"must": [
{
"range": {
"created_at": {
"gte": "2017-11-22 13:49:00",
"lte": "2017-11-22 23:50:00"
}
}
}
]
}
}
PS You need to pass date ranges in the same format as datecreatedsort in db ("2016-05-30T09:39:40.000Z")
try this:
{
"query": {
"filtered": {
"query": {
"query_string": {
"default_field": "description",
"query": $wildCardString
}
},
"filter": {
"range": {
"datecreatedsort": {
"gte": '2015-11-27',
"lte": '2017-11-27'
}
}
}
}
}
}
your code for the query will look something like this:
$query=array(
'filtered'=>array(
'query' => array(
'query_string' => [
'query' => $wildCardString,
'fields' => ['description'],
]
),
'filter'=>$filter_date
)
);
Hi Got the solution its working fine
$params1 = [
'index' => 'joborders',
'type' => 'joborder',
'body' => [
'query' => [
'bool' => [
'filter' => [
'range' => [ 'date_modified' => ['gt'=>$duration,'lt'=>$today,'boost'=> '2.0'] ]
],
'must' => [
'match' => [ 'description' => $wildCardString ]
]
]
]
]
];
Thanks guys for help.

Zend Framework 2 filter / validate array of contents

How do I apply a filter to a field element with the contents of an array?
For example:
$this->add(
"name" => "tags",
"type" => "text",
"filter" => array(
array("name" => "StripTags"),
array("name" => "StringTrim")
)
);
$tags[0] = "PHP";
$tags[1] = "CSS";
If I attempt to filter I receive an error saying a scalar object is excepted, array given.
This isn't really possible at this time. Your best bet is to use a Callback filter and filter each Item individually. Something like this
$this->add(
"name" => "tags",
"type" => "text",
"filter" => array(
array("name" => "Callback", "options" => array(
"callback" => function($tags) {
$strip = new \Zend\Filter\StripTags();
$trim = new \Zend\Filter\StringTrim();
foreach($tags as $key => $tag) {
$tag = $strip->filter($tag);
$tag = $trim->filter($tag);
$tags[$key] = $tag;
}
return $tags;
}))
)
);
I realize this is old but you can specify the input type as ArrayInput and InputFilter will handle it as expected:
"name" => "tags",
"type" => "Zend\\InputFilter\\ArrayInput", // Treat this field as an array of inputs
"filter" => array(
array("name" => "StripTags"),
array("name" => "StringTrim")
)
I've made a CollectionValidator that applies an existing validator to all items in an array.
I'm using it with Apigility as such:
'input_filter_specs' => [
'Api\\Contact\\Validator' => [
[
'name' => 'addresses',
'required' => false,
'filters' => [],
'validators' => [
[
'name' => 'Application\\Validator\\CollectionValidator',
'options' => ['validator' => 'Api\\Address\\Validator']
]
],
'description'=> 'List of addresses for contact'
],
[
'name' => 'birthdate',
# ...
]
],
]
I'm not sure if this is how you would use a validator inside a controller, but probably something like this:
new Collection(array('validator' => 'Zend\Validator\CreditCard'))
It returns validation_messages per index. Let's say it was REST POST request to create a contact, it indicates that the second address contains an error in the zipcode field.
{
"detail": "Failed Validation",
"status": 422,
"title": "Unprocessable Entity",
"type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
"validation_messages": {
"addresses": {
"1": {
"zipcode": {
"notAlnum": "The input contains characters which are non alphabetic and no digits"
}
}
},
"birthdate": {
"dateInvalidDate": "The input does not appear to be a valid date"
}
}
}
The Collection validator:
<?php
namespace Application\Validator;
class Collection extends \Zend\Validator\AbstractValidator implements \Zend\ServiceManager\ServiceLocatorAwareInterface {
protected $serviceLocator;
protected $em;
protected $messages;
protected $options = array(
'validator' => null
);
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator->getServiceLocator();
}
public function getServiceLocator() {
return $this->serviceLocator;
}
public function isValid($array) {
$inputFilterManager = $this->getServiceLocator()->get('inputfiltermanager');
$validatorName = $this->getOption('validator');
$this->messages = [];
$isvalid = true;
foreach($array as $index => $item) {
$inputFilter = $inputFilterManager->get($validatorName);
$inputFilter->setData($item);
$isvalid = $isvalid && $inputFilter->isValid($item);
foreach($inputFilter->getMessages() as $field => $errors) {
foreach($errors as $key => $string) {
$this->messages[$index][$field][$key] = $string;
}
}
}
return $isvalid;
}
public function getMessages() {
return $this->messages;
}
}
Current limitations:
No support for translation
Only the errors for the first erroneous array item are returned.
I had a very simular issue and I was able to solve it with Zend\Form\Element\Collection.
With the Collection Element I was able to validate inputs that looks like
$post = [
[
'idUser' => 1,
'address' => 'foo street',
],
[
'idUser' => 2,
'address' => 'bar street',
],
];
For a more detailed explanation check out the Zend Documentation and this working example

Categories