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. хорошим тоном считается выкладывать исходники
вот они

Комментарии

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

Автоматический вход пользователя на сервер посредством putty (протокол SSH)

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

WSL