I'm always trying to make the patterns I use more functional without buying into functionality I might not need in some cases, and a good example is the Repository pattern for data access.

A good Repository has the ability to query for specific objects inside it. A bad Repository makes us sign up for functionality we don't need when we try to do that querying. We don't want to jump through hoops to do something that should be simple.

Here's a simple example Repository (cut of normal methods for brevity):

<?php

namespace Jorpo\Bullshit;

interface Repository
{
    public function query(Specification $specification);
}

The above is good and simple. It allows us to query based on a Specification that we can provide. What happens when we need to add sorting? A standard approach would be to add another parameter like so.

<?php

namespace Jorpo\Bullshit;

interface Repository
{
    public function query(Specification $specification, SortBy $sorting);
}

What if I don't need sorting though? I'm now stuck with it. This is a design smell as it will grow and grow.

You could just set it to accept null!
SortBy $sorting = null

In my opinion, it's bad design to accept null parameters in methods, especially if your requirements grow over time, so I avoid it like the plague; you're methods should accept only what it needs to do it's job, no more, no less (but this is a different conversation).

So how do we solve the problem above?

Decorators.

The most elegant solution is to use the decorator pattern. It's so obvious when you see it!

<?php

namespace Jorpo\Bullshit;

class SortedRepository implements Repository
{
    private Repository $repo;
    private SortBy $sorting;

    public function __construct(Repository $repo, SortBy $sorting)
    {
        $this->repo = $repo;
        $this->sorting = $sorting;
    }

    public function query(Specification $specification)
    {
        return $this->applySorting($this->repo->query($specification));
    }

    private applySorting(array $results)
    {
        // My sorting magic
    }
}

How simple is that?! Of course, you can easily add methods to switch the SortBy implementation at runtime, but generally in PHP we would know that kind of information up front in the request response cycle and be OK with adding it via a constructor.

This can be applied to other things, like pagination also, just wrap the SortedRepository in a PagedRepository instance for example.

<?php

namespace Jorpo\Bullshit;

class PagedRepository implements Repository
{
    private Repository $repo;
    private Pagination $pagination;

    public function __construct(Repository $repo, Pagination $pagination)
    {
        $this->repo = $repo;
        $this->pagination = $pagination;
    }

    public function query(Specification $specification)
    {
        return $this->applyPaging($this->repo->query($specification));
    }

    private applyPaging(array $results)
    {
        // My paging magic
    }
}

Using Decorators to wrap our classes allows us to decide at runtime if we need the functionality. It keeps our base implementations clean and injectable, giving us some simple classes that we can wrap those implementations with when we know we need to sort or paginate, or whatever we need to do.

What other things can you think about that this pattern would be good for? Let me know in the comments below.