Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 101
OrderNumberBehavior
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
702.00
0.00% covered (danger)
0.00%
0 / 101
 init
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 9
 events
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 6
 assignDefault
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 11
 decrementGreater
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 17
 moveUpOn
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 23
 moveDownOn
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 23
 getValue
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 12
<?php
namespace common\components;
use Closure;
use Yii;
use yii\base\Behavior;
use yii\base\InvalidCallException;
use yii\db\BaseActiveRecord;
use yii\db\Expression;
class OrderNumberBehavior extends Behavior
{
    /**
     * @var array list of attributes that are to be automatically filled with the value specified via [[value]].
     * The array keys are the ActiveRecord events upon which the attributes are to be updated,
     * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
     * a single attribute, or an array to represent a list of attributes. For example,
     *
     * ```php
     * [
     *     ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],
     *     ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
     * ]
     * ```
     */
    public $attributes = [];
    /**
     * @var mixed the value that will be assigned to the current attributes. This can be an anonymous function,
     * an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`), scalar, string
     * or an arbitrary value. If the former, the return value of the function will be assigned to the attributes.
     * The signature of the function should be as follows,
     *
     * ```php
     * function ($event)
     * {
     *     // return value will be assigned to the attribute
     * }
     * ```
     */
    public $value;
    public $orderAttribute = 'on';
    public $filter = [];
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        if (empty($this->attributes)) {
            $this->attributes = [
                BaseActiveRecord::EVENT_BEFORE_INSERT => $this->orderAttribute,
                BaseActiveRecord::EVENT_AFTER_DELETE => $this->orderAttribute,
            ];
        }
    }
    /**
     * @inheritdoc
     */
    public function events()
    {
        return [
            BaseActiveRecord::EVENT_BEFORE_INSERT => 'assignDefault',
            BaseActiveRecord::EVENT_AFTER_DELETE => 'decrementGreater',
        ];
    }
    /**
     * Evaluates the attribute value and assigns it to the current attributes.
     * @param Event $event
     */
    public function assignDefault($event)
    {
        if (!empty($this->attributes[$event->name])) {
            $attributes = (array) $this->attributes[$event->name];
            foreach ($attributes as $attribute) {
                $value = $this->getValue($event, $attribute);
                if (is_string($attribute)) {
                    $this->owner->$attribute = $value;
                }
            }
        }
    }
    /**
     * Evaluates the attribute value and assigns it to the current attributes.
     * @param Event $event
     */
    public function decrementGreater($event)
    {
        if (!empty($this->attributes[$event->name])) {
            $attributes = (array) $this->attributes[$event->name];
            foreach ($attributes as $attribute) {
                if (is_string($attribute)) {
                    $lastOn = $this->owner->$attribute;
                    $this->owner->getDb()
                        ->createCommand()
                        ->update(
                            $this->owner->tableName(),
                            [$attribute => new Expression('[[' . $attribute . ']] - 1')],
                            ['>', $attribute, $lastOn]
                        )->execute();
                }
            }
        }
    }
    /**
     * Moves record up on ordered list.
     *
     * ```php
     * $model->moveDownOn('on');
     * ```
     * @param string $attribute the name of the attribute to update.
     * @throws InvalidCallException if owner is a new record.
     */
    public function moveUpOn($attribute = null)
    {
        /* @var $owner BaseActiveRecord */
        $owner = $this->owner;
        if ($owner->getIsNewRecord()) {
            throw new InvalidCallException('Moving up is not possible on a new record.');
        }
        if ($attribute === null) {
            $attribute = $this->orderAttribute;
        }
        $nextModel = $owner->find()
            ->where(['<', $attribute, $owner->$attribute])
            ->andWhere($this->filter instanceof Closure
                    ? call_user_func($this->filter, null, $attribute)
                    : $this->filter)
            ->orderBy('[[' . $attribute . ']] DESC')
            ->one();
        if (empty($nextModel)) {
            throw new InvalidCallException('Moving up is not possible on a first record.');
        }
        $owner->$attribute = $nextModel->$attribute;
        $owner->save();
        $nextModel->$attribute = $nextModel->$attribute + 1;
        $nextModel->save();
    }
    /**
     * Moves record down on ordered list.
     *
     * ```php
     * $model->moveDownOn('on');
     * ```
     * @param string $attribute the name of the attribute to update.
     * @throws InvalidCallException if owner is a new record.
     */
    public function moveDownOn($attribute = null)
    {
        /* @var $owner BaseActiveRecord */
        $owner = $this->owner;
        if ($owner->getIsNewRecord()) {
            throw new InvalidCallException('Moving down is not possible on a new record.');
        }
        if ($attribute === null) {
            $attribute = $this->orderAttribute;
        }
        $nextModel = $owner->find()
            ->where(['>', $attribute, $owner->$attribute])
            ->andWhere($this->filter instanceof Closure
                    ? call_user_func($this->filter, null, $attribute)
                    : $this->filter)
            ->orderBy('[[' . $attribute . ']] ASC')
            ->one();
        if (empty($nextModel)) {
            throw new InvalidCallException('Moving down is not possible on a last record.');
        }
        $nextModel->$attribute = $owner->$attribute;
        $nextModel->save();
        $owner->$attribute = $owner->$attribute + 1;
        $owner->save();
    }
    /**
     * Returns the default value for the current attributes.
     * This method is called by [[assignDefault()]]. Its return value will be assigned
     * to the attributes corresponding to the triggering event.
     * @param Event $event the event that triggers the current attribute updating.
     * @return mixed the attribute value
     */
    protected function getValue($event, $attribute)
    {
        if ($this->value === null) {
            $query = $this->owner->find();
            if ($this->filter !== null) {
                $query = $query->where($this->filter instanceof Closure
                    ? call_user_func($this->filter, $event, $attribute)
                    : $this->filter);
            }
            return $query->max('[[' . $attribute . ']]') + 1;
        }
        return $this->value instanceof Closure ? call_user_func($this->value, $event, $attribute) : $this->value;
    }
}