Find missing elements from array based on another (keys not values) - php

I need to test an array to make sure it has all elements I am expecing. The twist here is that we are talking about multidimensional arrays. Here is an example:
$required_data = [
'firstname',
'lastname',
'shipping' => [
'address',
'city',
'contacts' => [
'phone',
'email'
]
]
];
$incoming_data = [
'firstname' => 'Mike',
'shipping' => [
'address' => '1st Avenue',
'contacts' => [
'phone',
'email' => 'test#example.com'
]
]
];
I simply need to detect the two missing elements (lastname and city). I don't care about values. I test them separately.
At the moment I'm playing with this function just to get true when all required elements are provided or false otherwise.
It works when $incoming_data doesn't have any value but as soon as I start adding values (eg. Mike, 1st Avenue etc.) it fails.
function validate($incoming_data, $required_data)
{
foreach ($required as $key => $value) {
if (!isset($data[$key])) {
return false;
}
if (is_array($data[$key]) && false === validate($data[$key], $value)) {
return false;
}
}
return true;
}
I can't understand where my function starts playing with values. All is see are comparisons based on keys. Whut?
Thanks.

you have to set empty data in your $required_data tab like so :
$required_data = [
'firstname' => '',
'lastname' => '',
'shipping' => [
'address' => '',
'city' => '',
'contacts' => [
'phone',
'email'
]
]
];

Related

Not able to Validation data in Yii2 model

I am not able to validate value in model. I am newbie in this can anyone suggest what am I doing wrong. I am working on REST API with excel file upload and validating single row at a time
Here is my data and load data in model
$data = array(
'firstName' => 'asdjqkw',
'lastName' => '',
'email' => 'ansm#',
'companyName' => 'ddq',
'address_1' => '',
'address_2' => 'aas',
'country' => '',
'state' => 'New Brunswick',
'city' => '87875',
'zip' => '484527',
);
$model->load($data);
if (!$model->validate()) {
$validation = $model->errors;
}
And here is my model rules and attribute labels as I defined required, email and maxlength validation in it but still some values is available still it is sending required validation error for that field
public function rules() {
return [
[['firstName', 'lastName', 'email', 'companyName', 'address_1', 'country', 'state', 'city', 'zip'], 'required'],
['email', 'email'],
[['firstName', 'lastName', 'email', 'companyName', 'address_1', 'country', 'state', 'city', 'zip'], 'string', 'max' => 250],
];
}
public function attributeLabels() {
return [
'firstName' => 'First Name',
'lastName' => 'Last Name',
'email' => 'Email',
'companyName' => 'Company Name',
'address_1' => 'Address 1',
'country' => 'Country',
'state' => 'State',
'city' => 'City',
'zip' => 'Zip',
];
}
And I get validation errors after loading this data
"firstName": [
"First Name cannot be blank."
],
"lastName": [
"Last Name cannot be blank."
],
"email": [
"Email cannot be blank."
],
"companyName": [
"Company Name cannot be blank."
],
"address_1": [
"Address 1 cannot be blank."
],
"country": [
"Country cannot be blank."
],
"state": [
"State cannot be blank."
],
"city": [
"City cannot be blank."
],
"zip": [
"Zip cannot be blank."
]
So what's happening here is you're making a call to the load method of your model. This has 2 arguments, the first is your data and the second is a formName. When there is no formName argument declared, Yii2 will set it as equal to your model. So in this case it attempts to load data like this: $data['modelName']. So you could change your data array to follow the format of this:
$data = array(
'yourModel' => [
'yourData => 'yourValue'
]
)
But there's an easier way that doesn't require us to constantly remember how to construct our data arrays: set the 'formName' argument of the 'load' method to an empty string like so:
$model->load($data, '');
The reason this works is it allows the following code to run inside the 'load' method (where $scope is your formData argument):
if ($scope === '' && !empty($data)) {
$this->setAttributes($data);
return true;
}
This way we can continue writing our arrays in one less dimension, a small but nice way to keep our arrays ever-so-slightly tidier :).

Validate that JSON array has one associative array with fixed integer value

I am trying to validate some JSON using Opis's package. I am trying to validate that an array has at least one associative array with an id of value 1. Here is the code I've got:
$json = [
[
'id' => 1,
],
[
'id' => 2,
],
[
'id' => 3
]
];
$rules = [
'type' => 'array',
'contains' => [
'type' => 'array',
'properties' => [
'id' => [
'type' => 'integer',
'const' => 1,
],
],
'required' => ['id']
],
'minContains' => 1,
];
$validated = Common::validateJSON($json, json_encode($rules));
and here is the validateJSON method code:
public static function validateJSON($json, $rules)
{
$validator = new Validator();
// Validate
$result = $validator->validate($json, $rules);
if ($result->isValid()) {
return true;
}
$errorMessages = [];
if ($result->hasError()) {
$formatter = new ErrorFormatter();
$errorMessages[] = $formatter->format($result->error());
}
return $errorMessages;
}
so, in this case $validated returns:
array:1 [
0 => array:1 [
"/" => array:1 [
0 => "At least 1 array items must match schema"
]
]
]
changing $rules to this:
$rules = [
'type' => 'array',
'contains' => [
'type' => 'array',
],
'minContains' => 1,
];
returns the same result which is weird for me.
Changing const to any number doesn't change what is returned. So, my guess is that I am doing something wrong but I don't know what.
I've been googling various things nothing helped. I've been looking at the JSON schema site, particularly here but I haven't figured it out.
Before validating, as I am not json decoding the data as it is not coming from an http request, do this:
$json = json_encode($json);
$json = json_decode($json); // this, I think, will turn associative arrays into objects which makes it work
and the second type must be object.

There is a better way to translate the results of a query using yii2tech\spreadsheet\Spreadsheet

I'm doing a manual query. You can translate the names of the columns easily, but I can not do it with the results of the columns in a slightly more efficient way. The results are printed in excel with the help of yii2tech\spreadsheet\Spreadsheet.
Preparing query
$columns = [
[
'attribute' => 'description',
'label' => Yii::t('data', 'Description')
],
[
'attribute' => 'type',
'label' => Yii::t('data', 'Type')
]
];
$query
->addSelect(['description' => 'data.description'])
->addSelect(['type' => 'data.type'])
->from('data')
$rows = $query->all();
So far I make the query. The following is my way of translating the results of the type column. Because they can be just some values.
Translating results
foreach ($rows as $key => $row) {
$rows[$key]['type'] = Yii::t('data', $row['type']);
}
This data is exported to xls format:
Exporting results
$exporter = new Spreadsheet([
'dataProvider' => new ArrayDataProvider([
'allModels' => $rows,
]),
'columns' => $columns,
]);
You may define translation inside of $columns declaration - it will save you manual iteration trough results array to replace type with translated string:
$columns = [
[
'attribute' => 'description',
'label' => Yii::t('data', 'Description'),
],
[
'attribute' => 'type',
'label' => Yii::t('data', 'Type'),
'value' => function ($data) {
return Yii::t('data', $data['type']);
}
],
];
If sheet is big and types are often repeated, you may try to cache translated string - Yii::t() may be quite expensive:
$columns = [
[
'attribute' => 'description',
'label' => Yii::t('data', 'Description'),
],
[
'attribute' => 'type',
'label' => Yii::t('data', 'Type'),
'value' => function ($data) {
static $translations = [];
if (!isset($translations[$data['type']])) {
$translations[$data['type']] = Yii::t('data', $data['type']);
}
return $translations[$data['type']];
},
],
];
This will call Yii::t() only once per unique type. But if list of types is small and hardcoded, you may simplify this even more - create getTranslatedTypes() static method, which returns translated list of all types:
public static function getTranslatedTypes() {
return [
'some type' => Yii::t('data', 'some type'),
// ...
];
}
And use it as a source of translations:
$translations = Type::getTranslatedTypes();
$columns = [
[
'attribute' => 'description',
'label' => Yii::t('data', 'Description'),
],
[
'attribute' => 'type',
'label' => Yii::t('data', 'Type'),
'value' => function ($data) use ($translations) {
return $translations[$data['type']] ?? $data['type'];
},
],
];

How to filter in nested collections?

Hello StackOverflow community, I am working with laravel and i created this collection
$headquarters = collect([
[
'headquarter' => 'Leon',
'offers' => [
[
'name' => 'Name1',
'slug' => 'Name1'
], [
'name' => 'Name2',
'slug' => 'Name2'
]
]
],[
'headquarter' => 'Granada',
'offers' => [
[
'name' => 'Name3',
'slug' => 'Name3'
],[
'name' => 'Name4',
'slug' => 'Name4'
],[
'name' => 'Name5',
'slug' => 'Name5'
]
]
]
]);
I want to filter this collection by headquarter and offer slug in order to get a Single offer
Right now i am trying using filter
$offer = $this->headquarters()
->filter(function($hq) use ($headquarter, $slug) {
return $hq['headquarter'] == $headquarter && $hq['offers']['slug'] == $slug;
});
But with no success.
Thanks for any advice
You can get all offers in specific headquarter with this code
$this->headquarters()->where('headquarter', 'Leon')[0]['offers'][0];
Then for each all offers
foreach ($this->headquarters()->where('headquarter', 'Leon')[0]['offers'] as $offer) {
print_r($offer);
}
or try this code
$offer = collect($this->headquarters()->where('headquarter', $headquarter)
->first()['offers'])
->where('slug', $slug)->first();
You $hq['offers'] is an array, you should access as $hq['offers'][0]['slug'].
$offer = $this->headquarters()
->filter(function($hq) use ($headquarter, $slug) {
return $hq['headquarter'] == $headquarter && in_array($slug, array_column($hq['offers'], 'slug'));
});

Fatal error</b>: Call to a member function withJson() on array in

i am new to PHP coding and am trying to set up with Slim 3. I am just trying to write an add function to add fname, lname, address, email & age.
I keep getting the error:
"Fatal error: Call to a member function withJson() on array in"
this is for the line:
"return $res->withJson(["person"=>$data],404)->withAddedHeader('Location',"/users/$id", ["text"=>"person does not exist"]);" after the "catch(PDOException $e) {".
Thanks in advance..
Here is the code:
<?php
/POST /persons : add a new person
$app->post('/persons', function ($req, $res){
try{
// check if all data is there
$data=$req->getParsedBody();
$filter = [
'fname' => [
'filter' => FILTER_VALIDATE_REGEXP,
'options' => [
'regexp' => '/[a-zA-Z]+/'
]
],
'lname' => [
'filter' => FILTER_VALIDATE_REGEXP,
'options' => [
'regexp' => '/[a-zA-Z]+/'
]
],
'address' => [
'filter' => FILTER_VALIDATE_REGEXP,
'options' => [
'regexp' => '/[a-zA-Z]+/'
]
],
'email' => [
'filter' => FILTER_VALIDATE_EMAIL,
'options' => [
'regexp' =>'/[A-Z0-9._%+-]+#[A-Z0-9.-]+/'
]
],
'age' => [
'filter' => FILTER_VALIDATE_INT,
'options' => [
'regexp' => '/{1,100}'
]
]
];
$res = filter_var_array($data,$filter);
if(empty($data)){
return $res->withJson( ["error"=>
[ "text" => "The information provided is inadequate"]
],400);
}else
// ....
$sql = 'insert into person (fname, lname, address, email, age)values(:fname,:lname,:address,:email,:age)';
// prepare
$stmt =$this->db->prepare($sql);
// execute with values from $data
$stmt->execute(['fname'=>$res['fname'],'lname'=>$res['lname'],'address'=>$res['address'],'email'=>$res['email'],'age'=>$res['age']]);
// take lastinsertid and make a new header Location with it
$id = $this->db->lastInsertId();
// return a JSON saying all ok and new location
return $res->withJson(["person"=>$data],201)->withAddedHeader('Location',"/users/$id", ["text"=>"person added correctly"]);
}
catch(PDOException $e) {
return $res->withJson(["person"=>$data],404)->withAddedHeader('Location',"/users/$id", ["text"=>"person does not exist"]);
}
});
$res = filter_var_array($data,$filter); returns you array. The array type does not have method withJson(). This is the problem. Probably, you want to use json_encode() function.
Ok then do 2 things:
#1. change:
$res = filter_var_array($data,$filter);
to
$params = filter_var_array($data,$filter);
#2. change:
$stmt->execute(['fname'=>$res['fname'],'lname'=>$res['lname'],'address'=>$res['address'],'email'=>$res['email'],'age'=>$res['age']]);
to
$stmt->execute([
'fname' => $params['fname'],
'lname' => $params['lname'],
'address' => $params['address'],
'email' => $params['email'],
'age' => $params['age']
]);

Categories