1

When using Eloquent in Laravel 4 or 5, the $model->with() methods uses the method as relation name. So if we get a user with his profile, that will return:

$user = [
  'id' => 1,
  'name' => 'Example',
  'profile' => [
    'last_login' => '10-10-1900',
    'another_field' => 'with some data',
    'actions' => [
      0 => 'Logged in',
      1 => 'Logged out',
      2 => 'Signed up for news letter'
    ]
  ]
];

I want to flatten the full array and use the key of the relation as prefix, so i.e.:

$user = [
  'id' => 1,
  'name' => 'Example',
  'profile_last_login' => '10-10-1900',
  'profile_another_field' => 'with some data',
  'profile_actions' => [
    0 => 'Logged in',
    1 => 'Logged out',
    2 => 'Signed up for news letter'
  ]
];

How can I achieve this?

Edit

I also want to flatten multi-dimensional arrays, like this:

$user = [
    'id' => 1,
    'name' => 'Example',
    'profile' => [
        'last_login' => '10-10-1900',
        'another_field' => 'with some data',
        'actions' => [
            0 => 'Logged in',
            1 => 'Logged out',
            2 => 'Signed up for news letter'
        ],
        'friends' => [
            'friend_one' => [
                'name' => 'Test User',
                'email' => 'test@example.com'
            ],
            'friend_two' => [
                'name' => 'Test User',
                'email' => 'test@example.com'
            ]
        ]
    ]
];

Into this:

$out = [
    'id' => 1,
    'name' => 'Example',
    'profile_last_login' => '10-10-1900',
    'profile_another_field' => 'with some data',
    'profile_actions' => [
        0 => 'Logged in',
        1 => 'Logged out',
        2 => 'Signed up for news letter'
    ],
    'profile_friends_friend_one_name' => 'Test User',
    'profile_friends_friend_one_email' => 'test@example.com',
    'profile_friends_friend_two_name' => 'Test User',
    'profile_friends_friend_two_email' => 'test@example.com'
];
Will
  • 24,082
  • 14
  • 97
  • 108
Donny van V
  • 921
  • 1
  • 10
  • 22

1 Answers1

0

Simple Approach for Original Question (Single Dimension)

function flattenParentKey($array, $parentKey)
{
    foreach ($array[$parentKey] as $key => $value)
    {
        $array[$parentKey . '_' . $key] = $value;
    }

    unset($array[$parentKey]);

    return $array;
}

Here's an example in the PHP Shell (php -a):

php > $user = [
php >   'id' => 1,
php >   'name' => 'Example',
php >   'profile' => [
php >     'last_login' => '10-10-1900',
php >     'another_field' => 'with some data',
php >     'actions' => [
php >       0 => 'Logged in',
php >       1 => 'Logged out',
php >       2 => 'Signed up for news letter'
php >     ]
php >   ]
php > ];
php > $user = flattenParentArray($user, 'profile');
php > var_dump($user);
array(5) {
  ["id"]=>
  int(1)
  ["name"]=>
  string(7) "Example"
  ["profile_last_login"]=>
  string(10) "10-10-1900"
  ["profile_another_field"]=>
  string(14) "with some data"
  ["profile_actions"]=>
  array(3) {
    [0]=>
    string(9) "Logged in"
    [1]=>
    string(10) "Logged out"
    [2]=>
    string(25) "Signed up for news letter"
  }
}
php >

Recursive Approach for Multi-Dimensional Arrays

As you asked in the comments, and I added to your original question, here's how we'd do this with a nested, multi-dimensional array:

$user = [
    'id' => 1,
    'name' => 'Example',
    'profile' => [
        'last_login' => '10-10-1900',
        'another_field' => 'with some data',
        'actions' => [
            0 => 'Logged in',
            1 => 'Logged out',
            2 => 'Signed up for news letter'
        ],
        'friends' => [
            'friend_one' => [
                'name' => 'Test User',
                'email' => 'test@example.com'
            ],
            'friend_two' => [
                'name' => 'Test User',
                'email' => 'test@example.com'
            ]
        ]
    ]
];

/* Returns true if the specified parameter is an array, is not empty, and is 
 * not a contiguous numeric array).
 */
function isAssociativeArray($array)
{
    if (empty($array) || !is_array($array))
    {
        return false;
    }

    /* Returns true if arrays keys are not numeric or contiguous. */
    return (bool) array_keys($array) !== range(0, count($array) - 1);
}

/* $parentKey will always be null when we first call this function. As
 * it recurses through levels of depth, $parentKey will be passed and
 * appended to.
 */
function flattenChildArrays($array, $parentKey = null)
{
    $outputArray = [];

    foreach ($array as $key => $value)
    {
        /* If we have a $parentKey, our new key will be "$parentKey_$key" */
        $newKey = (($parentKey) ? $parentKey . '_' . $key : $key);

        if (isAssociativeArray($value))
        {
            /* If our $value is an associative array, we'll want to recursively
             * call ourselves again on $value with our new key as the $parentKey.
             */
            $outputArray = array_merge($outputArray, flattenChildArrays($value, $newKey));
        }
        else
        {
            /* If $value is not an associative array (i.e. just a simple value, or a
             * contiguous numeric array, just use the value as it is in our output
             * array. If we have a parent key (because we're inside 1 or more levels
             * of recursion), use "$parentKey_$key" (see where we define $newKey above).
             */
            $outputArray[$newKey] = $value;
        }
    }

    return $outputArray;
}

And an example in the PHP shell:

php > $user = [
php >     'id' => 1,
php >     'name' => 'Example',
php >     'profile' => [
php >         'last_login' => '10-10-1900',
php >         'another_field' => 'with some data',
php >         'actions' => [
php >             0 => 'Logged in',
php >             1 => 'Logged out',
php >             2 => 'Signed up for news letter'
php >         ],
php >         'friends' => [
php >             'friend_one' => [
php >                 'name' => 'Test User',
php >                 'email' => 'test@example.com'
php >             ],
php >             'friend_two' => [
php >                 'name' => 'Test User',
php >                 'email' => 'test@example.com'
php >             ]
php >         ]
php >     ]
php > ];
php > var_dump(flattenChildArrays($user));
array(11) {
  ["id"]=>
  int(1)
  ["name"]=>
  string(7) "Example"
  ["profile_last_login"]=>
  string(10) "10-10-1900"
  ["profile_another_field"]=>
  string(14) "with some data"
  ["profile_actions_0"]=>
  string(9) "Logged in"
  ["profile_actions_1"]=>
  string(10) "Logged out"
  ["profile_actions_2"]=>
  string(25) "Signed up for news letter"
  ["profile_friends_friend_one_name"]=>
  string(9) "Test User"
  ["profile_friends_friend_one_email"]=>
  string(16) "test@example.com"
  ["profile_friends_friend_two_name"]=>
  string(9) "Test User"
  ["profile_friends_friend_two_email"]=>
  string(16) "test@example.com"
}
php >

Recursion can be a difficult topic to understand, as it requires you to follow the program through multiple levels in your head, and it can be easy to lose track of where you are. I recommend taking a simple example, and "stepping through" your recursive function, line by line, commenting what each of the values will be on each level of depth.

The answers to this question provide more information about understanding recursion.

Will
  • 24,082
  • 14
  • 97
  • 108
  • Can use this on a array with more then 10 levels of arrays? – Donny van V Jan 23 '16 at 23:32
  • I dont have a example, but the array changes because i use it on my eloquent data to flatten it all – Donny van V Jan 23 '16 at 23:37
  • I see. You can call it multiple times or in a [recursive](http://www.sitepoint.com/understanding-recursion/) function. – Will Jan 23 '16 at 23:49
  • And I'm assuming, in your original example, you wouldn't want to flatten `[profile_actions]`, right? – Will Jan 23 '16 at 23:50
  • Yeah exactly. Its just for the values that doesnt have numeric key values. Can you please help me out cause im messing with this for hours now... – Donny van V Jan 23 '16 at 23:52
  • 1
    Sure, I can help. I have to run a quick errand, but give me an hour or so :) – Will Jan 24 '16 at 00:00
  • No problem! Check out my edit. This should do what you want for deep, multi-dimensional arrays. I added an example to your original question of what this will do. – Will Jan 24 '16 at 01:46
  • It works pretty well but sometimes it throws the key number in front of the array key, so i.e.: 1_location_city, 2_location_city, do you have a idea how to fix that? – Donny van V Jan 24 '16 at 21:22
  • Oh weird, let me look! – Will Jan 25 '16 at 00:48
  • Is there a way you could `var_dump()` one of the arrays that's causing `1_location_city` and put it on pastebin.com or something (change anything private/sensitive) so i can debug a bit? – Will Jan 25 '16 at 00:54
  • Im sorry for the late response Will, i've been working a lot. I have 2 paste bins. 1 with JSON and 1 as a array. Please use a syntax highlighter to format it well: http://pastebin.com/Uhyxqwqc The array: http://pastebin.com/39ZAqRvv – Donny van V Jan 28 '16 at 19:06