Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

aedart's avatar

Getter Setter Trait-package proposal [Not Laravel specific]

Background

Having worked with PHP for many years, I often felt frustrated when I had to copy & paste getters and setters, just so I could provide the same functionality to certain objects. Lucky PHP's traits has changed that a lot.

Nevertheless, I still often find my self in situations, where I need to provide some of the same getters and setters, throughout my data model objects. For instance, this could be getters and setters for;

  • id
  • Uuid
  • name
  • description
  • create date
  • update date
  • deleted date
  • path to file
  • path to picture
  • url
  • width
  • height
  • ...etc

Naturally, I am developing traits for these, so that I do not have to copy & paste code. And this is where I got an idea, to develop a package for such very general getters and setters - one that could grow over time

Proposal

My idea is to start developing a package, which consists of interfaces and traits for general purpose getters and setters. These should aim at making model data objects faster to create, provided that they are that general. The following sections briefly illustrates how a sup-package could look like

The Interfaces - I am aware of something

/**
 * PHPDoc not shown...
 */
interface IUuidAware {
    // The setter
    public function setUuid($id);

    // The getter
    public function getUuid();

    // A default value - if any
    public function getDefaultUuid();

    // A validator
    public function isUuidValid($id);
}

Every interface would be comprised of the same methods; a setter, a getter, a default value getter and a validator. This should allow for a high degree of flexibility

The Traits - Default implementation of interfaces

/**
 * Not all implementation shown
 */
trait UuidTrait {

    // UUID property
    protected $uuid = null;

    // The setter
    public function setUuid($id){
       if($this->isUuidValid($id)){
           $this->uuid = $id;
           return;
       }
       throw new InvalidUuidException();
    }

    // The getter
    public function getUuid(){
       if(is_null($this->uuid)){
              $this->setUuid($this->getDefaultUuid());
       }
       return $this->uuid;
    }

    // A default value - if any
    public function getDefaultUuid(){
       return null; // By default, should always return null - can be overridden!
    }

    // A validator
    public function isUuidValid($id){
       // ... not shown ...
    }
}

All traits would offer a basic implementation of a given interface. However, the get-default-value method should always be implemented with a return null, to ensure that unwanted default values are not set - concrete implementations should override this behaviour, if needed. Lastly, every trait should also have a corresponding unit test, which must ensure that it's implementation lives up to the promise of the interface.

Example of model data

/**
 * Example model data object
 */
class Person implements IUuidAware {

    use UuidTrait;

    public function getDefaultUuid(){
       return Uuid::uuid1(); // example, rhumsaa/uuid package
    }    
}

In the above minimalistic example, the default value method is overridden and a UUID is automatically generated, if none has been provided. This, however, might not always be needed / wanted. Furthermore, a Person class would most likely be comprised of several traits. Of course, a real Person-class should perhaps have its own dedicated interface, rather than implement several "I-am-aware-interfaces". This being said, the Persons interface could still take advantage of other / multiple smaller interfaces, each describing that common getter / setter functionality.

PHP's magic methods and a bit of PHPDoc magic

Using such traits could perhaps also allow to define some kind of automation-trait, which makes use of the __get() and __set() methods in PHP (http://php.net/manual/en/language.oop5.overloading.php), and automatically make use of the getter and setter method.

$person->uuid = '25769c6c-d34d-4bfe-ba98-e0ee856f3e7a';
echo $person->uuid; // Output the 25769c6c- ... etc

Also, using PHPDoc's "property", at class level, could enable code-hinting in most IDEs (http://www.phpdoc.org/docs/latest/references/phpdoc/tags/property.html).

But...

While the previously stated example(s) might work for me... I do not know how practicable it would be, for other developers. In addition to this, there are some questions I have yet to answer,... some of which perhaps YOU might share your thoughts upon

  • Is such a package really needed, could it be beneficial?
  • How does multiple interfaces and traits affect the performance of a class / object?
  • How would the classification of such sub-packages be - what data-definition / validation / structure standards are going to be followed, and why?

If you have thoughts about this, suggestions, ideas, please share them :)

0 likes
4 replies
RemiC's avatar

Sounds like something similar (in the intention at least) to doctrine's embedabbles. Did you heard of it ?

aedart's avatar

I haven't worked with embeddables, but it does resemble what I am trying to do. The exception is, however, I do wish to just keep it at object level, and separate it completely from database-specific-logic (provided I understand doctrine's version correctly), so that the traits can be as flexible and small as possible.

Doing so, I hope that its possible for developers to still use them and choose in what way / pattern they wish to use them in, e.g. Active Record or Object-Relational Mapper, etc. Sometimes, perhaps, there isn't even a need to persist data, but you just wish to have a temporary object, which can satisfy data-storage needs.

This being said, Doctrine's embedabbles do look interesting, thanks for the hint.

Vulfoliac's avatar

A little late to the ball game.... But

Just an Idea, but one would think that you could do this on your own in a base model. So for instance your base model could have a getCretedAtAttribute function/method that would effect all your sub models. And still gives you the ability to overwrite them on a as need bases.

aedart's avatar

@pmptech If you are thinking about Eloquent's Accessors & Mutators, then yes, absolutely! And you should also stick to using that format or standard.

However, at the time being, I was working on a large (and very complex) project, where I was using something similar to Data Transfer Objects, as a layer in between my business models and an external system (via XML serialisation). Those objects had a lot of common attribute names, e.g. user_id, product_id, name, description... etc. So, loving to reuse what I can, I decided to create small interfaces, e.g. IdAware, NameAware, ... and so on. Furthermore, each of those interfaces had a corresponding default trait implementation, so that I could get the most out of it. This resulted in a small OpenSource project, which we called Model.

Now, a bit of time has past, and we sadly ran into some development / maintenance issues - we started to re-develop something that wasn't needed; custom validators. Thus, the project has become hard to work with. Nevertheless, I do plan to revisit it as soon as I'm done with some other small projects, and do a version 2.x, that isn't split into several repositories (also hard to maintain), and without validation.

The idea of creating small interfaces and traits that define such small attributes / properties, is not intended for small projects; they tend to add a lot of complexity, which always ends up taking a lot of time. But, if you find yourself having to work with external systems (in large projects of course), and you need to guarantee that some data object must contain some specific data, then aiming for interfaced data objects, could be a good way to go. It really depends on your needs.

Please or to participate in this conversation.