I am working on an API which receives a PHP object of $POST data. I am trying to check wether the 'smsPhoneNumber' inside customFields exists but not sure how to do this.
I am currently able to check for 'email' using:
if ( property_exists( $data, 'email' ) ) {
return true;
}
Question: How to check if 'smsPhoneNumber' exists?
--
var_dump:
object(stdClass)[1515]
public 'email' => string 'email#email.com'
public 'customFields' =>
array (size=2)
0 =>
object(stdClass)[1512]
public 'name' => string 'Firstname'
public 'value' => string 'james'
1 =>
object(stdClass)[1514]
public 'name' => string 'smsPhoneNumber'
public 'value' => string '077'
You could use an array_filter to get the custom field you want.
$phoneFields = array_filter($data->customFields, function($field) {
return $field->name === 'smsPhoneNumber';
});
This will only return objects in the array that have a name property equal to smsPhoneNumber.
if (!count($phoneFields)) {
// Phone not found
}
// or
if ($phone = current($phoneFields)) {
echo "The first phone number found is " . $phone->value;
}
The drawback of using array_filter() to search for the subarray values is:
array_filter() will not stop once it finds a match; it will keep iterating even after a match is found until it reaches the end of the array.
You should use a technique that allows an early break/return.
I recommend a simple foreach() with a break.
$foundIndex = null;
foreach ($data->customFields as $index => $customFields) {
if ($customFields->name === 'smsPhoneNumber') {
$foundIndex = $index;
// or $wasFound = true;
// or $smsNumber = $customFields->value;
break;
}
}
This will prove to be very efficient and easy to read/maintain.
Related
I am trying to access values in a nested array that I send to my API but when do something like $data['inputs']{0} it only gives me back the first letter of the key when what I want is the whole value of the value and not the key but if I try something like $data['inputs']['type'] it gives me an offset error I ament sure how to correctly access the values the way I need to
public function saveEdit(Request $request)
{
try {
$unsanitizedData = $request->all();
$data = [];
$fid = $unsanitizedData['formId'];
$data['form_name'] = json_encode($unsanitizedData['cred']['name']);
$data['org'] = json_encode($unsanitizedData['cred']['organization']);
$data['updated_by'] = json_encode($unsanitizedData['updatedBy']);
$data['user_id'] = json_encode($unsanitizedData['id']);
$data['activated'] = json_encode($unsanitizedData['cred']['activated']);
$data['inputs'] = json_encode($unsanitizedData['cred']['inputs']);
$pattren = array("[","]","'",'"',"/","\\");
$data = str_replace($pattren,'', $data);
foreach ($unsanitizedData as $st) {
admin_form::where('id', $fid)->update([
'form_name' => $data['form_name'],
'org' => $data['org'],
'updated_by' => $data['updated_by'],
'user_id' => $data['user_id'],
'activated' => $data['activated']
]);
foreach ($data['inputs'] as $input) {
admin_form_fields::where('form_id', $fid)->update([
'type' => $input,
'name' => $input
]);
}
}
$res['status'] = true;
$res['message'] = 'Success';
return response($res, 200);
} catch (\Illuminate\Database\QueryException $ex) {
$res['status'] = false;
$res['message'] = $ex->getMessage();
return response($res, 500);
}
}
I thought if I use a foreach loop inside another foreach loop it would work because its a nested array so loop through the main one and then through the nested one but that did also not work
Data Structure when I do a data dump:
array:6 [
"form_name" => "Testname",
"org" => "TestOrg",
"updated_by" => "test",
"user_id" => "29",
"activated" => "false",
"inputs" => "{type:number,name:Phone},{type:input,name:Name},{type:input,name:Address},{type:email,name:Email}"
]
In your case, $data['inputs'] is a JSON encoded string from which you have removed the [ and ] characters so when you try to access its first element it's the first char (since strings are kind of arrays of strings in PHP, they are really array of strings in C).
The problem is that you call json_encode() in the first place. If it's how you sanitize input, you're doing it wrong. Since you're using an ORM, there's no real need to manually sanitize the input. Just keep your input as sent by the client and perform all your operations then use them unsanitized in the QueryBuilder
As far as I can see, you just need to use json_decode($data['inputs']) since your array is in fact just a string :)
I am using PHP7.
I have an Object I am trying to parse:
$RECORD = {
'name' => 'Stephen Brad Taylor',
'address' => '432 Cranberry Hills, Pittsburg',
'phone' => '708 865 456',
'Account' => (Object Vendor/Entity/User) {
'email' => 'INeedThisEmail#pleaseHelp.com' // I want to access this property.
'id' => 34,
'accessible' => ['email', 'id]
}
}
I have an array which I am using to select certain fields from RECORD:
$fieldnames = [
'name',
'address',
'phone',
'Account["email"]'
];
I am trying to parse the fieldnames from RECORD as follows:
$data[]
foreach($fieldnames as $k => $fieldname) {
$data[k] = $RECORD->$fieldname
}
The method above works for the first-level attributes: name, address, and phone. However, email returns null.
I have tried the following below and none have worked:
$data[k] = RECORD->${$fieldname}
$propertyName = '$RECORD->$fieldname'
$data[k] = ${$propertyName}
Does anyone know a way to access an object's properties using a string from an object reference?
Much gratitude <3
You can't use Account["email"] directly as a property accessor, it won't be split up to find the nested property. You need to parse it yourself.
foreach($fieldnames as $k => $fieldname) {
if (preg_match('/^(.*)\["(.*)"\]$/', $fieldname, $match) {
$data[$k] = $RECORD->{$match[1]}->{$match[2]};
} else {
$data[$k] = $RECORD->$fieldname;
}
}
Also, you need $ in $k.
This code only works for 1 level deep. If you need to deal with arbitrary levels, you'll have to write a recursive procedure. See How to access and manipulate multi-dimensional array by key names / path? for examples of how to code this.
I use anonymous functions for such cases. (PHP 7.4)
$index = fn($item) => $item->dotaItem->prop_def_index;
if (get_class($collection) === Collection::class) {
$index = fn($item) => $item->prop_def_index;
}
I would like to check if a key exist in one of the objects :
array (size=2)
0 =>
object(stdClass)[1631]
public 'label' => string 'Monsieur' (length=8)
public 'value' => string '1' (length=1)
public 'selected' => boolean true
1 =>
object(stdClass)[1633]
public 'label' => string 'Madame' (length=6)
public 'value' => string '2' (length=1)
In the example I have one array who contain two objects, the first one contain 'selected' key. I would like to return true if one of them contain 'selected' key. If the objects not contain 'selected' key I would like to return false.
I can have more than two objects. This is only for the example. Is there a function for this?
You can do this work using simple loop
$res = false;
foreach($arr as $item){
if (isset($item->selected))
$res = true;
}
Check result in demo
Note that if your array is large, you need to break loop when finding target key to prevent additional checking
Also you can do this work using array_filter()
$res = !!array_filter($arr, function($item){
return isset($item['selected']);
});
For a start, you don't have an array of arrays there, you have an array of objects.
Simply use property_exists to check if a key is in that object - however note, that it will return true even if value is null.
var_dump(property_exists($array[1], 'key'));
or if you want to test using arrays (docs):
var_dump(array_key_exists((array) $array[1], 'key'));
or as a function
function checkInArray($array, $key)
{
$found = array_filter($array, function($el)
{
return (property_exists((array) $el, $key));
}
return (!empty($found) ? true : false)
}
if (checkInArray($yourArray, 'selected')) {
# do something
}
Is there a function out there to make sure that any given array conforms to a particular structure? What I mean is that is has particular key names, perhaps particular types for values, and whatever nested structure.
Right now I have a place where I want to make sure that the array getting past has certain keys, a couple holding a certain data type, and one sub-array with particular key names. I've done a lot of run-around because I was passing malformed arrays to it, and finally I'm at the point where I have a bunch of
if ( ! isset($arr['key1']) ) { .... }
if ( ! isset($arr['key2']) ) { .... }
if ( ! isset($arr['key3']) ) { .... }
I would have saved a lot of time and consternation if I could have checked that the array conformed to a particular structure beforehand. Ideally something like
$arrModel = array(
'key1' => NULL ,
'key2' => int ,
'key3' => array(
'key1' => NULL ,
'key2' => NULL ,
),
);
if ( ! validate_array( $arrModel, $arrCandidate ) ) { ... }
So, the question I'm asking is, does this already exists, or do I write this myself?
Convert array to JSON:
http://us.php.net/manual/en/function.json-encode.php
Then check against a JSON Schema:
http://json-schema.org/
http://jsonschemaphpv.sourceforge.net/
It doesn't exist built in.
Maybe try something like (untested):
array_diff(array_merge_recursive($arrCandidate, $arrModel), $arrModel)
You can use the Symfony Validation component
Installation:
composer require symfony/validator doctrine/annotations
Usage:
$myArray = [
'name' => ['first_name' => 'foo', 'last_name' => 'bar'],
'email' => 'foo#email.com'
];
$constraints = new Assert\Collection([
'name' => new Assert\Collection([
'first_name' => new Assert\Length(['min' => 101]),
'last_name' => new Assert\Length(['min' => 1]),
]),
'email' => new Assert\Email(),
]);
$validator = Validation::createValidator();
$violations = $validator->validate($myArray, $constraints);
For more details, see How to Validate Raw Values (Scalar Values and Arrays)
accepted answer make diff based on values, since it's about array structure you dont want to diff values. Insted you should use [array_diff_key()][1]
Function alone is not recursive. It will not work out of box on sample array from question.
Edit after years: Got into similar trouble whit recursive array functions, so there is mine array_diff_recursive, this does not solve original question because it compare values as well, but i believe it can be easily modified and someone can find it useful .
function array_diff_recursive(array $array1, array $array2){
return array_replace_recursive(
array_diff_recursive_one_way($array1, $array2),
array_diff_recursive_one_way($array2, $array1)
);
}//end array_diff_recursive()
function array_diff_recursive_one_way(array $array1, array $array2)
{
$ret = array();
foreach ($array1 as $key => $value) {
if (array_key_exists($key, $array2) === TRUE) {
if (is_array($value) === TRUE) {
$recurse = array_diff_recursive_one_way($value, $array2[$key]);
if (count($recurse) > 0) {
$ret[$key] = $recurse;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
}
return $ret;
}//end array_diff_recursive_one_way()```
[1]: http://php.net/manual/en/function.array-diff-key.php
I know this is sort of an old post, sorry if my answer is not approppriate.
I'm in the process of writing a php package that does exactly what you are asking for, it's called Structure.
What you can do with the package is something like:
$arrayCheck = new \Structure\ArrayS();
$arrayCheck->setFormat(array("profile"=>"array"));
if ($arrayCheck->check($myArray)) {
//...
}
You can check it out here: http://github.com/3nr1c/structure
I came across a tool called Matchmaker on GitHub, which looks very comprehensive and has composer support and unit tests:
https://github.com/ptrofimov/matchmaker
You can include it into your project with
composer require ptrofimov/matchmaker.
I need to remove an element form a deeply nested array of unknown structure (i.e. I do not know what the key sequence would be to address the element in order to unset it). The element I am removing however does have a consistent structure (stdObject), so I can search the entire multidimensional array to find it, but then it must be removed. Thoughts on how to accomplish this?
EDIT: This is the function I have right now trying to achieve this.
function _subqueue_filter_reference(&$where)
{
foreach ($where as $key => $value) {
if (is_array($value))
{
foreach ($value as $filter_key => $filter)
{
if (isset($filter['field']) && is_string($filter['field']) && $filter['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($value[$filter_key]);
return TRUE;
}
}
return _subqueue_filter_reference($value);
}
}
return FALSE;
}
EDIT #2: Snipped of array structure from var_dump.
array (size=1)
1 =>
array (size=3)
'conditions' =>
array (size=5)
0 =>
array (size=3)
...
1 =>
array (size=3)
...
2 =>
array (size=3)
...
3 =>
array (size=3)
...
4 =>
array (size=3)
...
'args' =>
array (size=0)
empty
'type' => string 'AND' (length=3)
...so assuming that this entire structure is assigned to $array, the element I need to remove is $array[1]['conditions'][4] where that target is an array with three fields:
field
value
operator
...all of which are string values.
This is just a cursor problem.
function recursive_unset(&$array)
{
foreach ($array as $key => &$value) # See the added & here.
{
if(is_array($value))
{
if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($array[$key]);
}
recursive_unset($value);
}
}
}
Notes : you don't need to use is_string here, you can just make the comparison as you're comparing to a string and the value exists.
Don't use return unless you're sure there is only one occurrence of your value.
Edit :
Here is a complete example with an array similar to what you showed :
$test = array (
1 => array (
'conditions' =>
array (
0 => array ('field' => 'dont_care1', 'value' => 'test', 'operator' => 'whatever'),
1 => array ('field' => 'dont_care2', 'value' => 'test', 'operator' => 'whatever'),
2 => array ('field' => 'nodequeue_nodes_node__nodequeue_subqueue.reference', 'value' => 'test', 'operator' => 'whatever'),
3 => array ('field' => 'dont_care3', 'value' => 'test', 'operator' => 'whatever')
),
'args' => array (),
'type' => 'AND'
));
var_dump($test);
function recursive_unset(&$array)
{
foreach ($array as $key => &$value)
{
if(is_array($value))
{
if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($array[$key]);
}
recursive_unset($value);
}
}
}
recursive_unset($test);
var_dump($test);
One way to solve this was to extend your recursive function with a second parameter:
function _subqueue_filter_reference(&$where, $keyPath = array())
You'd still do the initial call the same way, but the internal call to itself would be this:
return _subqueue_filter_reference($value, array_merge($keyPath, array($key)));
This would provide you with the full path of keys to reach the current part of the array in the $keyPath variable. You can then use this in your unset. If you're feeling really dirty, you might even use eval for this as a valid shortcut, since the source of the input you'd give it would be fully within your control.
Edit: On another note, it may not be a good idea to delete items from the array while you're looping over it. I'm not sure how a foreach compiles but if you get weird errors you may want to separate your finding logic from the deleting logic.
I have arrived at a solution that is a spin-off of the function found at http://www.php.net/manual/en/function.array-search.php#79535 (array_search documentation).
Code:
function _subqueue_filter_reference($haystack,&$tree=array(),$index="")
{
// dpm($haystack);
if (is_array($haystack))
{
$result = array();
if (count($tree)==0)
{
$tree = array() + $haystack;
}
foreach($haystack as $k=>$current)
{
if (is_array($current))
{
if (isset($current['field']) && is_string($current['field']) && $current['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
eval("unset(\$tree{$index}[{$k}]);"); // unset all elements = empty array
}
_subqueue_filter_reference($current,$tree,$index."[$k]");
}
}
}
return $tree;
}
I hate having to use eval as it SCREAMS of a giant, gaping security hole, but it's pretty secure and the values being called in eval are generated explicitly by Drupal core and Views. I'm okay with using it for now.
Anyway, when I return the tree I simply replace the old array with the newly returned tree array. Works like a charm.