Skip to content

Commit

Permalink
Merge branch 'feature/deviable-casts' into 8.x
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Oct 28, 2020
2 parents e86f58a + 8f35c06 commit 1a55e3c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Illuminate\Contracts\Database\Eloquent;

interface DeviatesCastableAttributes
{
/**
* Increment the attribute.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function increment($model, string $key, $value, array $attributes);

/**
* Decrement the attribute.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function decrement($model, string $key, $value, array $attributes);
}
30 changes: 30 additions & 0 deletions src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,21 @@ protected function getCastType($key)
return trim(strtolower($this->getCasts()[$key]));
}

/**
* Increment or decrement the given attribute using the custom cast class.
*
* @param string $method
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function deviateClassCastableAttribute($method, $key, $value)
{
return $this->resolveCasterClass($key)->{$method}(
$this, $key, $value, $this->attributes
);
}

/**
* Serialize the given attribute using the custom cast class.
*
Expand Down Expand Up @@ -1162,6 +1177,21 @@ protected function isClassCastable($key)
throw new InvalidCastException($this->getModel(), $key, $castType);
}

/**
* Determine if the key is deviable using a custom class.
*
* @param string $key
* @return bool
*
* @throws \Illuminate\Database\Eloquent\InvalidCastException
*/
protected function isClassDeviable($key)
{
return $this->isClassCastable($key) &&
method_exists($castType = $this->parseCasterClass($this->getCasts()[$key]), 'increment') &&
method_exists($castType, 'decrement');
}

/**
* Determine if the key is serializable using a custom class.
*
Expand Down
4 changes: 3 additions & 1 deletion src/Illuminate/Database/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,9 @@ protected function incrementOrDecrement($column, $amount, $extra, $method)
return $query->{$method}($column, $amount, $extra);
}

$this->{$column} = $this->{$column} + ($method === 'increment' ? $amount : $amount * -1);
$this->{$column} = $this->isClassDeviable($column)
? $this->deviateClassCastableAttribute($method, $column, $amount)
: $this->{$column} + ($method === 'increment' ? $amount : $amount * -1);

$this->forceFill($extra);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,30 @@
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Contracts\Database\Eloquent\DeviatesCastableAttributes;
use Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes;
use Illuminate\Database\Eloquent\InvalidCastException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;

/**
* @group integration
*/
class DatabaseEloquentModelCustomCastingTest extends DatabaseTestCase
{
protected function setUp(): void
{
parent::setUp();

Schema::create('test_eloquent_model_with_custom_casts', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->decimal('price');
});
}

public function testBasicCustomCasting()
{
$model = new TestEloquentModelWithCustomCast;
Expand Down Expand Up @@ -135,6 +149,21 @@ public function testGetOriginalWithCastValueObjects()
$this->assertNull($model->address);
}

public function testDeviableCasts()
{
$model = new TestEloquentModelWithCustomCast;
$model->price = '123.456';
$model->save();

$model->increment('price', '530.865');

$this->assertSame((new Decimal('654.321'))->getValue(), $model->price->getValue());

$model->decrement('price', '333.333');

$this->assertSame((new Decimal('320.988'))->getValue(), $model->price->getValue());
}

public function testSerializableCasts()
{
$model = new TestEloquentModelWithCustomCast;
Expand Down Expand Up @@ -326,7 +355,7 @@ public function set($model, $key, $value, $attributes)
}
}

class DecimalCaster implements CastsAttributes, SerializesCastableAttributes
class DecimalCaster implements CastsAttributes, DeviatesCastableAttributes, SerializesCastableAttributes
{
public function get($model, $key, $value, $attributes)
{
Expand All @@ -338,6 +367,16 @@ public function set($model, $key, $value, $attributes)
return (string) $value;
}

public function increment($model, $key, $value, $attributes)
{
return new Decimal($attributes[$key] + $value);
}

public function decrement($model, $key, $value, $attributes)
{
return new Decimal($attributes[$key] - $value);
}

public function serialize($model, $key, $value, $attributes)
{
return (string) $value;
Expand Down

0 comments on commit 1a55e3c

Please sign in to comment.