In a few projects I had a form with parent category search field, and you need to move node or insert in exact position while creating or updating model. If you don't use nested sets and use only parentId field there is no problem at all, but with pure nested sets you should choose what operation to use in every case. After all I've come to this simple solution and just want to share it.
For simplicity you can add parentId field (not necessary to database!) and use this overridden behavior.
After this you can set parentId value and use just a save() method anywhere instead of many others.
Overridden behavior
class NestedSetsBehavior extends \creocoder\nestedsets\NestedSetsBehavior
{
/**
* Simplifying saving by checking parentId field if operation is not set
* or modifying field value otherwise
*/
protected function checkOperation() {
/** @var Category $owner */
$owner = $this->owner;
if (!in_array($this->operation, [
self::OPERATION_MAKE_ROOT,
self::OPERATION_PREPEND_TO,
self::OPERATION_APPEND_TO,
self::OPERATION_INSERT_BEFORE,
self::OPERATION_INSERT_AFTER,
])
) {
if ($owner->parentId) {
$this->operation = self::OPERATION_APPEND_TO;
$this->node = Category::findOne($owner->parentId);
} else {
$owner->parentId = null;
$this->operation = self::OPERATION_MAKE_ROOT;
}
}
if ($this->operation == self::OPERATION_MAKE_ROOT) {
$owner->parentId = null;
} else if ($this->node && in_array($this->operation, [
self::OPERATION_PREPEND_TO,
self::OPERATION_APPEND_TO,
])
) {
$owner->parentId = $this->node->id;
} else if ($this->node && in_array($this->operation, [
self::OPERATION_INSERT_BEFORE,
self::OPERATION_INSERT_AFTER,
])
) {
$owner->parentId = $this->node->parentId;
}
}
/**
*
* @inheritDoc
*/
public function beforeInsert() {
$this->checkOperation();
parent::beforeInsert();
}
/**
* @inheritDoc
*/
public function beforeUpdate() {
$this->checkOperation();
parent::beforeUpdate();
}
}
In controller
/**
* Creates a new Category model.
*
* @return mixed
*/
public function actionCreate() {
$model = new Category([]);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('catalog', 'Category created'));
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Search for typeahead
*
* @param $term
* @return array
*/
public function actionSearch($term) {
$data = [];
$term = trim($term);
/** @var Category[] $categories */
$categories = Category::find()
->orFilterWhere(['like', 'title', $term])
->orderBy('deleted ASC')->limit(10)->all();
if ($categories) foreach ($categories as $category) {
$data[] = [
'value' => $category->title,
'id' => $category->id,
];
}
Yii::$app->response->format = Response::FORMAT_JSON;
return $data;
}
PS: i asked this question some time ago here, and see people looking out here and as i'm not the only one, who wonders, just wanted to share my solution of this issue.
In a few projects I had a form with parent category search field, and you need to move node or insert in exact position while creating or updating model. If you don't use nested sets and use only parentId field there is no problem at all, but with pure nested sets you should choose what operation to use in every case. After all I've come to this simple solution and just want to share it.
For simplicity you can add parentId field (not necessary to database!) and use this overridden behavior. After this you can set parentId value and use just a save() method anywhere instead of many others.
Overridden behavior
In controller
In view
PS: i asked this question some time ago here, and see people looking out here and as i'm not the only one, who wonders, just wanted to share my solution of this issue.