├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── SoftDeleteBehavior.php └── SoftDeleteQueryBehavior.php /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/cornernote/yii2-softdelete). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ phpunit 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The BSD License (BSD) 2 | 3 | Copyright (c) 2013-2015, Mr PHP 4 | 5 | > Redistribution and use in source and binary forms, with or without modification, 6 | > are permitted provided that the following conditions are met: 7 | > 8 | > Redistributions of source code must retain the above copyright notice, this 9 | > list of conditions and the following disclaimer. 10 | > 11 | > Redistributions in binary form must reproduce the above copyright notice, this 12 | > list of conditions and the following disclaimer in the documentation and/or 13 | > other materials provided with the distribution. 14 | > 15 | > Neither the name of Mr PHP. nor the names of its 16 | > contributors may be used to endorse or promote products derived from 17 | > this software without specific prior written permission. 18 | > 19 | >THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | >ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | >WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | >DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | >ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | >(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | >LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | >ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | >(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | >SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 Soft Delete 2 | 3 | [![Latest Version](https://img.shields.io/github/tag/cornernote/yii2-softdelete.svg?style=flat-square&label=release)](https://github.com/cornernote/yii2-softdelete/tags) 4 | [![Software License](https://img.shields.io/badge/license-BSD-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Build Status](https://img.shields.io/travis/cornernote/yii2-softdelete/master.svg?style=flat-square)](https://travis-ci.org/cornernote/yii2-softdelete) 6 | [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/cornernote/yii2-softdelete.svg?style=flat-square)](https://scrutinizer-ci.com/g/cornernote/yii2-softdelete/code-structure) 7 | [![Quality Score](https://img.shields.io/scrutinizer/g/cornernote/yii2-softdelete.svg?style=flat-square)](https://scrutinizer-ci.com/g/cornernote/yii2-softdelete) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/cornernote/yii2-softdelete.svg?style=flat-square)](https://packagist.org/packages/cornernote/yii2-softdelete) 9 | 10 | Soft delete behavior for Yii2. 11 | 12 | 13 | ## Installation 14 | 15 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 16 | 17 | Either run 18 | 19 | ``` 20 | $ composer require cornernote/yii2-softdelete "*" 21 | ``` 22 | 23 | or add 24 | 25 | ``` 26 | "cornernote/yii2-softdelete": "*" 27 | ``` 28 | 29 | to the `require` section of your `composer.json` file. 30 | 31 | 32 | ## Usage 33 | 34 | In your ActiveRecord class: 35 | 36 | ```php 37 | public function behaviors() { 38 | return [ 39 | \cornernote\softdelete\SoftDeleteBehavior::className(), 40 | // or 41 | [ 42 | 'class' => \cornernote\softdelete\SoftDeleteBehavior::className(), 43 | 'attribute' => 'deleted_time', 44 | 'value' => new \yii\db\Expression('NOW()'), // for sqlite use - new \yii\db\Expression("date('now')") 45 | ], 46 | ]; 47 | } 48 | ``` 49 | 50 | then you can use explicitly `$model->softDelete()`, `$model->hardDelete()` 51 | and `$model->unDelete()` (for softly deleted models). Each of these methods return 52 | boolean result. Also `$model->softDelete()` calls indirectly from `$model->delete()`, 53 | which always returns `false`. 54 | 55 | In your ActiveQuery class: 56 | 57 | ```php 58 | public function behaviors() { 59 | return [ 60 | \cornernote\softdelete\SoftDeleteQueryBehavior::className(), 61 | // or 62 | [ 63 | 'class' => \cornernote\softdelete\SoftDeleteQueryBehavior::className(), 64 | 'attribute' => 'deleted_time', 65 | ], 66 | ]; 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cornernote/yii2-softdelete", 3 | "description": "Soft delete behavior for Yii2.", 4 | "keywords": ["yii2", "soft delete"], 5 | "type": "yii2-behavior", 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Brett O'Donnell", 10 | "email": "cornernote@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "*" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "~4.0", 18 | "phpunit/dbunit": "~1.0", 19 | "scrutinizer/ocular": "~1.1" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "cornernote\\softdelete\\": "src" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SoftDeleteBehavior.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright 2015 Mr PHP 5 | * @link https://github.com/cornernote/yii2-softdelete 6 | * @license BSD-3-Clause https://raw.github.com/cornernote/yii2-softdelete/master/LICENSE.md 7 | */ 8 | 9 | namespace cornernote\softdelete; 10 | 11 | use yii\base\Behavior; 12 | use yii\base\Event; 13 | use yii\db\BaseActiveRecord; 14 | use yii\db\Expression; 15 | 16 | /** 17 | * SoftDeleteBehavior 18 | * 19 | * @usage: 20 | * ``` 21 | * public function behaviors() { 22 | * return [ 23 | * [ 24 | * 'class' => 'cornernote\behaviors\SoftDeleteBehavior', 25 | * 'attribute' => 'delete_time', 26 | * 'value' => new Expression('NOW()'), 27 | * ], 28 | * ]; 29 | * } 30 | * ``` 31 | * 32 | * @property BaseActiveRecord $owner 33 | */ 34 | class SoftDeleteBehavior extends Behavior 35 | { 36 | /** 37 | * @var string SoftDelete attribute 38 | */ 39 | public $attribute = 'deleted_at'; 40 | 41 | /** 42 | * @var callable|Expression The expression that will be used for generating the timestamp. 43 | * This can be either an anonymous function that returns the timestamp value, 44 | * or an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`). 45 | * If not set, it will use the value of `time()` to set the attributes. 46 | */ 47 | public $value; 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function events() 53 | { 54 | return [BaseActiveRecord::EVENT_BEFORE_DELETE => 'softDeleteEvent']; 55 | } 56 | 57 | /** 58 | * Set the attribute with the current timestamp to mark as deleted 59 | * 60 | * @param Event $event 61 | */ 62 | public function softDeleteEvent($event) 63 | { 64 | // remove and mark as invalid to prevent real deletion 65 | $this->softDelete(); 66 | $event->isValid = false; 67 | } 68 | 69 | /** 70 | * Soft delete record 71 | */ 72 | public function softDelete() 73 | { 74 | // set attribute with evaluated timestamp 75 | $attribute = $this->attribute; 76 | $this->owner->$attribute = $this->getValue(null); 77 | // save record 78 | return $this->owner->save(false, [$attribute]); 79 | } 80 | 81 | /** 82 | * Restore record 83 | */ 84 | public function unDelete() 85 | { 86 | // set attribute as null 87 | $attribute = $this->attribute; 88 | $this->owner->$attribute = null; 89 | // save record 90 | return $this->owner->save(false, [$attribute]); 91 | } 92 | 93 | /** 94 | * Delete record from database regardless of the $safeMode attribute 95 | */ 96 | public function hardDelete() 97 | { 98 | // store model so that we can detach the behavior 99 | $model = $this->owner; 100 | $this->detach(); 101 | // delete as normal 102 | return 0 !== $model->delete(); 103 | } 104 | 105 | /** 106 | * Evaluate the timestamp to be saved. 107 | * 108 | * @param Event|null $event the event that triggers the current attribute updating. 109 | * @return mixed the attribute value 110 | */ 111 | protected function getValue($event) 112 | { 113 | if ($this->value instanceof Expression) { 114 | return $this->value; 115 | } else { 116 | return $this->value !== null ? call_user_func($this->value, $event) : time(); 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/SoftDeleteQueryBehavior.php: -------------------------------------------------------------------------------- 1 | 'cornernote\behaviors\SoftDeleteQueryBehavior', 18 | * 'attribute' => 'delete_time', 19 | * ], 20 | * ]; 21 | * } 22 | * ``` 23 | * 24 | * @property ActiveQuery $owner 25 | * 26 | * @author cornernote 27 | */ 28 | class SoftDeleteQueryBehavior extends Behavior 29 | { 30 | /** 31 | * @var string SoftDelete attribute 32 | */ 33 | public $attribute = 'deleted_at'; 34 | 35 | /** 36 | * @return static 37 | */ 38 | public function deleted() 39 | { 40 | return $this->owner->andWhere($this->tableName() . '.' . $this->attribute . ' IS NOT NULL'); 41 | } 42 | 43 | /** 44 | * @return static 45 | */ 46 | public function notDeleted() 47 | { 48 | return $this->owner->andWhere($this->tableName() . '.' . $this->attribute . ' IS NULL'); 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | protected function tableName() 55 | { 56 | /** @var ActiveRecord $modelClass */ 57 | $modelClass = $this->owner->modelClass; 58 | return $modelClass::tableName(); 59 | } 60 | 61 | } 62 | --------------------------------------------------------------------------------