Skipping insert if array item is not found? - php

I am currently inserting data from json api url into my database, except some arrays are not completed filled out resulting in 'undefined index' errors.
Is there a good way to deal with this? Ofcourse checking each item index with an if isset statement and only setting it if it is available would work but that would be quite tedious for each entry.
foreach($items['response']['groups'] as $item) {
foreach($item['items'] as $item) {
Bar::firstOrCreate([
'lat' => $item['venue']['location']['lat'],
'long' => $item['venue']['location']['lng'],
'postalCode' => $item['venue']['location']['postalCode'],
'city' => $item['venue']['location']['city'],
'state' => $item['venue']['location']['state'],
'country' => $item['venue']['location']['country'],
'address' => $item['venue']['location']['address'],
'rating' => $item['venue']['rating'],
'website' => $item['venue']['url'],
]);
}
}

use simplest way like:
foreach($items['response']['groups'] as $item1) {
foreach($item1['items'] as $item) {
Bar::firstOrCreate([
'lat' => isset($item['venue']['location']['lat'])?$item['venue']['location']['lat'] : '',
'long' => isset($item['venue']['location']['lng'])?$item['venue']['location']['lng'] : '',
'postalCode' => isset($item['venue']['location']['postalCode'])?$item['venue']['location']['postalCode'] : '',
'city' => isset($item['venue']['location']['city'])?$item['venue']['location']['city'] : '',
'state' => isset($item['venue']['location']['state'])?$item['venue']['location']['state'] : '',
'country' => isset($item['venue']['location']['country'])?item['venue']['location']['country'] : '',
'address' => isset($item['venue']['location']['address'])?$item['venue']['location']['address'] : '',
'rating' => isset($item['venue']['rating'])?$item['venue']['rating'] : '',
'website' => isset($item['venue']['url'])?$item['venue']['url'] : '',
]);
}
}

Your problem here is that you rewrite value of $item on each iteration.
foreach($items['response']['groups'] as $item) {
foreach($item['items'] as $item) {
// after first iteration here `$item` doesn't have `items` key
}
Use different variables in every foreach.

Check each array for completeness and only Bar::firstOrCreate() if it's really complete:
foreach($items['response']['groups'] as $item) {
foreach($item['items'] as $item) {
if(
isset($item['venue']) &&
isset($item['venue']['location']) &&
isset($item['venue']['location']['lat']) &&
isset($item['venue']['location']['lng']) &&
isset($item['venue']['location']['postalCode']) &&
isset($item['venue']['location']['city']) &&
isset($item['venue']['location']['state']) &&
isset($item['venue']['location']['country']) &&
isset($item['venue']['location']['address']) &&
isset($item['venue']['location']['lng']) &&
isset($item['venue']['rating']) &&
isset($item['venue']['url'])
)
Bar::firstOrCreate([
'lat' => $item['venue']['location']['lat'],
'long' => $item['venue']['location']['lng'],
'postalCode' => $item['venue']['location']['postalCode'],
'city' => $item['venue']['location']['city'],
'state' => $item['venue']['location']['state'],
'country' => $item['venue']['location']['country'],
'address' => $item['venue']['location']['address'],
'rating' => $item['venue']['rating'],
'website' => $item['venue']['url'],
]);
}
}

you can use these simple steps
remove all the keys from nested arrays that contain no value, then
remove all the empty nested arrays.
$yourArray= array_map('array_filter', $yourArray);
$yourArray= array_filter( $yourArray);

You will have to check the existence of an index somewhere ! If you don't want to do it in the controller, you could have an entity like which checks every field :
<?php
class MyEntity
{
private $lat;
private $long;
private $postalcode;
...
public static function createInstanceFromArray(array $item)
{
if (isset($item['lat']) {
$this->setLat($item['lat']);
}
... and so on
// you could also do it automatic :
foreach($item as $key => $value) {
$methodName = 'set' . ucfirst($key);
if (method_exists($this, $methodName)) {
$this->{$methodName}($value);
}
}
}
public function setLat($lat)
{
$this->lat = $lat;
return $this;
}
public function getLat()
{
return $this->lat;
}
}
Then you could do the following to your original code :
foreach($items['response']['groups'] as $item) {
foreach($item['items'] as $subItem) {
$myEntity = MyNamespace\MyEntity::cretaInstanceFromArray($subItem['venue']);
}
}
Then you could set the fields of your entity with your methods :
'lat' => $myEntity->getLat();
....
this way, even if the array field does not exists, the entity returns a default value.
If you don't want to use objects, you could use array_merge with default values to ensure you will not have undefindex indexes :
$item = array_merge([
'lat' => '',
'long' => '',
'postalCode' => '',
...
], $item['venue']);
And put that treatment in a function for an exemple.

Related

PHP - Set array key only if the value is not null

Is there a shorter solution for something like this?
$manufacturer = array();
if(!is_null($params->get('name'))){
$manufacturer['name'] = $params->get('name');
}
if(!is_null($params->get('link'))){
$manufacturer['link'] = $params->get('link');
}
if(!is_null($params->get('description'))){
$manufacturer['description'] = $params->get('description');
}
...
So a key of an array should only be set with the value if the value is not null. This is a bit shorter, but with this solution the keys will exist with the value NULL. But they should not even exist when the value was NULL:
$manufacturer = array(
'name' => !is_null($params->get('name')) ? $params->get('name') : null,
'link' => !is_null($params->get('link')) ? $params->get('link') : null,
'description' => !is_null($params->get('description')) ? $params->get('description') : null
);
EDIT:
It should work for multidimensional arrays and the array keys and param keys may vary
for #u_mulder foreach and #Nono array_filter solutions they work only for simple array, they do not remove null values from multidimensional arrays,
try this recursive function:
<?php
/**
just keep your array like this:
$manufacturer = array(
'name' => $params->get('name'),
'link' => $params->get('link'),
'description' => $params->get('description'),
'attribute' => array (
'street' => $params->get('street'),
...
)
...
);
**/
$manufacturer = [
'name' => 'yoeunes',
'link' => null,
'description' => 'fake description',
'attribute' => [
'street' => null,
'city' => 'Marrakech',
],
];
function array_remove_null($array)
{
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = array_remove_null($array[$key]);
}
if (is_null($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
echo "<pre>";
print_r(array_remove_null($manufacturer));
output:
Array
(
[name] => yoeunes
[description] => fake description
[attribute] => Array
(
[city] => Marrakech
)
)
$keys = ['name', 'link', ....];
foreach ($keys as $key) {
if(!is_null($params->get($key))){
$manufacturer[$key] = $params->get($key);
}
}
The foreach solutions are possible, but since the array key and params key may vary and because I have another array with values within this array, this is maybe a better solution, or what do you think?
$manufacturer = array(
'name' => !is_null($params->get('name')) ? $params->get('name') : false,
'link' => !is_null($params->get('link')) ? $params->get('link') : false,
'description' => !is_null($params->get('description')) ? $params->get('description') : false,
'attribute' => array (
'street' => !is_null($params->get('street')) ? $params->get('street') : false,
...
)
...
);
$manufacturer = array_filter($manufacturer);
array_filter (without callback) will remove all keys with false. (It's possible as long I don't have boolean values for my keys. If so you can do the same with NULL and then use a callback for array_filter)

Add and modify value in a deep nested mixed array

Let's say you have an array like this:
$list = array(
'name' => 'foobar',
'id' => '12302',
'group' => array(array(
'name' => 'teamA',
'members' => array(
array(
'ID' => 'OAHSJLASJ8888'
'name' => 'eric',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'OKZ8JJLJYYH6'
'name' => 'franz',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'OKOIYHJKKK'
'name' => 'Amr',
'fname' => 'ok',
'age' => '13'
)
)
),
array(
'name' => 'teamB',
'members' => array(
array(
'ID' => 'FGZ9ILKA'
'name' => 'Evan',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'KMLML2039KKK'
'name' => 'Michel',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'AAA2039KKK'
'name' => 'Nickr',
'fname' => 'ok',
'age' => '13'
)
)
)
)
);
You want to add a value to the associative array named Amr which is the third element of the member key of the group key $list[group][0][members][2][newKey] = B
Using recursive function and foreach, I'm able to find anything I'm aiming at. Using array_walk_recursive I can also find the targeted key value and modify it.
Using RecursiveIteratorIterator and foreach, I can also find the element and modify it's value.
My issue is that I can not replace the modified object within the tree. I can follow the path down, but I'm not able to climb the tree back. I could maintain a index of each array I traverse and then recalculate the path to the key, but it looks culprit to me.
I can not modify the data structure, the dataset I have is as is.
Thanks for any help you could bring.
Code for Iterator:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($list));
foreach($iterator as $key=>$value) {
if ($key === 'ID') {
$metas = get_relatedmeta_objects($value),true));
//metas key should be added to the current array
}
}
Recursive method:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as &$key => $value ) {
if ($key === "ID") {
$metas = get_relatedmeta_objects($value);
//metas key should be added to the current array
} else if (is_array($value)) {
searchObject($value);
}
}
}
}
array_walk_recursive method:
function alterArray(&$item, $key, &$parentRec) {
if (is_array($item) || is_object($item)) {
searchObject($item);
}
if ($key === 'ID') {
$parentRec = json_decode(json_encode($parentRec), true);
$parentRec['metas'] = get_field_objects($item);
// the current array is modified but the value does not go back to the $list initial array.
}
}
function searchObject( &$element, &$parent) {
array_walk_recursive($element, 'alterArray', $element);
}
The data set could be anything. You do not know the key, you just know that some nested object can have ID key and when they do you want to add more content to this object.
The recursive function can do it, but you should use the & prefix on $value instead of $key:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as $key => &$value ) {
if ($key === "ID") {
$element['meta'] = get_relatedmeta_objects($value);
} else {
searchKeyAndAdd($value);
}
}
}
}
searchKeyAndAdd($list);
The other two methods offer no reference to the parent, although in the case of array_walk_recursive you tried it with the third argument, but there things get messy: to make it work on each recursive depth, you call array_walk_recursive recursively... but array_walk_recursive already visits all the key/value pairs recursively. So this will lead to many calls to alterArray with the same key/value, but with a different ancestor as third argument for each of them.
Furthermore, with this line:
$parentRec = json_decode(json_encode($parentRec), true);
... you lose the reference to the original $parentRec, and so any modification you make to $parentRec will no longer have an effect on the original array.

PHP - Get key from another key value through a multi dimensional array

I have the following multidimensional array that is obtained through an API.
$exchangeID = array(
0 => array(
'id' => 'vcxz',
'currency' => 'GBP',
),
1 => array(
'id' => 'mnbv',
'currency' => 'EUR',
),
2 => array(
'id' => 'lkjh',
'currency' => 'USD',
),
3 => array(
'id' => 'poiuy',
'currency' => 'KRN',
),
);
I would like to obtain the id of USD which is lkjh. I know this can be obtained by simply doing $exchangeID[2]['id']. The problem is that the array is dynamic. For example when it is loaded the first subarray may be EUR instead of GBP, and the third subarray may be KRN instead of USD.
Basically, what I have in mind is to look for the subarray where there is the currency first, then accordingly find the corresponding id. E.g. if I want to find EUR. First I find the EUR, then get 'mnbv'.
I tried this $key = array_search('USD', array_column($exchangeID, 'currency')); but I got the following error in my error_log PHP Fatal error: Call to undefined function array_column() to get at least the array number e.g. in this case 2.
You can simply filter your array, like this:
$usd_exchanges = array_filter($exchangeID, function($row) {
return $row['currency'] == "USD";
}));
var_dump($usd_exchanges[0]);
You can also return the first element of the filter, using the current method:
$usd_exchange = current(array_filter($exchangeID, function($row) {
return $row['currency'] == "USD";
})));
var_dump($usd_exchange);
Try this:
foreach ($exchangeID as $key => $arr)
if($arr['currency'] == 'USD')
echo $key;
It is possible to use following custom function to get the key:
echo getKeyRecursive('USD', $exchangeID);
function getKeyRecursive($needle, $haystack, $strict = false)
{
foreach ($haystack as $key => $item){
if(($strict ? $item === $needle : $item == $needle) || (is_array($item) && getKeyRecursive($needle, $item, $strict))) return $key;
}
return false;
}
foreach($exchangeID as $key=>$value)
{
if($exchangeID[$key]['currency']=='USD'){
$usdId=$exchangeID[$key]['id'];
break;
}
}
//echo $usdId;
//Result:ikjh
<?php
$exchangeID = array(
0 => array(
'id' => 'vcxz',
'currency' => 'GBP',
),
1 => array(
'id' => 'mnbv',
'currency' => 'EUR',
),
2 => array(
'id' => 'lkjh',
'currency' => 'USD',
),
3 => array(
'id' => 'poiuy',
'currency' => 'KRN',
),
);
$db=new PDO('sqlite::memory:','','',array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$db->query('CREATE TABLE `troll` (`dbid` TEXT, `dbcurrency` TEXT);');
$stm=$db->prepare("INSERT INTO `troll` (`dbid`,`dbcurrency`) VALUES(:id,:currency);");
array_walk($exchangeID,function($arr)use($stm){$stm->execute($arr);});
$res=$db->query("SELECT `dbid` AS `id` FROM `troll` WHERE `dbcurrency` = ".$db->quote('USD').' LIMIT 1');
$id=$res->fetch(PDO::FETCH_ASSOC);
$id=$id['id'];
var_dump($id);
see working example here http://codepad.viper-7.com/1lg2Gj
Try this if this works:
foreach($exchangeID as $key => $val)
{
if(array_key_exists('currency', $val))
if($val['currency'] == 'USD'){
echo $val['id'];
echo $val['currency'];
elseif($val['currency'] == 'GBP'){a1
echo $val['id'];
echo $val['currency']);
elseif($val['currency'] == 'EUR'){
echo $val['id'];
echo $val['currency'];
}else{
echo $val['id'];
echo $val['currency'];
}
}
}

Cant seem to append to array in php

This simple script should theoretically check the form for errors and then print any errors it finds.
The formValidate function takes in a form array, each field in the form has a value which is validated. The field also has an errors key whose value is an empty array. I am trying to append any errors I find to the errors array of the particular field. I then return the form array when I am done.
I later print out all the errors for the fields. Unfortunately the errors never show up.
I have been breaking my head over this for many hours now and I cant for the life of me figure out why the form errors in my script are not appeding.
Any help would be greatly appreciated!
# get value from array
function array_get(array $array, $key, $default = null)
{
return (array_key_exists($key, $array)) ? $array[$key] : $default;
}
# get string value from array
function array_get_string(array $array, $key, $default = '', $trim = true)
{
$val = array_get($array, $key);
if (is_string($val)) {
return ($trim) ? trim($val) : $val;
}
return $default;
}
function validateForm($form)
{
// validateField each field
foreach ($form as $field)
{
foreach ($field['validation'] as $validate)
{
switch($validate)
{
case 'email':
if(!filter_var($field['value'], FILTER_VALIDATE_EMAIL)) {
$field['errors'][] = $field['value'] . ' is an invalid email address.';
}
break;
case 'number':
if(!preg_match('/^[0-9 ]+$/', $field['value'])) {
$field['errors'][] = $field['value'] . ' is an invalid number.';
}
break;
case 'alpha':
if(!preg_match('/^[a-zA-Z ]+$/', $field['value'])) {
$field['errors'][] = $field['value'] . ' contains invalid characters. This field only accepts letters and spaces.';
}
break;
}
}
}
return $form;
}
// $post = filter_input_array( INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS );
$post = $_POST;
$ajax = array_get_string($post, "request_method") == 'ajax';
# select data that needs validation
$form = array(
'fullname' => array(
'value' => array_get_string($post, "full-name"),
'validation' => array('alpha', 'required'),
'errors' => array(),
),
'emailaddress' => array(
'value' => array_get_string($post, "email-address"),
'validation' => array('email'),
'errors' => array(),
),
'activites' => array(
'value' => array_get_string($post, "activites"),
'validation' => array('default'),
'errors' => array(),
),
'country' => array(
'value' => array_get_string($post, "country"),
'validation' => array('default'),
'errors' => array(),
),
'contactpreference' => array(
'value' => array_get_string($post, "contact-preference"),
'validation' => array('default'),
'errors' => array(),
),
'message' => array(
'value' => array_get_string($post, "message"),
'validation' => array('alpha'),
'errors' => array(),
),
);
// validate the form
$form = validateForm($form);
foreach ($form as $field)
{
foreach ($field['errors'] as $error)
{
echo $error . '<br />';
}
}
foreach creates a copy of the array and works on it. If you need to make changes to the original array, pass it by reference like so:
foreach($form as &$field) {
...
}

PHP - How to remove empty entries of an array recursively?

I need to remove empty entries on multilevel arrays. For now I can remove entries with empty sub-arrays, but not empty arrays... confused, so do I... I think the code will help to explain better...
<?php
/**
*
* This function remove empty entries on arrays
* #param array $array
*/
function removeEmptysFromArray($array) {
$filtered = array_filter($array, 'removeEmptyItems');
return $filtered;
}
/**
*
* This is a Callback function to use in array_filter()
* #param array $item
*/
function removeEmptyItems($item) {
if (is_array($item)) {
return array_filter($item, 'removeEmptyItems');
}
if (!empty($item)) {
return true;
}
}
$raw = array(
'firstname' => 'Foo',
'lastname' => 'Bar',
'nickname' => '',
'birthdate' => array(
'day' => '',
'month' => '',
'year' => '',
),
'likes' => array(
'cars' => array('Subaru Impreza WRX STi', 'Mitsubishi Evo', 'Nissan GTR'),
'bikes' => array(),
),
);
print_r(removeEmptysFromArray($raw));
?>
Ok, this code will remove "nickname", "birthdate" but is not removing "bikes" that have an empty array.
My question is... How to remove the "bikes" entry?
Best Regards,
Sorry for my english...
Try this code:
<?php
function array_remove_empty($haystack)
{
foreach ($haystack as $key => $value) {
if (is_array($value)) {
$haystack[$key] = array_remove_empty($haystack[$key]);
}
if (empty($haystack[$key])) {
unset($haystack[$key]);
}
}
return $haystack;
}
$raw = array(
'firstname' => 'Foo',
'lastname' => 'Bar',
'nickname' => '',
'birthdate' => array(
'day' => '',
'month' => '',
'year' => '',
),
'likes' => array(
'cars' => array('Subaru Impreza WRX STi', 'Mitsubishi Evo', 'Nissan GTR'),
'bikes' => array(),
),
);
print_r(array_remove_empty($raw));
array_filter(explode('/', '/home/teste sdsd/ /'), 'trim');
//Result
[
1 => "home",
2 => "teste sdsd",
]
//-----------
array_filter(explode('/', '/home/teste sdsd/ /'), 'strlen')
//Result
[
1 => "home",
2 => "teste sdsd",
3 => " ",
]
I think this should solve your problem.
$retArray =array_filter($array, arrayFilter);
function arrayFilter($array) {
if(!empty($array)) {
return array_filter($array);
}
}
Here is my solution, it will remove exactly specified list of empty values recursively:
/**
* Remove elements from array by exact values list recursively
*
* #param array $haystack
* #param array $values
*
* #return array
*/
function array_remove_by_values(array $haystack, array $values)
{
foreach ($haystack as $key => $value) {
if (is_array($value)) {
$haystack[$key] = array_remove_by_values($haystack[$key], $values);
}
if (in_array($haystack[$key], $values, true)) {
unset($haystack[$key]);
}
}
return $haystack;
}
You can use it like this:
$clean = array_remove_by_values($raw, ['', null, []]);
Note, it removes empty sub arrays if you pass [] as one of values.
Recursively clear multidimensional array of empty'ish items:
final class ArrayCleaner
{
public static function clean(array $value): array
{
foreach ($value as $k => $v) {
if (\is_array($v)) {
$value[$k] = self::clean($v);
if (0 == \count($value[$k])) {
unset($value[$k]);
}
} elseif (empty($v)) {
unset($value[$k]);
}
}
return $value;
}
}
Unit test:
final class ArrayCleanerTest
{
public function testItCleans(): void
{
$input = [
'empty_string_to_remove' => '',
'empty_int_to_remove' => 0,
'empty_string_number_to_remove' => '0',
'value_to_keep' => 5,
'empty_array_to_remove' => [],
'empty_array_of_empty_arrays_to_remove' => [
'one' => [],
'two' => [],
'three' => [false, null, '0'],
],
'array_to_keep_1' => [
'value' => 1,
],
'array_to_keep_2' => [
'empty_to_remove' => [],
'array_to_keep' => ['yes'],
],
];
$expected = [
'value_to_keep' => 5,
'array_to_keep_1' => [
'value' => 1,
],
'array_to_keep_2' => [
'array_to_keep' => ['yes'],
],
];
$this->assertEquals($expected, ArrayCleaner::clean($input));
}
}
Working proof of concept at 3v4l.org
My function:
function removeEmptyItems($item)
{
if (is_array($item)) {
$item = array_filter($item, 'removeEmptyItems');
}
return !empty($item);
}
$nonEmpty = array_filter($raw, 'removeEmptyItems');
If you want array_filter to work recursively, you'll need to make sure that the subsequent calls may edit the deeper nested items of the array. Short: You'll need to pass it by reference:
function removeEmptyItems(&$item) {
if (is_array($item) && $item) {
$item = array_filter(&$item, 'removeEmptyItems');
}
return !!$item;
}

Categories