├── .gitignore ├── .travis.yml ├── src ├── config.php ├── RevaluationServiceProvider.php ├── Revaluable.php ├── Valuators │ ├── RmbCent.php │ └── Valuator.php └── Traits │ └── HasRevaluableAttributes.php ├── tests ├── OrderWithCustomPrefix.php ├── OrderWithToArrayFalse.php ├── OrderWithReplaceToArray.php ├── Order.php ├── stubs │ └── migrations │ │ └── 2017_01_09_132402_create_orders_table.php └── HasRevaluableAttributesTest.php ├── phpunit.xml ├── .php_cs ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | .php_cs.cache -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | 7 | dist: trusty 8 | sudo: false 9 | 10 | install: travis_retry composer install --no-interaction --prefer-source 11 | 12 | script: vendor/bin/phpunit --verbose 13 | -------------------------------------------------------------------------------- /src/config.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | return [ 13 | 'default_valuator' => Overtrue\LaravelRevaluation\Valuators\Valuator::class, 14 | 15 | 'options' => [ 16 | 'rmb' => [ 17 | 'precision' => 2, 18 | 'currency_format' => '¥%i', 19 | ], 20 | ], 21 | ]; 22 | -------------------------------------------------------------------------------- /tests/OrderWithCustomPrefix.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Tests; 13 | 14 | /** 15 | * Class OrderWithCustomPrefix. 16 | * 17 | * @author overtrue 18 | */ 19 | class OrderWithCustomPrefix extends Order 20 | { 21 | protected $revaluatedAttributePrefix = 'display'; 22 | } 23 | -------------------------------------------------------------------------------- /tests/OrderWithToArrayFalse.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Tests; 13 | 14 | /** 15 | * Class OrderWithToArrayFalse. 16 | * 17 | * @author overtrue 18 | */ 19 | class OrderWithToArrayFalse extends Order 20 | { 21 | protected $appendRevaluatedAttributesToArray = false; 22 | } 23 | -------------------------------------------------------------------------------- /tests/OrderWithReplaceToArray.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Tests; 13 | 14 | /** 15 | * Class OrderWithReplaceToArray. 16 | * 17 | * @author overtrue 18 | */ 19 | class OrderWithReplaceToArray extends Order 20 | { 21 | protected $appendRevaluatedAttributesToArray = true; 22 | 23 | protected $replaceRawAttributesToArray = true; 24 | } 25 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | 6 | 7 | This source file is subject to the MIT license that is bundled 8 | with this source code in the file LICENSE. 9 | EOF; 10 | 11 | return PhpCsFixer\Config::create() 12 | ->setRiskyAllowed(true) 13 | ->setRules(array( 14 | '@Symfony' => true, 15 | 'header_comment' => array('header' => $header), 16 | 'array_syntax' => array('syntax' => 'short'), 17 | 'ordered_imports' => true, 18 | 'no_useless_else' => true, 19 | 'no_useless_return' => true, 20 | 'php_unit_construct' => true, 21 | 'php_unit_strict' => true, 22 | )) 23 | ->setFinder( 24 | PhpCsFixer\Finder::create() 25 | ->exclude('vendor') 26 | ->in(__DIR__) 27 | ) 28 | ; -------------------------------------------------------------------------------- /tests/Order.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Tests; 13 | 14 | use Illuminate\Database\Eloquent\Model; 15 | use Overtrue\LaravelRevaluation\Traits\HasRevaluableAttributes; 16 | 17 | /** 18 | * Class Order. 19 | * 20 | * @author overtrue 21 | */ 22 | class Order extends Model 23 | { 24 | use HasRevaluableAttributes; 25 | 26 | protected $replaceRawAttributesToArray = false; 27 | 28 | protected $table = 'orders'; 29 | 30 | protected $fillable = [ 31 | 'title', 'total', 'postage', 'paid_in', 32 | ]; 33 | 34 | protected $revaluable = [ 35 | 'total', 'postage', 'paid_in', 36 | ]; 37 | } 38 | -------------------------------------------------------------------------------- /src/RevaluationServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation; 13 | 14 | use Illuminate\Support\ServiceProvider; 15 | 16 | class RevaluationServiceProvider extends ServiceProvider 17 | { 18 | /** 19 | * Application bootstrap event. 20 | */ 21 | public function boot() 22 | { 23 | $this->publishes([__DIR__.'/config.php' => config_path('/revaluation.php')]); 24 | } 25 | 26 | /** 27 | * Register the service provider. 28 | */ 29 | public function register() 30 | { 31 | setlocale(LC_MONETARY, config('app.locale')); 32 | 33 | $this->mergeConfigFrom(__DIR__.'/config.php', 'revaluation'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Revaluable.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation; 13 | 14 | use JsonSerializable; 15 | 16 | /** 17 | * Interface Revaluable. 18 | */ 19 | interface Revaluable extends JsonSerializable 20 | { 21 | /** 22 | * Translate to the default format. 23 | * 24 | * @return mixed 25 | */ 26 | public function toDefaultFormat(); 27 | 28 | /** 29 | * Get raw value. 30 | * 31 | * @return mixed 32 | */ 33 | public function getRaw(); 34 | 35 | /** 36 | * Return storable value. 37 | * 38 | * @param mixed $value 39 | * 40 | * @return mixed 41 | */ 42 | public static function toStorableValue($value); 43 | } 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overtrue/laravel-revaluation", 3 | "description": "Laravel 5 model revaluation helper.", 4 | "require": { 5 | "laravel/framework": "5.8.*||^6.0||^7.0" 6 | }, 7 | "require-dev": { 8 | "phpunit/phpunit": "^8.0", 9 | "orchestra/testbench": "^3.4", 10 | "orchestra/database": "^4.3" 11 | }, 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "overtrue", 16 | "email": "i@overtrue.me" 17 | } 18 | ], 19 | "autoload": { 20 | "psr-4": { 21 | "Overtrue\\LaravelRevaluation\\": "src/" 22 | } 23 | }, 24 | "autoload-dev": { 25 | "psr-4": { 26 | "Overtrue\\LaravelRevaluation\\Tests\\": "tests" 27 | } 28 | }, 29 | "extra": { 30 | "laravel": { 31 | "providers": [ 32 | "Overtrue\\LaravelRevaluation\\RevaluationServiceProvider" 33 | ] 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Valuators/RmbCent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Valuators; 13 | 14 | /** 15 | * Class RmbCent. 16 | */ 17 | class RmbCent extends Valuator 18 | { 19 | public function toDefaultFormat() 20 | { 21 | return $this->inYuan(); 22 | } 23 | 24 | public function inYuan() 25 | { 26 | $precision = config('revaluation.options.rmb.precision', config('revaluation.options.rmb.pricision')); 27 | 28 | return number_format(round($this->value / 100, $precision), $precision, '.', ''); 29 | } 30 | 31 | public function asCurrency($format = null) 32 | { 33 | return money_format($format ?? config('revaluation.options.rmb.currency_format'), abs($this->inYuan())); 34 | } 35 | 36 | public static function toStorableValue($value) 37 | { 38 | return null === $value ? null : $value * 100; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Valuators/Valuator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Valuators; 13 | 14 | use Overtrue\LaravelRevaluation\Revaluable; 15 | 16 | /** 17 | * Class Valuator. 18 | */ 19 | class Valuator implements Revaluable 20 | { 21 | protected $value; 22 | 23 | public function __construct($value) 24 | { 25 | $this->value = $value; 26 | } 27 | 28 | public function jsonSerialize() 29 | { 30 | return $this->toDefaultFormat(); 31 | } 32 | 33 | public function getRaw() 34 | { 35 | return $this->value; 36 | } 37 | 38 | public function toDefaultFormat() 39 | { 40 | return $this->value; 41 | } 42 | 43 | public function __toString() 44 | { 45 | return $this->toDefaultFormat(); 46 | } 47 | 48 | public static function toStorableValue($value) 49 | { 50 | return $value; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 overtrue 4 | Copyright (c) 2016 Mohammed Isa 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /tests/stubs/migrations/2017_01_09_132402_create_orders_table.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | use Illuminate\Support\Facades\Schema; 13 | use Illuminate\Database\Schema\Blueprint; 14 | use Illuminate\Database\Migrations\Migration; 15 | 16 | class CreateOrdersTable extends Migration 17 | { 18 | /** 19 | * Run the migrations. 20 | */ 21 | public function up() 22 | { 23 | Schema::create('orders', function (Blueprint $table) { 24 | $table->increments('id'); 25 | $table->string('title'); 26 | $table->unsignedInteger('total'); 27 | $table->unsignedInteger('paid_in'); 28 | $table->unsignedInteger('postage'); 29 | $table->timestamps(); 30 | }); 31 | 32 | \DB::table('orders')->insert([ 33 | 'title' => 'order1', 34 | 'total' => 1234500, 35 | 'postage' => 1, 36 | 'paid_in' => 1234566, 37 | ]); 38 | } 39 | 40 | /** 41 | * Reverse the migrations. 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('orders'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/HasRevaluableAttributesTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Tests; 13 | 14 | use Orchestra\Testbench\TestCase; 15 | use Illuminate\Contracts\Console\Kernel as ConsoleKernel; 16 | use Overtrue\LaravelRevaluation\RevaluationServiceProvider; 17 | use Overtrue\LaravelRevaluation\Valuators\RmbCent; 18 | 19 | /** 20 | * Class HasRevaluableAttributesTest. 21 | * 22 | * @author overtrue 23 | */ 24 | class HasRevaluableAttributesTest extends TestCase 25 | { 26 | public function setUp() 27 | { 28 | parent::setUp(); 29 | $path = __DIR__.'/stubs/migrations'; 30 | $this->app->afterResolving('migrator', function ($migrator) use ($path) { 31 | $migrator->path($path); 32 | }); 33 | 34 | $this->artisan('migrate'); 35 | $this->app[ConsoleKernel::class]->setArtisan(null); 36 | } 37 | 38 | protected function getPackageProviders($app) 39 | { 40 | return [RevaluationServiceProvider::class]; 41 | } 42 | 43 | protected function getEnvironmentSetUp($app) 44 | { 45 | $app['config']->set('database.default', 'testing'); 46 | $app['config']->set('database.connections.testing', [ 47 | 'driver' => 'sqlite', 48 | 'database' => ':memory:', 49 | 'prefix' => '', 50 | ]); 51 | $app['config']['revaluation.default_valuator'] = RmbCent::class; 52 | } 53 | 54 | public function testBasicFeatures() 55 | { 56 | // database order 57 | $order = Order::find(1); 58 | 59 | $this->assertEquals(12345, $order->total); 60 | $this->assertEquals(1234500, $order->getOriginal('total')); 61 | 62 | // mutator getter 63 | $this->assertEquals(1234500, $order->raw_total); 64 | $this->assertInstanceOf(RmbCent::class, $order->revaluated_total); 65 | 66 | // new order 67 | $order = new Order([ 68 | 'total' => 100, 69 | 'title' => 'test order.', 70 | 'postage' => 20, 71 | 'paid_in' => 120, 72 | ]); 73 | $order->save(); 74 | 75 | $this->assertEquals(100, $order->total); 76 | $this->assertEquals(20, $order->postage); 77 | $this->assertEquals(120, $order->paid_in); 78 | 79 | // increment/decrement 80 | $order->increment('total', 100); 81 | $this->assertEquals(20000, $order->getOriginal('total')); 82 | $this->assertEquals(200, $order->total); 83 | 84 | $order->decrement('total', 100); 85 | $this->assertEquals(10000, $order->getOriginal('total')); 86 | $this->assertEquals(100, $order->total); 87 | 88 | // toArray 89 | $array = $order->toArray(); 90 | 91 | $this->assertArrayHasKey('revaluated_total', $array); 92 | $this->assertArrayHasKey('revaluated_postage', $array); 93 | $this->assertArrayHasKey('revaluated_paid_in', $array); 94 | 95 | $this->assertEquals(100, $array['revaluated_total']); 96 | $this->assertEquals(20, $array['revaluated_postage']); 97 | $this->assertEquals(120, $array['revaluated_paid_in']); 98 | 99 | $order->postage = -100; 100 | $this->assertEquals('¥100.00', $order->revaluated_postage->asCurrency()); 101 | } 102 | 103 | public function testCustomPrefix() 104 | { 105 | $order = OrderWithCustomPrefix::find(1); 106 | 107 | $this->assertInstanceOf(RmbCent::class, $order->display_total); 108 | 109 | $array = $order->toArray(); 110 | $this->assertArrayHasKey('display_total', $array); 111 | $this->assertEquals(12345, $array['display_total']); 112 | } 113 | 114 | public function testToArray() 115 | { 116 | $order = OrderWithToArrayFalse::find(1); 117 | 118 | $this->assertEquals(12345, $order->total); 119 | $this->assertInstanceOf(RmbCent::class, $order->revaluated_total); 120 | 121 | $array = $order->toArray(); 122 | $this->assertArrayNotHasKey('revaluated_total', $array); 123 | $this->assertEquals(1234500, $array['total']); 124 | $this->assertEquals(1234500, $order->raw_total); 125 | } 126 | 127 | public function testReplaceToArray() 128 | { 129 | $order = OrderWithReplaceToArray::find(1); 130 | 131 | $this->assertEquals(12345, $order->total); 132 | $this->assertInstanceOf(RmbCent::class, $order->revaluated_total); 133 | 134 | $array = $order->toArray(); 135 | $this->assertArrayNotHasKey('revaluated_total', $array); 136 | $this->assertEquals(12345, $array['total']); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Laravel Revaluation

2 | 3 |

Laravel 5 model revaluation helper.

4 | 5 |

6 | Build Status 7 | Latest Stable Version 8 | Latest Unstable Version 9 | Scrutinizer Code Quality 10 | Code Coverage 11 | Total Downloads 12 | License 13 |

14 | 15 | 16 | ## Installation 17 | 18 | You can install the package using composer 19 | 20 | ```sh 21 | $ composer require overtrue/laravel-revaluation -vvv 22 | ``` 23 | 24 | Then add the service provider to `config/app.php` 25 | 26 | ```php 27 | Overtrue\LaravelRevaluation\RevaluationServiceProvider::class, 28 | ``` 29 | 30 | Publish the config file: 31 | 32 | ```sh 33 | $ php artisan vendor:publish --provider='Overtrue\LaravelRevaluation\RevaluationServiceProvider' 34 | ``` 35 | 36 | Finally, use `Overtrue\LaravelRevaluation\Traits\HasRevaluableAttributes` in model. And specify which attributes in the `$revaluable` property can be revalued: 37 | 38 | ```php 39 | '\Foo\Support\Valuator\Foo', 56 | // 'bar' => '\Foo\Support\Valuator\Bar', 57 | // 'baz', // default valuator 58 | //]; 59 | 60 | //... 61 | } 62 | ``` 63 | 64 | ## Usage 65 | 66 | 67 | ### Basic usage with default options. 68 | 69 | ```php 70 | $order = Order::find(1); 71 | 72 | $order->total; // 345 (Db: 34500) 73 | $order->raw_total; // 34500 74 | 75 | $order->getRevaluatedTotalAttribute() or $order->revaluated_total; // Overtrue\LaravelRevaluation\Valuators\RmbCent 76 | $order->revaluated_total->inYuan(); // 345.00 77 | $order->revaluated_total->asCurrency(); // ¥345.00 78 | 79 | // automatic setter. 80 | $order->total = 123; 81 | $order->save(); 82 | 83 | $order->total; // 123 84 | $order->raw_total; // 12300 85 | $order->revaluated_total->asCurrency(); // ¥123.00 86 | 87 | // to array 88 | $order->toArray(); 89 | //[ 90 | // 'total' => 12300, 91 | // 'revaluated_total' => 123.0, 92 | //] 93 | ``` 94 | 95 | ### Custom revaluated attribute prefix 96 | 97 | ```php 98 | protected $revaluatedAttributePrefix = 'display'; 99 | 100 | $order->total; // 123.0; 101 | $order->raw_total; // 12300 102 | $order->display_total->asCurrency(); // ¥123.00 103 | 104 | // to array 105 | $order->toArray(); 106 | //[ 107 | // 'total' => 12300, 108 | // 'display_total' => 123.0, 109 | //] 110 | ``` 111 | 112 | ### Disable auto append revaluated attributes to array 113 | 114 | ```php 115 | protected $appendRevaluatedAttributesToArray = false; 116 | 117 | $order->total; // 123.0; 118 | $order->raw_total; // 12300 119 | $order->display_total->asCurrency(); // ¥123.00 120 | 121 | // to array 122 | $order->toArray(); 123 | //[ 124 | // 'total' => 12300, 125 | //] 126 | ``` 127 | 128 | ### Using revaluated value replace raw attributes value 129 | 130 | ```php 131 | protected $replaceRawAttributesToArray = true; 132 | 133 | $order->total; // 123.0; 134 | $order->raw_total; // 12300 135 | $order->display_total->asCurrency(); // ¥123.00 136 | 137 | // to array 138 | $order->toArray(); 139 | //[ 140 | // 'total' => 123.0, 141 | //] 142 | ``` 143 | 144 | More usage examples, Please refer to [unit testing](https://github.com/overtrue/laravel-revaluation/tree/master/tests) 145 | 146 | ## PHP 扩展包开发 147 | 148 | > 想知道如何从零开始构建 PHP 扩展包? 149 | > 150 | > 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package) 151 | 152 | ## License 153 | 154 | MIT 155 | -------------------------------------------------------------------------------- /src/Traits/HasRevaluableAttributes.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelRevaluation\Traits; 13 | 14 | /** 15 | * Trait HasRevaluableAttributes. 16 | */ 17 | trait HasRevaluableAttributes 18 | { 19 | /** 20 | * Revaluated attributes append to array. 21 | * 22 | * @var bool 23 | */ 24 | //protected $appendRevaluatedAttributesToArray = true; 25 | 26 | /** 27 | * @var bool 28 | */ 29 | //protected $replaceRawAttributesToArray = false; 30 | 31 | /** 32 | * Prefix of revaluated attribute getter. 33 | * 34 | *
 35 |      *      $model->revaluated_price;
 36 |      * 
37 | * 38 | * @var string 39 | */ 40 | //protected $revaluatedAttributePrefix = 'revaluated'; 41 | 42 | /** 43 | * Return valuator instance of attribute. 44 | * 45 | * @param string $attribute 46 | * 47 | * @return \Overtrue\LaravelRevaluation\Revaluable 48 | */ 49 | public function getRevaluatedAttribute($attribute) 50 | { 51 | $attribute = \Illuminate\Support\Str::snake($attribute); 52 | 53 | if ($valuator = $this->getAttributeValuator($attribute)) { 54 | return new $valuator(parent::getAttribute($attribute), $attribute, $this); 55 | } 56 | 57 | return false; 58 | } 59 | 60 | /** 61 | * Return revaluable attributes. 62 | * 63 | * @example 64 | * 65 | *
 66 |      * // 1. Using default valuator:
 67 |      * protected $revaluable = [
 68 |      *     'foo', 'bar', 'baz'
 69 |      * ];
 70 |      *
 71 |      * // 2. Use the specified valuator:
 72 |      * protected $revaluable = [
 73 |      *     'foo' => '\Foo\Support\Valuator\Foo',
 74 |      *     'bar' => '\Foo\Support\Valuator\Bar',
 75 |      *     'baz',
 76 |      * ];
 77 |      * 
78 | * 79 | * @return array 80 | */ 81 | public function getRevaluableAttributes() 82 | { 83 | if (!property_exists($this, 'revaluable') || !is_array($this->revaluable)) { 84 | return []; 85 | } 86 | 87 | $revaluable = []; 88 | 89 | foreach ($this->revaluable as $key => $valuator) { 90 | if (is_int($key)) { 91 | $revaluable[$valuator] = config('revaluation.default_valuator'); 92 | } else { 93 | $revaluable[$key] = $valuator; 94 | } 95 | } 96 | 97 | return $revaluable; 98 | } 99 | 100 | /** 101 | * @return string 102 | */ 103 | public function getRevaluableAttributePrefix() 104 | { 105 | return rtrim($this->revaluatedAttributePrefix ?? 'revaluated', '_'); 106 | } 107 | 108 | /** 109 | * @example 110 | *
111 |      * $object->revaluated_price;
112 |      * $object->raw_price;
113 |      * 
114 | * 115 | * @param string $attribute 116 | * 117 | * @return mixed 118 | * 119 | * @throws \Exception 120 | */ 121 | public function getAttribute($attribute) 122 | { 123 | if ($this->hasGetMutator($attribute)) { 124 | return parent::getAttribute($attribute); 125 | } 126 | 127 | if (\Illuminate\Support\Str::startsWith($attribute, 'raw_')) { 128 | return $this->getRevaluatedAttribute(substr($attribute, strlen('raw_')))->getRaw(); 129 | } 130 | 131 | $prefix = $this->getRevaluableAttributePrefix(); 132 | if (\Illuminate\Support\Str::startsWith($attribute, $prefix)) { 133 | return $this->getRevaluatedAttribute(substr($attribute, strlen($prefix) + 1)); 134 | } 135 | 136 | if ($valuator = $this->getRevaluatedAttribute($attribute)) { 137 | return $valuator->toDefaultFormat(); 138 | } 139 | 140 | return parent::getAttribute($attribute); 141 | } 142 | 143 | /** 144 | * Set attribute. 145 | * 146 | * @param string $attribute 147 | * @param mixed $value 148 | * 149 | * @return $this 150 | */ 151 | public function setAttribute($attribute, $value) 152 | { 153 | if ($valuator = $this->getAttributeValuator($attribute)) { 154 | $value = forward_static_call([$valuator, 'toStorableValue'], $value); 155 | } 156 | 157 | return parent::setAttribute($attribute, $value); 158 | } 159 | 160 | /** 161 | * Run the increment or decrement method on the model. 162 | * 163 | * @param string $column 164 | * @param int $amount 165 | * @param array $extra 166 | * @param string $method 167 | * 168 | * @return int 169 | */ 170 | protected function incrementOrDecrement($column, $amount, $extra, $method) 171 | { 172 | $query = $this->newQuery(); 173 | 174 | if (!$this->exists) { 175 | return $query->{$method}($column, $amount, $extra); 176 | } 177 | 178 | $this->incrementOrDecrementAttributeValue($column, $amount, $extra, $method); 179 | 180 | // ***[ fix increment/decrement bug]*** 181 | if ($valuator = $this->getAttributeValuator($column)) { 182 | $amount = forward_static_call([$valuator, 'toStorableValue'], $amount); 183 | } 184 | 185 | return $query->where( 186 | $this->getKeyName(), 187 | $this->getKey() 188 | )->{$method}($column, $amount, $extra); 189 | } 190 | 191 | /** 192 | * Override HasAttributes::attributesToArray. 193 | * 194 | * @return array 195 | */ 196 | public function attributesToArray() 197 | { 198 | $attributes = parent::attributesToArray(); 199 | 200 | if ($this->shouldAppendRevaluatedAttributesToArray()) { 201 | foreach (array_keys($this->getRevaluableAttributes()) as $attribute) { 202 | if ($valuator = $this->getRevaluatedAttribute($attribute)) { 203 | $attribute = $this->shouldReplaceRawAttributesToArray() ? $attribute : $this->getRevaluablePrefixedAttributeName($attribute); 204 | $attributes[$attribute] = $valuator->toDefaultFormat(); 205 | } 206 | } 207 | } 208 | 209 | return $attributes; 210 | } 211 | 212 | /** 213 | * @param string $attribute 214 | * 215 | * @return string 216 | */ 217 | public function getRevaluablePrefixedAttributeName($attribute) 218 | { 219 | return $this->getRevaluableAttributePrefix().'_'.$attribute; 220 | } 221 | 222 | /** 223 | * Fetch attribute. 224 | * 225 | * @example 226 | *
227 |      * $object->getRevaluatedPriceAttribute();
228 |      * $object->getRevaluatedXXXAttribute();
229 |      * 
230 | * 231 | * @param string $method 232 | * 233 | * @return mixed 234 | */ 235 | public function __call($method, $args) 236 | { 237 | $prefix = \Illuminate\Support\Str::studly($this->getRevaluableAttributePrefix()); 238 | if (preg_match("/get{$prefix}(?\\w+)Attribute/i", $method, $matches)) { 239 | return $this->getRevaluatedAttribute($matches['attribute']); 240 | } 241 | 242 | return parent::__call($method, $args); 243 | } 244 | 245 | /** 246 | * @return bool 247 | */ 248 | protected function shouldAppendRevaluatedAttributesToArray() 249 | { 250 | return property_exists($this, 'appendRevaluatedAttributesToArray') ? $this->appendRevaluatedAttributesToArray : true; 251 | } 252 | 253 | /** 254 | * @return bool 255 | */ 256 | protected function shouldReplaceRawAttributesToArray() 257 | { 258 | return property_exists($this, 'replaceRawAttributesToArray') ? $this->replaceRawAttributesToArray : true; 259 | } 260 | 261 | /** 262 | * Return revaluated value of attribute. 263 | * 264 | * @param string $attribute 265 | * 266 | * @return mixed 267 | */ 268 | protected function getStorableValue($attribute) 269 | { 270 | if ($valuator = $this->getAttributeValuator($attribute)) { 271 | if (is_callable($valuator, 'toStorableValue')) { 272 | $value = forward_static_call([$valuator, 'toStorableValue'], $this->attributes[$attribute]); 273 | } 274 | } 275 | 276 | return $value; 277 | } 278 | 279 | /** 280 | * Get attribute valuator. 281 | * 282 | * @param string $attribute 283 | * 284 | * @return string 285 | */ 286 | protected function getAttributeValuator($attribute) 287 | { 288 | return \Illuminate\Support\Arr::get($this->getRevaluableAttributes(), $attribute); 289 | } 290 | } 291 | --------------------------------------------------------------------------------