Skip to content

Instantly share code, notes, and snippets.

@scrubmx
Last active March 12, 2024 02:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scrubmx/99f4af42fd66449a8f12013c7a663a12 to your computer and use it in GitHub Desktop.
Save scrubmx/99f4af42fd66449a8f12013c7a663a12 to your computer and use it in GitHub Desktop.
<?php
namespace App\Models\Traits;
use LogicException;
trait Immutables
{
/**
* Set a given attribute on the model.
*
* @override \Illuminate\Database\Eloquent\Model
*
* @param string $key
* @param mixed $value
* @return $this
*
* @throws \LogicException
*/
public function setAttribute($key, $value)
{
if (! property_exists($this, 'immutables')) {
throw new LogicException('Class must define an $immutables array property.');
}
// First we will check for the presence of the attribute in the immutables
// array property if found then we'll check if a value has already been
// set in the model attributes. If both conditions are met just bail.
if (in_array($key, $this->immutables) && isset($this->attributes[$key])) {
return $this;
}
return parent::setAttribute($key, $value);
}
}
<?php
namespace Tests\Unit\Models\Traits;
use App\Models\Traits\Immutables;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use LogicException;
use Tests\TestCase;
class ImmutablesTest extends TestCase
{
/** @test */
public function it_throws_an_exception_if_immutables_property_is_missing()
{
$this->expectException(LogicException::class);
$model = new class extends Model {
use Immutables;
};
$model->year = '2018-01-01 00:00:00';
}
/** @test */
public function it_does_not_allow_to_change_a_property_after_it_is_been_set()
{
$model = new class extends Model {
use Immutables;
protected $immutables = ['validated_at'];
};
$this->assertNull($model->validated_at);
$model->validated_at = '2018-01-01 00:00:00';
$this->assertEquals('2018-01-01 00:00:00', $model->validated_at);
$model->validated_at = null;
$this->assertNotNull($model->validated_at);
$this->assertEquals('2018-01-01 00:00:00', $model->validated_at);
}
/** @test */
public function it_works_well_with_attribute_mutators()
{
$model = new class extends Model {
use Immutables;
protected $immutables = ['validated_at'];
public function setValidatedAtAttribute($value)
{
$this->attributes['validated_at'] = Carbon::parse($value)->addYear();
}
};
$this->assertNull($model->validated_at);
$model->validated_at = '2018-01-01 00:00:00';
$this->assertEquals('2019-01-01 00:00:00', $model->validated_at);
$model->validated_at = null;
$this->assertEquals('2019-01-01 00:00:00', $model->validated_at);
}
}
<?php
namespace App\Models;
class Subscription extends Model
{
use Traits\Immutables;
/**
* The attributes that should not be mutated once they are set.
*
* @var array
*/
protected $immutables = ['started_at'];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment