A few days ago I discovered I was copy/pasting the same few lines of code in a few places, and if you know me you know that I loathe copy/pasta code.

The problem I had to solve was simple. I had to display data in a variable number of columns. Seems simple enough, not anything anyone has not done before. Here is a simplified version of the code I was using:

<div class="container">
    @foreach($collection->chunk(ceil($collectionOfData->count() / $columnCount)) as $columnData)
        <div class="column">
            @foreach($columnData as $dataToDisplay)
                <p>{{ $dataToDisplay }}</p>
            @endforeach
        </div>
    @endforeach
</div>

Its ugly, but it gets the job done. Now I can deal with isolated ugly code, but when I have to re-use ugly code I start to have problems. I figured, I am re-using this code often enough that it might deserve its own macro. Here is what I came up with:

/**
 * Splits the collection into a number of pieces.
 *
 * @param int $pieces
 * @param bool $preserveKeys
 * @return Collection
 */
Collection::macro('divideInto', function(int $pieces, bool $preserveKeys = true): Collection
{
    /** @var Collection $this */
    return $this
        ->chunk(ceil($this->count() / $pieces))
        ->map(function(Collection $chunk) use ($preserveKeys) {
            return $preserveKeys ? $chunk : $chunk->values();
        });
});

This does more or less the same thing as what was described above, except I added some flexibility into the mix. Lets go through it line by line.

  • The first line (after the PHPDoc, of course) defines the divideInto() macro and the inputs it will use, $pieces and $preserveKeys.
  • The next is a simple PHPDoc that helps your IDE. Your IDE does not know the scope that $this will run in, so it simply says that "$this is a Collection".
  • The line that starts with ->chunk is pretty straightforward. It "divides" the collection into the number of pieces the user is requesting.
  • The last lines in the macro are just a tiny bit more complicated. If $preserveKeys has been set to false, it will reset all of the keys in all of the new collection chunks.

Here is a quick example of it being used in its most basic form:

Here is an example of it being used, telling it not to preserve the array keys:

So to wrap it all up, lets re-write that code from above with this new macro!

<div class="container">
    @foreach($collection->divideInto($columnCount) as $columnData)
        <div class="column">
            @foreach($columnData as $dataToDisplay)
                <p>{{ $dataToDisplay }}</p>
            @endforeach
        </div>
    @endforeach
</div>