savchukoleksii
4/7/2018 - 7:03 AM

Behaviors Yii2

<?php
/**
 * Created by PhpStorm.
 * User: savch
 * Date: 01.03.2018
 * Time: 14:41
 */

namespace common\components\behaviors;

use yii\base\Behavior;

class SortedTreeBehavior extends Behavior
{
    public $idAttributeName             = 'id';
    public $parentIdAttributeName       = 'parent_id';
    public $pathAttributeName           = 'path';
    public $depthAttributeName          = 'depth';
    public $pathAttributeDelimiter      = '.';
    public $pathAttributeSize           = 8;

    public $sortAttributeName           = 'sort';
    public $previousIdAttributeName     = 'previous_id';
    public $nextIdAttributeName         = 'next_id';

    public $sortAttributeDelimiter      = '.';

    public $sortAttributeSize           = 8;
    public $sortAttributeReserveSymbols = 4;

    public function attach($owner)
    {
        parent::attach($owner);

        $owner->attachBehavior(MaterializedPathBehavior::className(), [
            'class'                         => MaterializedPathBehavior::className(),
            'idAttributeName'               => $this->idAttributeName,
            'parentIdAttributeName'         => $this->parentIdAttributeName,
            'pathAttributeName'             => $this->pathAttributeName,
            'depthAttributeName'            => $this->depthAttributeName,
            'pathAttributeDelimiter'        => $this->pathAttributeDelimiter,
            'pathAttributeSize'             => $this->pathAttributeSize
        ]);

        $owner->attachBehavior(LinkedListBehavior::className(), [
            'class'                         => LinkedListBehavior::className(),
            'idAttributeName'               => $this->idAttributeName,
            'sortAttributeName'             => $this->sortAttributeName,
            'listIdAttributeName'           => $this->parentIdAttributeName,
            'previousIdAttributeName'       => $this->previousIdAttributeName,
            'nextIdAttributeName'           => $this->nextIdAttributeName,
            'sortAttributeDelimiter'        => $this->sortAttributeDelimiter,
            'sortAttributeSize'             => $this->sortAttributeSize,
            'sortAttributeReserveSymbols'   => $this->sortAttributeReserveSymbols
        ]);
    }

    public function detach()
    {
        $this->owner->detachBehavior(MaterializedPathBehavior::className());
        $this->owner->detachBehavior(LinkedListBehavior::className());

        parent::detach();
    }
}
<?php
namespace common\components\behaviors;

use Yii;
use yii\db\ActiveRecord;
use yii\base\Behavior;
use yii\validators\Validator;

class MaterializedPathBehavior extends Behavior
{
    public $idAttributeName         = 'id';
    public $parentIdAttributeName   = 'parent_id';
    public $pathAttributeName       = 'path';
    public $depthAttributeName      = 'depth';

    protected $ownerClassName;
    protected $ownerTableName;

    public $pathAttributeDelimiter  = '.';
    public $pathAttributeSize       = 8;

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_DELETE   => 'deleteCurrentElementOfTree',
            ActiveRecord::EVENT_BEFORE_UPDATE   => 'updateCurrentElementOfTree',
            ActiveRecord::EVENT_AFTER_INSERT    => 'insertCurrentElementOfTree',
        ];
    }

    public function attach($owner){
        parent::attach($owner);

        $this->ownerClassName = $owner->className();
        $this->ownerTableName = $owner->tableName();

        $this->registerMaterializedPathValidators($owner);
    }

    protected function registerMaterializedPathValidators($owner){
        $owner->validators[] = Validator::createValidator('integer', $owner, [
            $this->parentIdAttributeName,
            $this->depthAttributeName
        ]);

        $owner->validators[] = Validator::createValidator('required', $owner, [
            $this->parentIdAttributeName
        ]);

        $owner->validators[] = Validator::createValidator('string', $owner, [
            $this->pathAttributeName
        ]);
    }

    public function getParentElement(){
        $ownerClassName = $this->ownerClassName;

        if($this->owner->{$this->parentIdAttributeName} === null){
            return $this->getSuperElement();
        }

        $parent = $ownerClassName::find()->where([
            $this->idAttributeName => $this->owner->{$this->parentIdAttributeName}
        ])->one();

        if($parent === null){
            $parent = $this->getSuperElement();
        }

        if($parent === null){
            throw new \Exception("You must have super parent in your table!");
        }

        return $parent;
    }

    public function getSuperElement(){
        $ownerClassName = $this->ownerClassName;
        return $ownerClassName::find()->where([
            $this->parentIdAttributeName => null
        ])->one();
    }

    public function insertCurrentElementOfTree(){
        $parent = $this->getParentElement();

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->parentIdAttributeName    => $parent->{$this->idAttributeName},
            $this->pathAttributeName        => $this->calculatePathAttribute(
                $parent->{$this->pathAttributeName},
                $this->encodePathAttribute($this->owner->{$this->idAttributeName})
            ),
            $this->depthAttributeName       => $parent->{$this->depthAttributeName} + 1,
        ),
        array(
            $this->idAttributeName          => $this->owner->{$this->idAttributeName}
        ))->execute();
    }

    public function updateCurrentElementOfTree($event){
        $ownerClassName = $this->ownerClassName;
        $old = $ownerClassName::findOne($this->owner->{$this->idAttributeName});

        if($old === null){
            $event->isValid = false;
            return false;
        }

        $superElement = $this->getSuperElement();

        if($this->owner->{$this->idAttributeName} === $superElement->{$this->idAttributeName}){
            $event->isValid = false;
            return false;
        }

        $parent = $this->getParentElement();

        $elements = $ownerClassName::find()->andWhere(
            ['like', $this->pathAttributeName, $old->{$this->pathAttributeName}]
        )->all();

        $transact = Yii::$app->db->beginTransaction();

        $path = $this->calculatePathAttribute(
            $parent->{$this->pathAttributeName},
            $this->encodePathAttribute($this->owner->{$this->idAttributeName})
        );

        $this->owner->{$this->depthAttributeName} = $parent->{$this->depthAttributeName} + 1;

        try{
            foreach ($elements as $element){
                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->pathAttributeName        => $this->recalculatePathAttribute(
                        $old->{$this->pathAttributeName},
                        $path,
                        $element->{$this->pathAttributeName}
                    ),
                    $this->depthAttributeName       => $parent->{$this->depthAttributeName} + $element->{$this->depthAttributeName} - $old->{$this->depthAttributeName} + 1,
                ),
                array(
                    $this->idAttributeName          => $element->{$this->idAttributeName}
                ))->execute();
            }

            $transact->commit();
            return true;
        }catch (\Exception $e){
            $transact->rollBack();
        }catch(\Throwable $e){
            $transact->rollBack();
        }

        $event->isValid = false;
        return true;
    }

    public function deleteCurrentElementOfTree($event){
        $superElement = $this->getSuperElement();

        $transact = Yii::$app->db->beginTransaction();
        try{
            Yii::$app->db->createCommand()->delete(
                $this->ownerTableName,
                "{$this->pathAttributeName} LIKE '{$this->owner->{$this->pathAttributeName}}%' AND NOT {$this->idAttributeName} = {$superElement->{$this->idAttributeName}}"
            )->execute();

            $transact->commit();

            if($this->owner->{$this->idAttributeName} === $superElement->{$this->idAttributeName}){
                $event->isValid = false;
            }

            $transact->commit();
            return true;
        }catch (\Exception $e){
            $transact->rollBack();
        }catch(\Throwable $e){
            $transact->rollBack();
        }

        $event->isValid = false;
        return false;
    }

    public function calculatePathAttribute($parent, $child){
        return implode($this->pathAttributeDelimiter, array(
            $parent,
            $child
        ));
    }

    public function recalculatePathAttribute($old, $new, $path){
        $old = '/'. $old . '/';
        return preg_replace($old, $new, $path, 1);
    }

    public function encodePathAttribute($id){
        return str_pad($id, $this->pathAttributeSize ,'0',STR_PAD_LEFT);
    }
}
<?php

namespace common\components\behaviors;

use Yii;
use yii\db\ActiveRecord;
use yii\base\Behavior;
use yii\validators\Validator;
use yii\web\View;

class LinkedListBehavior extends Behavior
{
    const START    = 'start';
    const BEFORE   = 'before';
    const AFTER    = 'after';
    const END      = 'end';

    public $idAttributeName             = 'id';
    public $sortAttributeName           = 'sort';
    public $listIdAttributeName         = 'list_id';
    public $previousIdAttributeName     = 'previous_id';
    public $nextIdAttributeName         = 'next_id';

    public $sortAttributePrefix         = null;
    public $sortAttributePrefixFunction = null;
    public $sortAttributeDelimiter      = ':';


    public $sortAttributeSize           = 8;
    public $sortAttributeReserveSymbols = 4;
    public $position;

    protected $length;
    protected $ownerClassName;
    protected $ownerTableName;

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_DELETE       => 'deleteCurrentElementOfList',
            ActiveRecord::EVENT_AFTER_INSERT        => 'insertCurrentElementOfList',
            ActiveRecord::EVENT_BEFORE_UPDATE       => 'deleteCurrentElementOfList',
            ActiveRecord::EVENT_AFTER_UPDATE        => 'insertCurrentElementOfList'
        ];
    }

    public function attach($owner){
        parent::attach($owner);

        $this->ownerClassName = $owner->className();
        $this->ownerTableName = $owner->tableName();
        $this->length         = $this->getLengthOfCurrentList();

        $this->registerLinkedListAttributeValidators($owner);
        $this->registerLinkedListJsScripts();
    }

    public function attachConditions(&$query){
        $query->addOrderBy([
            $this->sortAttributeName    => SORT_ASC
        ]);
    }

    protected function registerLinkedListAttributeValidators($owner){
        $modelClass = strtolower(end(explode('\\', $this->ownerClassName)));

        $owner->validators[] = Validator::createValidator('required', $owner, [
            $this->listIdAttributeName,
            'position'
        ]);

        $owner->validators[] = Validator::createValidator('string', $owner, [
            $this->sortAttributeName,
            'position'
        ]);

        $owner->validators[] = Validator::createValidator('integer', $owner, [
            $this->listIdAttributeName,
            $this->previousIdAttributeName,
            $this->nextIdAttributeName
        ]);

        $owner->validators[] = Validator::createValidator('required', $owner, [
            $this->previousIdAttributeName
        ],
        [
            'when' => function($model){
                return $model->position == self::AFTER;
            },
            'whenClient' => "function (attribute, value) {
                return $(\"#{$modelClass}-position\").val() == '" . self::AFTER . "';
            }",
        ]);

        $owner->validators[] = Validator::createValidator('required', $owner, [
            $this->nextIdAttributeName
        ],
        [
            'when' => function($model){
                return $model->position === self::BEFORE;
            },
            'whenClient' => "function (attribute, value) {
                return $(\"#{$modelClass}-position\").val() == '" . self::BEFORE . "';
            }",
        ]);
    }

    protected function registerLinkedListJsScripts(){
        $modelClass = strtolower(end(explode('\\', $this->ownerClassName)));

        Yii::$app->controller->getView()->registerJS("
            var switchInputs = function(){
                $(\".field-{$modelClass}-{$this->previousIdAttributeName}\").hide();
                $(\".field-{$modelClass}-{$this->nextIdAttributeName}\").hide();
    
                if($(\"#{$modelClass}-position\").val() == '" . self::AFTER . "'){
                    $(\".field-{$modelClass}-{$this->previousIdAttributeName}\").show();
                }
    
                if($(\"#{$modelClass}-position\").val() == '" . self::BEFORE . "'){
                    $(\".field-{$modelClass}-{$this->nextIdAttributeName}\").show();
                }
            };
    
            switchInputs();
    
            $(\"#{$modelClass}-position\").change(switchInputs);
        ", View::POS_END);
    }

    public function getLengthOfCurrentList(){
        $ownerClassName = $this->ownerClassName;
        return $ownerClassName::find()->where([
            $this->listIdAttributeName      => $this->owner->{$this->listIdAttributeName}
        ])->count();
    }

    public function getFirstElementOfList(){
        $ownerClassName = $this->ownerClassName;
        $query = $ownerClassName::find()->where([
            $this->listIdAttributeName      => $this->owner->{$this->listIdAttributeName},
            $this->previousIdAttributeName  => null
        ]);

        if(!$this->owner->isNewRecord){
            $query->andWhere(['!=', $this->idAttributeName, $this->owner->{$this->idAttributeName}]);
        }

        return $query->one();
    }

    public function getLastElementOfList(){
        $ownerClassName = $this->ownerClassName;
        $query = $ownerClassName::find()->where([
            $this->listIdAttributeName      => $this->owner->{$this->listIdAttributeName},
            $this->nextIdAttributeName  => null
        ]);

        if(!$this->owner->isNewRecord){
            $query->andWhere(['!=', $this->idAttributeName, $this->owner->{$this->idAttributeName}]);
        }

        return $query->one();
    }

    public function getPreviousElementOfList(){
        $ownerClassName = $this->ownerClassName;

        if($this->owner->{$this->previousIdAttributeName} === null){
            return null;
        }

        return $ownerClassName::find()->where([
            $this->listIdAttributeName      => $this->owner->{$this->listIdAttributeName},
            $this->idAttributeName          => $this->owner->{$this->previousIdAttributeName}
        ])->one();
    }

    public function getNextElementOfList(){
        $ownerClassName = $this->ownerClassName;

        if($this->owner->{$this->nextIdAttributeName} === null){
            return null;
        }

        return $ownerClassName::find()->where([
            $this->listIdAttributeName      => $this->owner->{$this->listIdAttributeName},
            $this->idAttributeName          => $this->owner->{$this->nextIdAttributeName}
        ])->one();
    }

    public function insertCurrentElementOfList(){
        if($this->owner->isNewRecord){
            return;
        }

        $transaction = Yii::$app->db->beginTransaction();
        try{
            if($this->position === self::START){
                $this->pushFrontCurrentElementOfList();
            }

            if($this->position === self::END){
                $this->pushBackCurrentElementOfList();
            }

            if($this->position === self::BEFORE){
                $this->pushBeforeCurrentElementOfList();
            }

            if($this->position === self::AFTER){
                $this->pushAfterCurrentElementOfList();
            }

            $transaction->commit();
        }catch(\Exception $e){
            $transaction->rollBack();
        }catch (\Throwable $e) {
            $transaction->rollBack();
        }
    }

    public function updateCurrentElementOfList(){
        $this->deleteCurrentElementOfList();
        $this->insertCurrentElementOfList();
    }

    public function deleteCurrentElementOfList(){
        if($this->owner->isNewRecord){
            return false;
        }

        $previous   = $this->getPreviousElementOfList();
        $next       = $this->getNextElementOfList();

        if($previous === null & $next === null){
            return true;
        }

        $transaction = Yii::$app->db->beginTransaction();
        try{
            if($previous === null && $next !== null){
                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->previousIdAttributeName  => null,
                ),
                array(
                    $this->idAttributeName => $next->{$this->idAttributeName}
                ))->execute();
            }

            if($previous !== null && $next === null){
                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->nextIdAttributeName  => null,
                ),
                array(
                    $this->idAttributeName => $previous->{$this->idAttributeName}
                ))->execute();
            }

            if($previous !== null && $next !== null){
                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->nextIdAttributeName => $next->{$this->idAttributeName}
                ),
                array(
                    $this->idAttributeName => $previous->{$this->idAttributeName}
                ))->execute();

                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->previousIdAttributeName => $previous->{$this->idAttributeName},
                ),
                array(
                    $this->idAttributeName => $next->{$this->idAttributeName}
                ))->execute();
            }

            $transaction->commit();
            return true;
        }catch(\Exception $e){
            $transaction->rollBack();
        }catch (\Throwable $e) {
            $transaction->rollBack();
        }
        return false;
    }

    protected function pushFrontCurrentElementOfList(){
        $this->position = self::START;
        $next = $this->getFirstElementOfList();

        if($next === null){
            $next_id = null;
        }else{
            $next_id = $next->{$this->idAttributeName};
        }

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => null,
            $this->nextIdAttributeName      => $next_id,
            $this->sortAttributeName        => $this->calculateSortAttribute(),
        ),
        array(
            $this->idAttributeName => $this->owner->{$this->idAttributeName}
        ))->execute();

        if($next === null){
            return;
        }

        // update getNextElementOfList item
        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $next->{$this->idAttributeName}
        ))->execute();
    }

    protected function pushBackCurrentElementOfList(){
        $this->position = self::END;
        $previous = $this->getLastElementOfList();

        if($previous === null){
            $previous_id = null;
        }else{
            $previous_id = $previous->{$this->idAttributeName};
        }

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => $previous_id,
            $this->nextIdAttributeName      => null,
            $this->sortAttributeName        => $this->calculateSortAttribute(),
        ),
        array(
            $this->idAttributeName => $this->owner->{$this->idAttributeName}
        ))->execute();

        if($previous === null){
            return;
        }

        // update getPreviousElementOfList item
        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->nextIdAttributeName => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $previous->{$this->idAttributeName}
        ))->execute();
    }

    protected function pushAfterCurrentElementOfList(){
        $this->position = self::AFTER;
        $previous = $this->getPreviousElementOfList();

        if($previous === null){
            $this->pushFrontCurrentElementOfList();
            return;
        }

        if($previous->{$this->nextIdAttributeName} === null){
            $this->pushBackCurrentElementOfList();
            return;
        }

        $next = $previous->getNextElementOfList();

        if($next === null){
            $this->pushBackCurrentElementOfList();
            return;
        }

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => $previous->{$this->idAttributeName},
            $this->nextIdAttributeName      => $next->{$this->idAttributeName},
            $this->sortAttributeName        => $this->calculateSortAttribute(),
        ),
        array(
            $this->idAttributeName => $this->owner->{$this->idAttributeName}
        ))->execute();

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->nextIdAttributeName      => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $previous->{$this->idAttributeName}
        ))->execute();

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $next->{$this->idAttributeName}
        ))->execute();
    }

    protected function pushBeforeCurrentElementOfList(){
        $this->position = self::BEFORE;
        $next = $this->getNextElementOfList();

        if($next === null){
            $this->pushBackCurrentElementOfList();
            return;
        }

        if($next->{$this->previousIdAttributeName} === null){
            $this->pushFrontCurrentElementOfList();
            return;
        }

        $previous = $next->getPreviousElementOfList();

        if($previous === null){
            $this->pushFrontCurrentElementOfList();
            return;
        }

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => $previous->{$this->idAttributeName},
            $this->nextIdAttributeName      => $next->{$this->idAttributeName},
            $this->sortAttributeName        => $this->calculateSortAttribute(),
        ),
        array(
            $this->idAttributeName => $this->owner->{$this->idAttributeName}
        ))->execute();

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->nextIdAttributeName      => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $previous->{$this->idAttributeName}
        ))->execute();

        Yii::$app->db->createCommand()->update($this->ownerTableName, array(
            $this->previousIdAttributeName  => $this->owner->{$this->idAttributeName}
        ),
        array(
            $this->idAttributeName => $next->{$this->idAttributeName}
        ))->execute();
    }

    protected function encodeSortAttribute($position){
        $result = str_pad($position, $this->sortAttributeSize + $this->sortAttributeReserveSymbols - 1 ,'0',STR_PAD_LEFT);

        if(is_string($this->sortAttributePrefix)){
            $result = implode($this->sortAttributeDelimiter, array(
                $this->sortAttributePrefix,
                $result,
            ));
        }

        if(!is_string($this->sortAttributePrefix) && is_string($this->sortAttributePrefixFunction)){
            if($this->owner->hasMethod($this->sortAttributePrefixFunction)){
                $result = implode($this->sortAttributeDelimiter, array(
                    $this->owner->{$this->sortAttributePrefixFunction}(),
                    $result,
                ));
            }
        }

        return $result;
    }

    protected function decodeSortAttribute($code){
        $result = $code;

        if(is_string($this->sortAttributePrefix) || (!is_string($this->sortAttributePrefix) && is_string($this->sortAttributePrefixFunction))){
            $result = explode($this->sortAttributeDelimiter, $code);
            if(sizeof($result) == 1){
                $result = $result[0];
            }else if(sizeof($result) > 1){
                $result = $result[sizeof($result) - 1];
            }
        }

        return ltrim($result, '0');
    }

    public function calculateSortAttribute(){
        if($this->position == self::START){
            $next = $this->getFirstElementOfList();

            if($next === null){
                return $this->encodeSortAttribute(10 ** ($this->sortAttributeReserveSymbols - 1));
            }

            return $this->encodeSortAttribute(
                $this->calculateAverageSortAttribute(0, $this->decodeSortAttribute($next->{$this->sortAttributeName}))
            );
        }

        if($this->position == self::END){
            $last = $this->getLastElementOfList();

            $number = floor($this->decodeSortAttribute($last->{$this->sortAttributeName}) / (10 ** ($this->sortAttributeReserveSymbols - 1)) + 1);
            return $this->encodeSortAttribute(
                $number * (10 ** ($this->sortAttributeReserveSymbols - 1))
            );
        }

        if($this->position == self::AFTER){
            $previous = $this->getPreviousElementOfList();

            if($previous === null){
                $this->position = self::START;
                $this->calculateSortAttribute();
            }

            if($previous->{$this->nextIdAttributeName} === null){
                $this->position = self::END;
                $this->calculateSortAttribute();
            }

            $next = $previous->getNextElementOfList();

            if($next === null){
                $this->position = self::END;
                $this->calculateSortAttribute();
            }

            return $this->encodeSortAttribute(
                $this->calculateAverageSortAttribute(
                    $this->decodeSortAttribute($previous->{$this->sortAttributeName}),
                    $this->decodeSortAttribute($next->{$this->sortAttributeName})
                )
            );
        }

        if($this->position == self::BEFORE){
            $next = $this->getNextElementOfList();
            if($next === null){
                $this->position = self::END;
                $this->calculateSortAttribute();
            }

            if($next->{$this->previousIdAttributeName} === null){
                $this->position = self::START;
                $this->calculateSortAttribute();
            }

            $previous = $next->getPreviousElementOfList();

            if($previous === null){
                $this->position = self::START;
                $this->calculateSortAttribute();
            }

            return $this->encodeSortAttribute(
                $this->calculateAverageSortAttribute(
                    $this->decodeSortAttribute($previous->{$this->sortAttributeName}),
                    $this->decodeSortAttribute($next->{$this->sortAttributeName})
                )
            );
        }
    }

    protected function calculateAverageSortAttribute($previous, $next){
        $position = ceil(($previous + $next) / 2);
        if($position > $previous && $position < $next){
            return $position;
        }

        if($this->updateSortAttributeInAllRecords()){
            return $this->calculateSortAttribute();
        }
    }

    protected function updateSortAttributeInAllRecords(){
        $ownerClassName = $this->ownerClassName;

        $items = $ownerClassName::find()->where([
            $this->listIdAttributeName => $this->owner->{$this->listIdAttributeName}
        ])->andWhere([
            'not', [$this->sortAttributeName => null]
        ])->orderBy([
            $this->sortAttributeName => SORT_ASC
        ]);

        $i = 1;

        $transaction = Yii::$app->db->beginTransaction();
        try{
            foreach ($items->each(100) as $item){
                Yii::$app->db->createCommand()->update($this->ownerTableName, array(
                    $this->sortAttributeName => $this->encodeSortAttribute($i * (10 ** ($this->sortAttributeReserveSymbols - 1)))
                ),
                array(
                    $this->idAttributeName => $item->{$this->idAttributeName}
                ))->execute();

                $i++;
            }

            $transaction->commit();
            return true;
        }catch(\Exception $e){
            $transaction->rollBack();
        }catch (\Throwable $e) {
            $transaction->rollBack();
        }

        return false;
    }
}