From 1536d3d6723a7e841229afa59c288bc33e69dccf Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Fri, 22 Dec 2023 17:23:47 +0200 Subject: [PATCH] add docs --- README.md | 281 ++++++++++++++++++++++++++++++++++++++- src/TypecastBehavior.php | 5 +- 2 files changed, 283 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9ce583c..973bb9c 100644 --- a/README.md +++ b/README.md @@ -38,4 +38,283 @@ to the "require" section of your composer.json. Usage ----- -This extension provides support for Yii1 Model and ActiveRecord attributes typecast. \ No newline at end of file +This extension provides support for Yii1 Model and ActiveRecord automatic attributes typecast. +It is performed via usage of `\yii1tech\model\typecast\TypecastBehavior` behavior. +It should be attached to `\CModel` or `\CActiveRecord` descendant. +For example: + +```php + [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'id' => TypecastBehavior::TYPE_INTEGER, + 'amount' => TypecastBehavior::TYPE_INTEGER, + 'price' => TypecastBehavior::TYPE_FLOAT, + 'is_active' => TypecastBehavior::TYPE_BOOLEAN, + 'created_at' => TypecastBehavior::TYPE_DATETIME, + 'json_data' => TypecastBehavior::TYPE_ARRAY_OBJECT, + ], + 'typecastAfterValidate' => true, + 'typecastBeforeSave' => false, + 'typecastAfterSave' => true, + 'typecastAfterFind' => true, + ], + ]; + } + + // ... +} +``` + +> Tip: you may leave `\yii1tech\model\typecast\TypecastBehavior::$attributeTypes` blank - in this case its value will be detected +automatically: for ActiveRecord - based on owner DB table schema, for regular model - based validation rules. + +In the above example attribute typecasting will be automatically performed in following cases: + +- after model successful validation +- after model successful saving +- after model retrieval from Database + +For example: + +```php +setAttributes([ + 'name' => 'item name', + 'price' => '10.50', + 'amount' => '14', + 'is_active' => '1', +]); + +if ($model->validate()) { + var_dump($model->price); // outputs: float(10.5) + var_dump($model->amount); // outputs: int(14) + var_dump($model->is_active); // outputs: bool(true) +} + +$model = Item::model()->findByPk($id); +var_dump($model->id); // outputs: int(12345) +var_dump($model->amount); // outputs: int(18) +var_dump($model->is_active); // outputs: bool(true) +``` + +You can manually trigger attribute typecasting anytime invoking `\yii1tech\model\typecast\TypecastBehavior::typecastAttributes()` method: + +```php +price = '38.5'; +$model->is_active = 1; +$model->typecastAttributes(); + +var_dump($model->price); // outputs: float(38.5) +var_dump($model->is_active); // outputs: bool(true) +``` + + +### JSON typecasting + +This behavior allows automatic conversion of array or traversable objects into JSON string on model saving. +For example: + +```php +json_data = [ // will be saved in DB as '{foo: "bar"}' + 'foo' => 'bar', +]; +$model->save(); +``` + +> Note: such conversion will take place even, if there is no direct attribute type specification. + +You can typecast JSON column value either to plain `array` or `\ArrayObject` instance. +Plain arrays consume less memory, but writing of its particular internal keys will not work. +`\ArrayObject` allows free operation over internal keys, but you should note that its value always passed by reference. + +```php + [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'json_data' => TypecastBehavior::TYPE_ARRAY, + ], + ], + ]; + } + + // ... +} + +$model = Item::model()->findByPk($id); +var_dump($model->json_data); // outputs: array(1) {...} +var_dump($model->json_data['foo']); // outputs: string(bar) +$model->json_data['foo'] = 'new value'; // PHP E_NOTICE: Indirect modification of overloaded property Item::$json_data has no effect! +$model->json_data = [ // no problem + 'foo' => 'new value', +]; +$model->save(); + +// in case mapping in `attributeTypes` is set to `TypecastBehavior::TYPE_ARRAY_OBJECT` +use yii1tech\model\typecast\TypecastBehavior; + +class Item extends CActiveRecord +{ + public function behaviors() + { + return [ + 'typecastBehavior' => [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'json_data' => TypecastBehavior::TYPE_ARRAY_OBJECT, + ], + ], + ]; + } + + // ... +} + +$model = Item::model()->findByPk($id); +var_dump($model->json_data); // outputs: object(ArrayObject) {...} +var_dump($model->json_data['foo']); // outputs: string(bar) +$model->json_data['foo'] = 'new value'; // no problem +$jsonDataCopy = $model->json_data; // new variable holds the reference to `\ArrayObject` instance! +$jsonDataCopy['foo'] = 'value from copy'; // changes value of `$model->json_data`! +var_dump($model->json_data['foo']); // outputs: string(value from copy) +$model->save(); +``` + + +### DateTime typecasting + +This behavior allows automatic conversion of `\DateTime` instances into ISO datetime string on model saving. +For example: + +```php + [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'created_at' => TypecastBehavior::TYPE_DATETIME, + ], + ], + ]; + } + + // ... +} + +$model = new Item(); +$model->created_at = new DateTime('now'); // will be saved in DB as '2023-12-22 10:14:17' +$model->save(); + +$model = Item::model()->findByPk($id); +var_dump($model->created_at); // outputs: object(DateTime) +``` + +In case you store the dates using integer Unix timestamp, you can use `\yii1tech\model\typecast\TypecastBehavior::TYPE_TIMESTAMP` for correct +conversion. For example: + +```php + [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'created_at' => TypecastBehavior::TYPE_TIMESTAMP, + ], + ], + ]; + } + + // ... +} + +$model = new Item(); +$model->created_at = new DateTime('now'); // will be saved in DB as '1703257478' +$model->save(); + +$model = Item::model()->findByPk($id); +var_dump($model->created_at); // outputs: object(DateTime) +``` + + +### Custom typecasting + +You may specify any custom typecasting for the attribute using a callable as a type specification at `\yii1tech\model\typecast\TypecastBehavior::$attributeTypes`. +For example: + +```php + [ + 'class' => TypecastBehavior::class, + 'attributeTypes' => [ + 'heap_data' => function ($value) { + if (is_object($value)) { + return $value; + } + + $heap = new \SplMaxHeap(); + foreach (json_decode($value) as $element) { + $heap->insert($element); + } + + return $heap; + }, + ], + ], + ]; + } + + // ... +} + +$model = Item::model()->findByPk($id); +var_dump($model->heap_data); // outputs: object(SplMaxHeap) +``` diff --git a/src/TypecastBehavior.php b/src/TypecastBehavior.php index 9db325a..78e233c 100644 --- a/src/TypecastBehavior.php +++ b/src/TypecastBehavior.php @@ -32,6 +32,7 @@ * 'typecastBehavior' => [ * 'class' => TypecastBehavior::class, * 'attributeTypes' => [ + * 'id' => TypecastBehavior::TYPE_INTEGER, * 'amount' => TypecastBehavior::TYPE_INTEGER, * 'price' => TypecastBehavior::TYPE_FLOAT, * 'is_active' => TypecastBehavior::TYPE_BOOLEAN, @@ -50,8 +51,8 @@ * } * ``` * - * Tip: you may left {@see $attributeTypes} blank - in this case its value will be detected - * automatically based on owner DB table schema, or validation rules. + * Tip: you may leave {@see $attributeTypes} blank - in this case its value will be detected + * automatically: for ActiveRecord - based on owner DB table schema, for regular model - based validation rules. * * Note: you can manually trigger attribute typecasting anytime invoking {@see typecastAttributes()} method: *