[Php] How to write getter/setter to access multi-level array by key names?


Answers

I have solution for you not in the pure PHP, but using ouzo goodies concretely Arrays::getNestedValue method:

$arr = array('a' => 1,
    'b' => array(
        'y' => 2,
        'x' => array('z' => 5, 'w' => 'abc')
    ),
    'c' => null);

$key = 'b.x.z';
$path = explode('.', $key);

print_r(Arrays::getNestedValue($arr, $path));

Similarly if you need to set nested value you can use Arrays::setNestedValue method.

$arr = array('a' => 1,
    'b' => array(
        'y' => 2,
        'x' => array('z' => 5, 'w' => 'abc')
    ),
    'c' => null);

Arrays::setNestedValue($arr, array('d', 'e', 'f'), 'value');
print_r($arr);
Question

I've to implement a setter in PHP, that allows me to specify the key, or sub key, of an array (the target), passing the name as a dot-separated-keys value.

Given the following code:

$arr = array('a' => 1,
             'b' => array(
                 'y' => 2,
                 'x' => array('z' => 5, 'w' => 'abc')
             ),
             'c' => null);

$key = 'b.x.z';
$path = explode('.', $key);

From the value of $key I want to reach the value 5 of $arr['b']['x']['z'].

Now.... given a variable value of $key and a different $arr value (with different deepness).

How can I set the value of the element pointed by $key?

For the getter get() I wrote this code:

public static function get($name, $default = null)
{
    $setting_path = explode('.', $name);
    $val = $this->settings;

    foreach ($setting_path as $key) {
        if(array_key_exists($key, $val)) {
            $val = $val[$key];
        } else {
            $val = $default;
            break;
        }
    }
    return $val;
}

To write a setter is more difficult because I succeed in reaching the right element (from the $key), but I am not able to set the value in the original array and I don't know how to specify the key all at once.

Should I use some kind of backtracking? Or can I avoid it?




Convert dot syntax like “this.that.other” to multi-dimensional array in PHP

Try this number...

function assignArrayByPath(&$arr, $path, $value, $separator='.') {
    $keys = explode($separator, $path);

    foreach ($keys as $key) {
        $arr = &$arr[$key];
    }

    $arr = $value;
}

CodePad

It will loop through the keys (delimited with . by default) to get to the final property, and then do assignment on the value.

If some of the keys aren't present, they're created.




Dynamically modify associative array values in php

Here is the dynamic set funciton, you can use it set any depth. Demo here for you question.

function set($settings, $target_directory, $new_value)
{
  $array = explode('.', $target_directory);
  $ref = &$settings;
  while($v = current($array))
  {
    $ref = &$ref[$v];
    next($array);
  }
  $ref = $new_value;
  return $settings;
}



//In your main function
public function something() {
    $settings = array(
     "age" => "25",
     "data" => array(
        "name" => "John Dewey",
        "zip_code" => "00000"
      )
    );

   $target = 'data.name';
   $input = 'Other Name'

  $new_arr = dont_know_what_to_do($target_dir, $input);

  print_r($new_arr);        
}


//Create new function
public function dont_know_what_to_do($settings, $target, $input) {
  $key = explode('.', $target)
  return $settings['data'][$key[1]] = $input;
}



<?php
    $string = 'router.format';
    $array = array(
        'router' => array(
            'format' => 'xml',
        ),
    );

    $string = explode('.', $string);
    $result = $array;

    for($i = 0; $i < count($string); ++$i)
    {  
        $key = $string[$i];
        if(isset($result[$key]))
            $result = $result[$key];
        else
            break;
    }

    print_r($result);



I would suggest using dflydev/dot-access-data.

If you're not familiar with using Composer, head over to https://getcomposer.org/ for an introduction so that you can download and autoload the package as as dependency for your project.

Once you have the package, you can load a multi-dimensional array into a Data object:

use Dflydev\DotAccessData\Data;

$data = new Data(array(
  's1' => array(
    't1' => array(
      'column' => array(
        '1' => 'size:33%',
      ),
    ),
  ),
);

And access the values using dot notation:

$size = $username = $data->get('s1.t1.column.1');