Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traits with own properties

Tags:

php

traits

The objective of PHP traits is to manage a bunch of logic. However what is the best way to make this bunch of logic works according some dedicated properties and avoiding naming conflicts ?

I'm thinking about MVC and particularly the model class. Indeed models seems to be good candidates for traits. Models can implement tree structure, be draftable, revisionnable, sluggable, and so on.

I would like to write something like this:

class MyModel extends Model {
    use Tree, Revision, Slug;
    protected $_behaviors = array(
        'Tree' => array('parentFieldname' => 'p_id'),
        'Revision' => array(/* some options */),
        'Slug' => array(/* some options */)
    );
    public function __construct() {
        foreach($this->_behaviors as &$options) {
            $options += /* trait defaults ? */
        }
    }
}

If I intend to set the Tree trait like this :

trait Tree {
    protected $_defaults = array(
        'parentFieldname' => 'parent_id',
        /*...other options...*/
    );
    public function moveUp();
    public function moveDown();
    public function setParent(); //<- need the `'parentFieldname' => 'p_id'`attribute
    /*...and so on...*/
}

I'll reach into naming conflicts because of $_defaults since each trait needs its own defaults. Using the name of the trait as property name imply using something like (new ReflectionClass(__CLASS__))->getTraits())... which is not really awesome.

In other words is there a way to create traits with "overridable defaults" and avoid naming conflicts ?

like image 765
Jails Avatar asked Jan 14 '23 13:01

Jails


1 Answers

Like you will do in every OOP-concept: Open your eyes! Thats exactly the same, as you extends a class and misuse already existing property. Nothing more.

class A {
  protected $defaults = array ('foo' => 'bar');
}
class B extends A {
  protected $defaults = array('foo2' => 42); // Bum: 'foo' disappeared
}

Having a generic $_defaults-property sounds to me like a code smell anyway: What "defaults"? This defaults? System defaults? Application defaults? [1] Setup your class with values, not "defaults", because thats (spreading default value) something for an initialization process, or the initialization of the concrete properties (public $parentFieldName = 'parent_id';)

trait A {
  public $parentFieldName = 'parent_id';
  public function construct ($options) { /
     if (isset($options['parentFieldName'])) {
       $this->parentFieldName = $options['parentFieldName'];
     }
  }
}
class Foo {
  use A {
    A::construct as protected constructA;
  }
  public function __construct ($options) {
    $this->constructA($options['A']);
  }
}

Some notes: It's important, that you alias construct() because it would conflict with other methods (from other traits) as well, and construct() is not a special method. It's only named this way (from me) to clarify, that it is "a kind of" constructor. Other names like init() or such would work as well of course ;) You must call it within the "real" constructor (see Foo::__construct()) yourself.

[1] "default" as an identifier is like "type", "status", "i", ...: They are to generic to be useful.

like image 178
KingCrunch Avatar answered Jan 19 '23 00:01

KingCrunch