Using Traits for Code Reuse in Zend Framework 2

Using Traits in Zend Framework 2

This is a post I’ve been meaning to write for a feels weeks now after I first started using Traits for simple reuse, as it solved a need I had at the time. After a while it seemed to be not too bad of a solution also.

What Are Traits?

If you’re not familiar with Traits the PHP manual describes them as:

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Why Use Traits?

But I digress. Here’s the situation which prompted the use of them, in a nutshell. I had a custom view helper which performed some rather elementary date & time formatting, based purely on US standards. When I first wrote the ViewHelper, I wasn’t aware of any other use case I’d have for it. So it made sense for it to be self-contained.

Such is life however, as later in development the need did arise to do more date/time formatting. But this time, far removed from the view layer in a model.

As I viewed it at the time, there was no clear path to deriving both classes from a common ancestor; none which made sense anyway.

What’s more, whilst OOP is my paradigm of choice, I don’t have a religious affinity with it nor any other specific approach; I view that as healthy.

So I weighed up my options and chose to go with Traits. I’ll be honest, there was the new & cool factor to them as well – as well as an irresistible sense of simplicity in them.

See, whilst Traits have been in PHP for a while now (since version 5.4.0), I was quite a dinosaur regarding them. For some time, I’ve kept using the same old approaches repeatedly. So Traits were something, to me untried, and well worth exploring.

What Was Implemented?

That’s the background. Here’s what was implemented. The formatting-specific code and constants were refactored from the ViewHelper into a Trait and the Trait was then made available to both the ViewHelper and Model classes. In the snippets below, you can see the code in question and how it’s shared around.

trait FormatDateTime
{

protected $_timeFormat = 'g:i A';
protected $_dateFormat = 'n-j-Y';

public function formatTime($time, $format = "")
{
    if (!empty($time)) {
        $time = new \DateTime($time);
        $formattedDate = $time->format($this->_timeFormat);
        return ($formattedDate == "0:00 AM" || 
            $formattedDate == "12:00 AM") ? '': $formattedDate;
    }
}

public function formatDate($date, $format = "")
{
    if (!is_null($date)) {
        $date = new \DateTime(str_replace("-", "/", $date));
        return $date->format((!empty($format)) ? $format : $this->_dateFormat);
    }
}

}
use Application\Traits\FormatDateTime;
use Zend\View\Helper\AbstractHelper;

class ReadableDate extends AbstractHelper
{
  use FormatDateTime; // pull in the trait to help with reuse

  public function __invoke($date, $format=null)
  {
    return $this->formatDate($date, $format);
  }
}
use Zend\Filter\Word\CamelCaseToSeparator;
use Application\Traits\FormatDateTime;

class ContentQuickSearch
{
  use FormatDateTime; // pull in the trait to help with reuse
}

For me, this works quite well for a number of reasons.

These are:

  • Code need only be written once (DRY)
  • Code can be shared around as needed
  • Large inheritance chains can be avoided
  • It’s clear where the code came from
  • Simple to maintain and extend

Now these are my opinions. So I posed the question to the Zend Framework 2 group on Google+.

Asking about Traits in Zend Framework 2 on Google Plus

Asking about Traits in Zend Framework 2 on Google Plus

I’ve been considering the feedback for a while now. But in this case, I don’t see an issue with the approach I’ve taken. This isn’t to rebuke their feedback. Not in the slightest. I completely understand their perspectives and would likely have proffered the same, if the situation was reversed.

However, for the reasons listed above, I don’t see an issue with the code as it stands, for the reasons listed. To play Devil’s advocate just for a moment though:

  • What if the code’s required in yet more locations?
  • What if the logic becomes more complex, requiring formatting with multiple arguments for a range of formats?
  • What then?

As the first release is all but done, I don’t see these becoming issues – but you never know what’s around the corner.

Tell me what you think. Am I mad to have followed this approach? Have I thrown out sound development principles & conventional wisdom and created a maintenance nightmare for myself and future project maintainers? Would you have taken the same approach?

Suggest, talk, rantCOMMENT! The comments are open and waiting.

PHP