Yii (дерево страниц с добавлением и удалением элементов)

Забегу вперед, покажу что мы хотим получить


Пойдем по шагам. Подразумевается, что yii у вас установлен и настроен. Если нет, то смотрите как это делается в предыдущих уроках.
1. Создаем таблицу.
Я решил работать с PostgreSQL поэтому DDL такой

CREATE TABLE "SC_Wonder".t_page (
  p_id SERIAL,
  p_title TEXT NOT NULL,
  p_link TEXT,
  p_parent INTEGER,
  p_order INTEGER,
  p_type INTEGER,
  p_comment TEXT
) 
WITH (oids = false);

COMMENT ON TABLE "SC_Wonder".t_page
IS 'таблица страниц';

COMMENT ON COLUMN "SC_Wonder".t_page.p_id
IS 'ид';

COMMENT ON COLUMN "SC_Wonder".t_page.p_title
IS 'название';

COMMENT ON COLUMN "SC_Wonder".t_page.p_link
IS 'ссылка';

COMMENT ON COLUMN "SC_Wonder".t_page.p_parent
IS 'предок';

COMMENT ON COLUMN "SC_Wonder".t_page.p_order
IS 'позиция';

COMMENT ON COLUMN "SC_Wonder".t_page.p_type
IS 'тип';

COMMENT ON COLUMN "SC_Wonder".t_page.p_comment
IS 'комментарий';

DDL for MySQL

CREATE TABLE `t_page` (
  `p_id` INTEGER(11) NOT NULL AUTO_INCREMENT COMMENT 'ид',
  `p_title` TEXT COLLATE utf8_general_ci NOT NULL COMMENT 'название',
  `p_link` TEXT COLLATE utf8_general_ci COMMENT 'ссылка',
  `p_parent` INTEGER(11) DEFAULT NULL COMMENT 'предок',
  `p_order` INTEGER(11) DEFAULT NULL COMMENT 'позиция',
  `p_type` INTEGER(11) DEFAULT NULL COMMENT 'тип',
  `p_comment` INTEGER(11) DEFAULT NULL COMMENT 'комментарий',
  PRIMARY KEY (`p_id`) USING BTREE,
  UNIQUE KEY `p_id` (`p_id`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT='таблица страниц'
;
..
2. Создаем model и crud
3.Дальше пошла магия.
Вот качаем расширение
Кидаем как обычно в ..\protected\extensions
добавляем в main
//config/main.php    
'components' => array(        
        'import' => 'simpletree_widget',
..
4. Систему мы настроили
5.А сейчас пойдет самое интересное
Изменим page/index.php
<?php
/* @var $this PageController */
/* @var $dataProvider CActiveDataProvider */

$this->breadcrumbs = array(
    'Страницы',
);
?>
<?php if (Yii::app()->user->hasFlash('flash')): ?>
    <div class="info">
        <?php echo Yii::app()->user->getFlash('flash'); ?>
    </div>
<?php endif; ?>
<?php
$this->menu = array(
    array('label' => 'Поиск', 'url' => array('admin')),
);
?>
<h1>Страницы</h1>

<?php
$this->widget('CTreeView', array(
    'data' => $dataTree,
    'htmlOptions' => array(
        'id' => 'treeview-categ',
        'class' => 'treeview-red', //there are some classes that ready to use
    ),
));

if (!$dataTree)
    echo CHtml::link('Создать корневой элемент?', array('page/create?new=true'));
?> 
..
6. В модели добавляем функцию dataTree

.
.
public function getModelName()
{
    return __CLASS__;
}

public static function dataTree($buttons = false)
{
    $refs = array();
    $list = array();

    $result = self::model()->findAll(array('order'=>'id_parent ASC, p_order ASC'));
    foreach ($result as $data)
    {
        $thisref = &$refs[$data['id']];
        $thisref['id_parent'] = $data['id_parent'];
        $self = strtolower(self::model()->modelName);  
        
        $button = array(
            'addChild' => CHtml::link(CHtml::image(Yii::app()->baseUrl . '/images/icons/sm2_addChild.png', 'Добавить'),
                    array( $self.'/create', 'id_parent' => $data['id']), array('title' => 'Добавить')),
            'update' => CHtml::link(CHtml::image(Yii::app()->baseUrl . '/images/icons/update.png', 
                    'Изменить'), array($self.'/update', 'id' => $data['id']), array('title' => 'Изменить')),
            'order' => CHtml::link(CHtml::image(Yii::app()->baseUrl . '/images/icons/order.png', 
                    'Сортировать'), array($self.'/order', 'id' => $data['id']), array('title' => 'Сортировать')),
            'delete' => CHtml::ajaxLink(
                    CHtml::image(Yii::app()->baseUrl . '/images/icons/sm2_delete.png', 'delete'), 
                    Yii::app()->createUrl('admin/'.$self.'/delete', array('id' => $data['id'])), array(
                'type' => 'POST',
                'success' => 'function(data) {
                                                                top.location.href="' . Yii::app()->createUrl('admin/'.$self.'/index') . '"; 
                                                }',
                    ), array(
                'href' => Yii::app()->createUrl('admin/'.$self.'/delete', array('id' => $data['id'])),
                'confirm' => 'Уверены?',
                'title' => 'Удалить',
                    )
            ),
        );

        if ($buttons)
        {
            $thisref['text'] = "<span title='{$data['id']}'>" . $data['title'] . "</span>";
            if ($data['id_parent'] == 0)
            {
                $thisref['text'] .= ' ' . $button['addChild'];
                //  $thisref['text'] .= ' ' . $button['order'];
            }
            //  if ($data['id'] > 1)
            if ($data['id_parent'] > 0)
            {
                $thisref['text'] .= ' ' . $button['addChild'];
                $thisref['text'] .= ' ' . $button['update'];
                $thisref['text'] .= ' ' . $button['delete'];
            }
        }
        else
        {
            $thisref['text'] = $data['title'];
        }
        $thisref['id'] = $data['id'];

        if ($data['id_parent'] == 0)
        {
            $list[$data['id']] = &$thisref;
        }
        else
        {
            $refs[$data['id_parent']]['children'][$data['id']] = &$thisref;
        }
    }
    return $list;
}
.
.
иконки здесь
кидаем в \images всю папку


7.В контроллере переопределяем функцию индекс
    public function actionIndex()
    {
        $dataProvider=new CActiveDataProvider('Page');
        $this->render('index',array(
            //'dataProvider'=>$dataProvider,
            'dataTree' => Page::dataTree(true),
        ));
    }
..
8.Поменяем _form
<?php
/* @var $this PageController */
/* @var $model Page */
/* @var $form CActiveForm */
?>

<div class="form">

    <?php
    $form = $this->beginWidget('CActiveForm', array(
        'id' => 'page-form',
        // Please note: When you enable ajax validation, make sure the corresponding
        // controller action is handling ajax validation correctly.
        // There is a call to performAjaxValidation() commented in generated controller code.
        // See class documentation of CActiveForm for details on this.
        'enableAjaxValidation' => false,
    ));
    ?>

    <p class="note">Fields with <span class="required">*</span> are required.</p>

    <?php echo $form->errorSummary($model); ?>

    <div class="row">
        <?php echo $form->labelEx($model, 'title'); ?>
        <?php echo $form->textArea($model, 'title', array('rows' => 6, 'cols' => 50)); ?>
        <?php echo $form->error($model, 'title'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'link'); ?>
        <?php echo $form->textArea($model, 'link', array('rows' => 6, 'cols' => 50)); ?>
        <?php echo $form->error($model, 'link'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'id_parent'); ?>
        <?php
        if ($_GET['new'] == 'true')
            $model->id_parent = 0;
        else
            $model->id_parent = $model->idParent->title;

        echo $form->textField($model, 'id_parent', array('disabled' => true));
        ?>
        <?php echo $form->error($model, 'id_parent'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'p_order'); ?>
        <?php echo $form->textField($model, 'p_order'); ?>
        <?php echo $form->error($model, 'p_order'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'type'); ?>
        <?php
        $type = array('1' => 'внешняя ссылка', '2' => 'внутренння ссылка');
        echo $form->radioButtonList($model, 'type', $type, array('separator' => ' &nbsp; ',
            'labelOptions' => array('style' => 'display:inline'), // add this code
        ));
        ?>
        <?php echo $form->error($model, 'type'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'hidden'); ?>
        <?php echo $form->checkBox($model, 'hidden'); ?>
        <?php echo $form->error($model, 'hidden'); ?>
    </div>

    <div class="row">
        <?php echo $form->labelEx($model, 'comment'); ?>
        <?php echo $form->textArea($model, 'comment', array('rows' => 6, 'cols' => 50)); ?>
        <?php echo $form->error($model, 'comment'); ?>
    </div>

    <div class="row buttons">
        <?php echo CHtml::submitButton($model->isNewRecord ? 'Создать' : 'Сохранить'); ?>
    </div>

    <?php $this->endWidget(); ?>

</div><!-- form -->
..
на данном этапе нужно создать корневой элемент
9.Для добавления новых элементов перекроим _form
здесь внимательно.
idParent
это название отношения
    public function relations()
    {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(            
            'idParent' => array(self::BELONGS_TO, 'Page', 'p_parent'),
            'tPages' => array(self::HAS_MANY, 'Page', 'p_parent'),
        );
    }
..
но это не заработает если не изменить метод в контроллере
    public function actionCreate()
    {
        $model = new Page;
        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);
        if (isset($_GET['p_parent']))
            $model->p_parent = $_GET['p_parent'];

        if (isset($_POST['Page']))
        {
            $model->attributes = $_POST['Page'];
            if ($model->save())
            //$this->redirect(array('view','id'=>$model->p_id));
            {
                Yii::app()->user->setFlash('flash', "Успешное создание.");
                $this->redirect(array('index'));
            }
        }
        $this->render('create', array(
            'model' => $model,
        ));
        //
    }

..
надеюсь при нажатии на плюсик вы видите примерно следующее

..

..
10. переходим к удалению
в модели пишем
    public function beforeDelete()
    {
        if ($this->id_parent == NULL)
            throw new CHttpException(400, 'Главную категорию нельзя удалить');

       /* $res = Yii::app()->db2->createCommand()
                ->select('COUNT(*) as cnt')
                ->from('t_page')
                ->where('p_parent=:id', array('id' => $this->p_id))
                ->queryRow();*/
        $count = Page::Model()->count("p_parent=:id", array("id" => $this->id));

        //if ($res['cnt'] > 0)
        if ($count > 0)
            throw new CHttpException(400, 'Можно удалить только пустую категорию.');

        return parent::beforeDelete();
    }
..

в контроллере
public function actionDelete($id)
{        
    if (Yii::app()->request->isPostRequest)
    {
        // we only allow deletion via POST request
        try
        {
            $this->loadModel($id)->delete();
            Yii::app()->user->setFlash('flash', "Успешное удаление.");
        }
        catch (Exception $e)
        {
            Yii::app()->user->setFlash('flash', $e->getMessage());
        }
    }
    else
        throw new CHttpException(400, 'Invalid request. Please do not repeat this request again.');
}
..
11.апдейт. практически ничего не меняем
public function actionUpdate($id)
{
    $model = $this->loadModel($id);
    // Uncomment the following line if AJAX validation is needed
    // $this->performAjaxValidation($model);
    if (isset($_POST['Page']))
    {
        $model->attributes = $_POST['Page'];
        if ($model->save())
        {
            Yii::app()->user->setFlash('flash', "Успешное создание.");
            $this->redirect(array('index', 'id' => $model->p_id));
        }
    }

    $this->render('update', array(
        'model' => $model,
    )); 
}
..
12.как вижу вроде все сделано.

13.можно вывести дерево страниц
в column2 пропишем следующее
<?php

$a = Page::model()->findAll();
foreach ($a as $user)
{
    $all[$user->p_parent][] = array(id => $user->p_id,
        text => CHtml::link($user->p_title, Yii::app()->createUrl('site/page', 
                array('view' => 'frameset', 'frame' => $user->p_id))),
        //text=> $user->p_title,
        parent_id => $user->p_parent);
}

function RecursiveTree2(&$rs, $parent)
{
    $out = array();
    if (!isset($rs[$parent]))
    {
        return $out;
    }
    foreach ($rs[$parent] as $row)
    {
        $chidls = RecursiveTree2($rs, $row['id']);
        if ($chidls)
        {

            if ($row['parent_id'] == 0)
            {
                $row['toggle'] = false;
                $row['expanded'] = true;
                $row['children'] = $chidls;
                $row['text'] = '';
            }
            else
            {
                $row['expanded'] = false;
                $row['children'] = $chidls;
            }
        }
        $out[] = $row;
    }
    return $out;
}

$this->widget('CTreeView', array('data' => RecursiveTree2($all, 0),
    'htmlOptions' => array('class' => 'treeview-red')));
получили примерно следующее


..

..
14. хорошим тоном считается выкладывать исходники
вот они

Популярные сообщения из этого блога

Пишем логи на C# (.NET). Легкий способ.

Средства для работы с базой данный PostgreSql

Авторизация yii 2 из базы