How to Swap Items in an Array Using Laravel Macros
Tags: laravel, php, webdev
Published: 2021-04-26 21:09:29

## Introduction

There may be times when you are working with arrays in your <a href="https://www.php.net/" target="_blank" rel="noopener noreferrer">PHP</a> code and need to switch the order of two items. If you're using an array that uses integers as keys, this can be a pretty straightforward task. However, if you're trying to swap two items of an associative array, this can be little bit more hands-on. So, in this short post, I'll show you how you can use macros in <a href="https://laravel.com" target="_blank" rel="noopener noreferrer">Laravel</a> to keep your code looking clean and reusable.

By the end of the post, you should have a brief idea on how to create a macro that you can use to change the order of your like in this example:

```php
$assocArray = [
    'item_one'   => ['name' => 'One'],
    'item_two'   => ['name' => 'Two'],
    'item_three' => ['name' => 'Three'],
    'item_four'  => ['name' => 'Four'],
];

$newArray = Arr::swap($array, 'item_one', 'item_three');

/*
 * $newArray is now:
 * [
 *     'item_three' => ['name' => 'Three'],
 *     'item_two'   => ['name' => 'Two'],
 *     'item_one'   => ['name' => 'One'],
 *     'item_four'  => ['name' => 'Four'],
 * ]
 */
```

## What Are Macros?

In Laravel, macros are methods that can be added to classes at runtime. They can be a pretty cool way of extending the functionality of a class that you might not have full access to and might not want to inherit from. Not all classes are "macroable", but if you want a full list of the Laravel classes that you can add macros to, check out <a href="https://tighten.co/blog/the-magic-of-laravel-macros/" target="_blank" rel="noopener noreferrer">this article written by Caleb Porzio</a>.

## Where Do I Define The Macro?

Whenever we create a new macro, we'll need to let Laravel new that we're providing a new macro; and we can do this inside a <a href="https://laravel.com/docs/8.x/providers#introduction" target="_blank" rel="noopener noreferrer">service provider</a>. In theory, we can add this code to the ` AppServiceProvider ` but I like to try and keep the providers split out; otherwise, that file starts to get full pretty early on in your project. But this is just my personal opinion though, so feel free to use the ` AppServiceProvider ` if that helps you.

So, if you want to add the macro to a separate provider, let's start by creating a new ` MacroServiceProvider ` using the following command:

```
php artisan make:provider MacroServiceProvider
```

We'll then need to remember to register this service provider inside the ` app/config.php ` like below:

```php
return [
    
    'providers' => [
        // ...
        \App\Providers\MacroServiceProvider::class,
        // ...
    ],
	
]
```

Now that we've created the new service provider, let's create the macro. This is the macro that we're going to be adding:

```php
Arr::macro('swap', function (array $array, $keyOne, $keyTwo): array {
    if (! Arr::isAssoc($array)) {
        $itemOneTmp = $array[$keyOne];

        $array[$keyOne] = $array[$keyTwo];
        $array[$keyTwo] = $itemOneTmp;

        return $array;
    }

    $updatedArray = [];
    foreach ($array as $key => $value) {
        if ($key === $keyOne) {
            $updatedArray[$keyTwo] = $array[$keyTwo];
        } elseif ($key === $keyTwo) {
            $updatedArray[$keyOne] = $array[$keyOne];
        } else {
            $updatedArray[$key] = $value;
        }
    }

    return $updatedArray;
});
```

So, let's add this macro to the service provider so that we can access it:

```php
namespace App\Providers;

use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class MacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot(): void
    {
        Arr::macro('swap', function (array $array, $keyOne, $keyTwo): array {
            if (! Arr::isAssoc($array)) {
                $itemOneTmp = $array[$keyOne];

                $array[$keyOne] = $array[$keyTwo];
                $array[$keyTwo] = $itemOneTmp;

                return $array;
            }

            $updatedArray = [];
            foreach ($array as $key => $value) {
                if ($key === $keyOne) {
                    $updatedArray[$keyTwo] = $array[$keyTwo];
                } elseif ($key === $keyTwo) {
                    $updatedArray[$keyOne] = $array[$keyOne];
                } else {
                    $updatedArray[$key] = $value;
                }
            }

            return $updatedArray;
        });
    }
}
```

Now that we've defined the macro inside the service provider, we can swap items in arrays using the ` Arr::swap() ` method. Here are some examples below of how we would be able to use this method:

```php
$array = [
    ['name' => 'One'],
    ['name' => 'Two'],
    ['name' => 'Three'],
    ['name' => 'Four'],
];

$newArray = Arr::swap($array, 0, 2);

/*
 * $newArray is now:
 * [
 *     ['name' => 'Three'],
 *     ['name' => 'Two'],
 *     ['name' => 'One'],
 *     ['name' => 'Four'],
 * ]
 */
```
 
```php
$assocArray = [
    'item_one'   => ['name' => 'One'],
    'item_two'   => ['name' => 'Two'],
    'item_three' => ['name' => 'Three'],
    'item_four'  => ['name' => 'Four'],
];

$newArray = Arr::swap($array, 'item_one', 'item_three');

/*
 * $newArray is now:
 * [
 *     'item_three' => ['name' => 'Three'],
 *     'item_two'   => ['name' => 'Two'],
 *     'item_one'   => ['name' => 'One'],
 *     'item_four'  => ['name' => 'Four'],
 * ]
 */
```
 
Hopefully, this short post will have given you a very brief overview of how to quickly create a macro that we can use swap two items in a PHP array. And a pretty nice benefit is that we can reuse that macro method across our system and keep our code looking readable and Laravel-y.

I'd love to hear any feedback in the comments if this helped you or not! 🚀