php 行为Yii2
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php 行为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;
}
}
以上是关于php 行为Yii2的主要内容,如果未能解决你的问题,请参考以下文章