Reputation: 4113
I'm trying to extend a trait. By that I mean create a trait that redefines a couple methods for a different trait.
This is my code (PHP 5.6 and Laravel 5.4):
namespace App\Traits;
use Illuminate\Database\Eloquent\Concerns\HasTimestamps;
trait ExtendHasTimestamps
{
use HasTimestamps {
HasTimestamps::setCreatedAt as parentSetCreatedAt;
HasTimestamps::setUpdatedAt as parentSetUpdatedAt;
}
public function setCreatedAt($value) {
if ($value !== '') return $this;
return $this->parentSetCreatedAt($value);
}
public function setUpdatedAt($value) {
if ($value !== '') return $this;
return $this->parentSetUpdatedAt($value);
}
}
The issue comes in when I use ExtendHasTimestamps
in a Model, it conflicts with HasTimestamps
because Eloquent\Model
has use HasTimestamps
. Before PHP 7, traits throw a fatal error if you try to define the same property twice. So since I'm defining $timestamps
both in HasTimestamps
and again through ExtendHasTimestamps
by virtue of use HasTimestamps
, it breaks.
In short:
trait HasTimestamps {
protected $timestamps = true; //problematic property
}
trait ExtendHasTimestamps {
use HasTimestamps;
// add validation then call HasTimestamps method
}
class Model {
use HasTimestamps;
}
class MyModel extends Model {
use ExtendHasTimestamps; //everything breaks
}
Is there a way to either convince PHP that it is really the same thing and it can stop conflicting with itself, or to inform the class I'm working with (an extension of Eloquent\Model
) to stop using HasTimestamps
in favor of my trait?
This question does not answer mine because I'm trying to use a trait to overwrite another trait rather than just adding methods to each class where I need to use this code.
Upvotes: 2
Views: 1463
Reputation: 40861
It seems to me that you don't really need to extend the trait in the first place. I would simply override the methods in a trait used in your child class, then call the parent
methods within the trait.
Demo: https://3v4l.org/2DqZQ
<?php
trait HasTimestamps
{
protected $timestamps = true;
public function setCreatedAt($value)
{
echo 'HasTimestamps::setCreatedAt'.PHP_EOL;
return $this;
}
public function setUpdatedAt($value)
{
echo 'HasTimestamps::setUpdatedAt'.PHP_EOL;
return $this;
}
}
trait ExtendHasTimestamps
{
public function setCreatedAt($value)
{
echo 'ExtendHasTimestamps::setCreatedAt'.PHP_EOL;
if ($value !== '') return $this;
return parent::setCreatedAt($value);
}
public function setUpdatedAt($value)
{
echo 'ExtendHasTimestamps::setUpdatedAt'.PHP_EOL;
if ($value !== '') return $this;
return parent::setUpdatedAt($value);
}
}
class Model
{
use HasTimestamps;
}
class MyModel extends Model
{
use ExtendHasTimestamps;
}
(new MyModel)->setCreatedAt('');
echo PHP_EOL;
(new MyModel)->setCreatedAt('time');
ExtendHasTimestamps::setCreatedAt
HasTimestamps::setCreatedAtExtendHasTimestamps::setCreatedAt
Upvotes: 1