Tree gridview
Tree gridview
Добрый день.
в Yii2 кто-нибудь знает есть ли что-то на подобие
http://jqgrid-php.net/examples/?render=jqOutTree
с поддержкой Ajax
в Yii2 кто-нибудь знает есть ли что-то на подобие
http://jqgrid-php.net/examples/?render=jqOutTree
с поддержкой Ajax
Re: Tree gridview
jstree делал для категорий. Если интересно, могу поделиться
Re: Tree gridview
Спасибо, если можно то хотел бы взглянуть
Re: Tree gridview
View
Controller
Код: Выделить всё
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $config [] */
$this->title = 'Дерево категорий';
$this->params['breadcrumbs'][] = 'Категории';
\backend\assets\JstreeAsset::register($this);
?>
<div class="category-index">
<h1><?= Html::encode($this->title) ?></h1>
<div id="explorer"></div>
</div>
<?php
$this->registerJs("
$('#explorer').jstree({
'core': {
'data' : {
'url' : '" . url(['/category/explorer-request']) . "',
'data' : function (node) {
return {'id': node.id};
}
},
'check_callback' : true
},
'types': {
'default' : {
'icon' : 'glyphicon glyphicon-flash'
}
},
'contextmenu':{
'items': function(node) {
var tree = $('#explorer').jstree(true);
return {
'Create': {
'label': 'Добавить дочернюю',
'action': function (obj) {
node = tree.create_node(node);
}
},
'Rename': {
label: 'Переименовать',
icon: 'glyphicon glyphicon-pencil',
action: function (obj) {
tree.edit(node);
}
},
'Remove': {
label: 'Удалить',
icon: 'glyphicon glyphicon-remove',
action: function (obj) {
tree.delete_node(node);
}
}
};
}
},
'state' : { 'key' : 'state_demo' },
'plugins' : [
'contextmenu',
'dnd',
'state',
// 'types' // При этом жутко тормозит создание ноды
]
}).on('create_node.jstree', function(event, data) {
$.post(
'" . url(['/category/create-node']) . "',
{parent: data.node.parent},
function(response){
if(response.status == 'success') {
$('#explorer').jstree(true).set_id(data.node, response.id);
$('#explorer').jstree(true).edit(data.node);
}
}
);
}).on('rename_node.jstree', function(event, data) {
$.post(
'" . url(['/category/rename-node']) . "',
{id: data.node.id, name: data.node.text},
function(data){}
);
}).on('delete_node.jstree', function(event, data) {
$.post(
'" . url(['/category/delete-node']) . "',
{id: data.node.id},
function(data){}
);
}).on('move_node.jstree', function(event, data) {
$.post(
'" . url(['/category/move-node']) . "',
{
id: data.node.id,
prev_id: $('#' + data.node.id).prev().attr('id'),
parent_id: data.node.parent
},
function(data){}
);
});
");
Код: Выделить всё
<?php
namespace backend\controllers;
use Yii;
use common\models\Category;
use backend\models\search\CategorySearch;
use yii\filters\ContentNegotiator;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\Response;
/**
* Class CategoryController
* @package backend\controllers
*/
class CategoryController extends BackendController
{
/**
* @inheritdoc
*/
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['verbs'] = [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
];
$behaviors['negotiator'] = [
'class' => ContentNegotiator::className(),
'only' => ['explorer-request', 'create-node', 'rename-node', 'delete-node', 'move-node'],
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
return $behaviors;
}
/**
* @return string
*/
public function actionExplorer()
{
return $this->render('explorer');
}
/**
* Ajax запрос на смену состояние проводника
* @link https://github.com/vakata/jstree
* @param $id
* @return array|string
*/
public function actionExplorerRequest($id = '#')
{
$data = [];
if($id == '#') {
$root = Category::getRoot();
$children = $root->children(1)->asArray()->all();
foreach($children as $child) {
$data[] = [
'id' => $child['id'],
'text' => $child['name'],
'children' => true,
];
}
}
else {
$root = Category::findOne($id);
$data['id'] = $id;
$data['text'] = $root['name'];
$data['children'] = [];
$children = $root->children(1)->asArray()->all();
foreach($children as $child) {
$data['children'][] = [
'id' => $child['id'],
'text' => $child['name'],
'children' => true,
];
}
}
return $data;
}
/**
* Ajax запрос на создание категории из проводника
* @return array
*/
public function actionCreateNode() {
$parent = Yii::$app->request->post('parent');
$root = Category::findOne($parent);
if($root) {
$category = new Category(['name' => 'New node']);
if($category->appendTo($root)) {
return [
'status' => 'success',
'id' => $category->id
];
}
}
return [
'status' => 'error'
];
}
/**
* Ajax запрос на переименование категории из проводника
* @return array
*/
public function actionRenameNode() {
$id = Yii::$app->request->post('id');
$name = Yii::$app->request->post('name');
$category = Category::findOne($id);
$category->name = $name;
if($category->save()) {
return ['response' => 'success'];
}
return ['response' => 'error'];
}
/**
* Ajax запрос на удаление категории из проводника
* @return array
*/
public function actionDeleteNode() {
$id = Yii::$app->request->post('id');
$category = Category::findOne($id);
if($category && $category->deleteWithChildren()) {
return ['response' => 'success'];
}
return ['response' => 'error'];
}
/**
* Ajax запрос на перемещение категории из проводника
* @return array
*/
public function actionMoveNode() {
$id = Yii::$app->request->post('id');
$parent_id = Yii::$app->request->post('parent_id');
$prev_id = Yii::$app->request->post('prev_id');
$category = Category::findOne($id);
if($prev_id === null) { // Значит мы засовываем вначало списка у $parent_id
$root = Category::findOne($parent_id);
$category->prependTo($root);
}
else { // Значит мы вставляем за $prev_id
$sibling = Category::findOne($prev_id);
$category->insertAfter($sibling);
}
return ['response' => 'success'];
}
/**
* Lists all Category models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new CategorySearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Category model.
* @param integer $id
* @return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* @return string|\yii\web\Response
* @throws NotFoundHttpException
*/
public function actionCreate()
{
$model = new Category();
$root = Category::getRoot();
if ($model->load(Yii::$app->request->post())) {
if($model->parent_id) {
$root = Category::findOne($model->parent_id);
}
$model->appendTo($root);
Yii::$app->session->setFlash('success', 'Категория добавлена');
return $this->redirect(['index']);
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing Category model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
$root = Category::getRoot();
if ($model->load(Yii::$app->request->post())) {
if($model->parent_id) {
$root = Category::findOne($model->parent_id);
}
$model->appendTo($root);
Yii::$app->session->setFlash('success', 'Категория сохранена');
return $this->redirect(['index']);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Category model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param integer $id
* @return mixed
*/
public function actionDelete($id)
{
$model = $this->findModel($id);
if($model->parent_id == null) {
$model->deleteWithChildren();
}
else {
$model->delete();
}
return $this->redirect(Yii::$app->request->referrer);
}
/**
* Finds the Category model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return Category the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Category::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
Re: Tree gridview
Спасибо
Re: Tree gridview
futbolim,
был бы очень благодарен, если бы Вы еще модель показали
был бы очень благодарен, если бы Вы еще модель показали
Re: Tree gridview
Код: Выделить всё
<?php
namespace common\models;
use common\models\queries\CategoryQuery;
use common\traits\TranslationsMethodTrait;
use creocoder\nestedsets\NestedSetsBehavior;
use yii\behaviors\SluggableBehavior;
use yii\db\ActiveRecord;
/**
* @property integer $id
* @property integer $tree
* @property integer $lft
* @property integer $rgt
* @property integer $depth
* @property string $name
* @property string $slug
*
* @property NewsArticle[] $newsArticles
* @property Translation[] $translations
*/
class Category extends ActiveRecord
{
use TranslationsMethodTrait;
// helper fields
public $parent_id;
/**
* @return CategoryQuery
*/
public static function find()
{
return new CategoryQuery(get_called_class());
}
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%category}}';
}
/**
* @return array
*/
public function behaviors()
{
return [
'tree' => ['class' => NestedSetsBehavior::className(), 'treeAttribute' => 'tree'],
'slug' => ['class' => SluggableBehavior::className(), 'attribute' => 'name'],
];
}
/**
* @return array
*/
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_ALL,
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
['slug', 'trim'],
// ['slug', 'required'],
['slug', 'string', 'max' => 255],
['name', 'trim'],
['name', 'required'],
['name', 'string', 'max' => 255],
['parent_id', 'integer'],
['translations_field', 'safe'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => t('Name'),
'slug' => t('Seo slug'),
'parent_id' => t('Parent category'),
'translations_field' => t('Translations'),
];
}
/**
* @param bool $dropDown
* @param null $without_id
* @param string $prefix
* @param string $name
* @return array
*/
public static function getTree($dropDown = false, $without_id = null, $prefix = '—', $name = 'Product categories')
{
$root = self::findOne(['name' => $name]);
$children = $root->children()->asArray()->all();
if($dropDown) {
$result = [];
foreach ($children as $child) {
if($without_id && $without_id == $child['id']) {
continue;
}
$result[$child['id']] = str_repeat($prefix, $child['depth'] - 1) . ' ' . $child['name'];
}
return $result;
}
return $children;
}
/**
* @return array
*/
public static function getTreeWithGroups()
{
$root = self::findOne(['name' => 'Product categories']);
$children = $root->children(2)->asArray()->all();
$result = [];
$parent_name = '';
foreach ($children as $child) {
if($child['depth'] == 1) {
$parent_name = $child['name'];
$result[$parent_name] = [];
}
if($child['depth'] == 2) {
$result[$parent_name][$child['id']] = $child['name'];
}
}
// d($result);
return $result;
}
/**
* @param null $query
* @param string $name
* @return array
*/
public static function getNestedTree($query = null, $name = 'Product categories')
{
$root = self::findOne(['name' => $name]);
$categories = $root->children()->asArray()->all();
$result = [];
$first_level_i = $second_level_i = 0;
foreach ($categories as $i => $category) {
if($category['depth'] == 1) {
$result[$i] = $category;
$first_level_i = $i;
$result[$i]['need'] = ($query && mb_stripos($category['name'], $query) === false) ? false : true;
}
if($category['depth'] == 2) {
$second_level_i = $i;
$result[$first_level_i]['children'][$second_level_i] = $category;
if($query && mb_stripos($category['name'], $query) === false) {
$result[$first_level_i]['children'][$second_level_i]['need'] = false;
}
else {
$result[$first_level_i]['need'] = true;
$result[$first_level_i]['children'][$second_level_i]['need'] = true;
}
}
if($category['depth'] == 3) {
$result[$first_level_i]['children'][$second_level_i]['children'][$i] = $category;
if($query && mb_stripos($category['name'], $query) === false) {
$result[$first_level_i]['children'][$second_level_i]['children'][$i]['need'] = false;
}
else {
$result[$first_level_i]['need'] = true;
$result[$first_level_i]['children'][$second_level_i]['need'] = true;
$result[$first_level_i]['children'][$second_level_i]['children'][$i]['need'] = true;
}
}
}
if($query !== null) {
// 1 level
foreach($result as $i => $cat1) {
if(!$cat1['need']) {
unset($result[$i]);
}
}
// 2 level
foreach($result as $i => $cat1) {
if(isset($cat1['children']) && count($cat1['children'])) {
foreach ($cat1['children'] as $j => $cat2) {
if(!$cat2['need']) {
unset($result[$i]['children'][$j]);
}
}
}
}
// 3 level
foreach($result as $i => $cat1) {
if(isset($cat1['children']) && count($cat1['children'])) {
foreach ($cat1['children'] as $j => $cat2) {
if(isset($cat2['children']) && count($cat2['children'])) {
foreach ($cat2['children'] as $k => $cat3) {
if(!$cat3['need']) {
unset($result[$i]['children'][$j]['children'][$k]);
}
}
}
}
}
}
}
return $result;
}
/**
* @return \yii\db\ActiveQuery
*/
public function getNewsArticles()
{
return $this->hasMany(NewsArticle::className(), ['category_id' => 'id']);
}
/**
* @param bool $insert
* @param array $changedAttributes
*/
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
if($this->translations_field && count($this->translations_field)) {
Translation::deleteAll([
'class_name' => self::className(),
'entity_id' => $this->id,
]);
foreach ($this->translations_field as $language_code => $translation_field) {
(new Translation([
'class_name' => self::className(),
'entity_id' => $this->id,
'language_code' => $language_code,
'title' => $translation_field['title'],
]))->save();
}
}
}
/**
* @param $name
* @return static[]
*/
public static function findList($name)
{
return static::find()
// @todo lower index
->where(['like', 'lower(name)', mb_strtolower($name)])
->limit(10)
->asArray()
->all();
}
}
Re: Tree gridview
А вот так можно с бесконечным уровнем вложенности делать дерево:
Код: Выделить всё
<?php
namespace common\helpers;
use Yii;
/**
* Class ReviewHelper
* @package common\helpers
*/
class ReviewHelper
{
/**
* @param $models
* @return mixed
*/
public static function buildNestedTree($models)
{
$max_depth = 1;
foreach($models as $model) {
if($model['depth'] > $max_depth) {
$max_depth = $model['depth'];
}
}
if($max_depth == 1) {
return $models;
}
for ($depth = $max_depth; $depth >= 2; $depth--) { // глубина от самой большой к малой
$models = array_values($models);
for ($i = count($models) - 1; $i >= 0; $i--) { // все подряд от конца к началу
if(isset($models[$i]) && $models[$i]['depth'] == $depth) {
for($j = $i - 1; $j >= 0; $j--) { // только предыдущие (ищем родителя)
if(isset($models[$j]['depth']) && isset($models[$i]['depth']) && $models[$j]['depth'] < $models[$i]['depth']) {
if(!isset($models[$j]['children'])) {
$models[$j]['children'] = [];
}
array_unshift($models[$j]['children'], $models[$i]);
unset($models[$i]);
break;
}
}
}
}
}
return $models;
}
}