S.Tominoff

Fullstack JavaScript разработчик

Как быстро сделать сайт визитку на Yii или разбираемся и дорабатываем модуль YCM

В этой статье я покажу как можно создать простой сайт-визитку на основе связки Yii Framework + YCM.

Немного предыстории

Вообще впервые YCM мы опробовали на проекте ka73.ru. По началу лично меня немного коробило это расширение - оно прямо-таки заставляет тебя размещать всю логику в моделях, что в конечном итоге оказалось довольно таки удобно.

 

Главный недостаток этого модуля в том, что он работает только с фиксированными виджетами - только посмотрите на этот метод.

Уже целых 360 СТРОК ДЛЯ КОНСТРУКЦИИ switch!!!

 

 

И это далеко не предел - как только становятся нужны кастомные виджеты, этот свитч будет расти и расти и расти. Лично мое терпение лопнуло, когда после внедрения виджета мультиаплоадинга, мне понадобилось добавить в админку еще парочку виджетов. Именно по этой причине я и форкнул этот модуль и добавил туда возможность создавать свои кастомные виджеты.

Однако это тянет на отдельную публикацию, в этом же материале, давайте сосредоточимся на главном - как быстро сделать сайт визитку?

 

Так как сайт будет максимально простым, я буду использовать для его разработки оригинальный модуль YCM от Jani Mikkonen.

 

Шаг 1. Подготовка.

Сперва стоит спроектировать базу данных для проекта. Для примера я возьму наипростейшую схему:

у нас будут такие сущности - Страница, Меню, Пользователь.

 

С помощью mysql workbench рисуем простую er-диаграмму (либо создаем в phpMyAdmin напрямую):

 

Далее преобразуем все это в сущности базы данных с помощью synchronize model, либо выгрузки и импортирования sql кода.

 

 

Затем нам понадобится Yii Framework (процесс установки и настройки см. на их сайте), и соответствено модуль YCM (ссылки см. выше).

Как только приложение будет сгенерировано и настроено можно приступать к разработке.

 

Шаг 2. Подключение YCM.

С помощью инструмента Gii генерируем необходимые модели для приложения:

 

Как только сгенерились все необходимые модели, можно приступать к подключению YCM. 

Для этого в файле protected/config/main.php, в разделе modules надо добавить следующие строчки:

'ycm'=>array(
      'username'=>'YOUR USERNAME', // логин администратора
      'password'=>'YOUR PASSWORD', // пароль администратора
      'registerModels'=>array(
           // пути к моделям с которыми будет работать YCM
          'application.models.Page',
          'application.models.MenuItem'        
      )
),

 

После этой нехитрой операции на сайте станет доступен адрес http://<sitename>/ index.php/ycm.

 

 

На этом этап подключения YCM завершен, и можно приступить к разработке моделей.

 

Шаг 3. Модели

 

Вообще YCM по умолчанию пытается добавить виджеты исходя из типа столбца, т.е. если у нас в таблице title это varchar, то будет генерироваться виджет TextField, а если content это text, то генериться будет wysiwyg редактор текста.

 

Тут в принципе все понятно, но в нашем случае для модели Page будут также создаваться поля Date Create и Date Update, что не всегда желательно.

 

Поэтому стоит озадачиться ручной настройкой виджетов.

 

Page

Для этого нужно в класс модели Page (protected/models/Page.php) добавить метод attributeWidgets:

    // метод возвращает хэшмассив с настройками виджетов для каждого поля
    public function attributeWidgets() {
        return array(
            array('id_page', 'hide'), // скрывает поле
            array('title', 'text'), // текстовая строка
            array('description', 'wysiwyg'), // редактор текста
            array('content', 'wysiwyg'),
            array('date_create', 'hide'),
            array('date_update', 'hide')
        );
    }

 

Модифицировав таким образом модель мы получаем такое представление:

 

Создаем страницу с помощью этой формы и видим такую фигу:

 

 

Так происходит потому что в модели нет данных для списка. Чтобы исправить положение сделаем следующие добавления в модель Page:

    public function adminSearch() {
        return array(
            'columns'=>array(
                'id_page',
                'title',
                'date_create',
                'date_update'
            ),
        );
    }

 

 

Метод adminSearch дает модулю YCM понять какие поля нужно выводить в списке материалов. В нашем случае будут выводиться поля - ид, заголовок, дата создания, дата редактирования.

 

Дата создания/сохранения будут пустыми, т.к. мы не создали автозаполнение для этих полей.

 

Для решения этой проблемы можно пойти двумя путями - использовать для этого встроенный класс поведения CTimestampBehavior, или реализовать это в методе beforeSave руками.

Вот как это делается с CTimestampBehavior:

 

    public function behaviors() {
        return array(
            'CTimestampBehavior' => array(
                'class' => 'zii.behaviors.CTimestampBehavior',
                'updateAttribute' => 'date_update',
                'createAttribute' => 'date_create',
                'timestampExpression' => 'date("Y-m-d H:i:s")'
            )
        );
    }

 

Теперь попробуем отредактировать страницу, добавить еще одну и посмотрим что получится:

 

 

Ну и напоследок, думаю, не лишним будет метод getUrl(), который бы возвращал фиксированный url для страниц:

    // возвращает абсолютный или относительный url в зависимости от параметра $absolute
    public function getUrl($absolute = false) {
        if($absolute == true)
            return Yii::app()->createAbsoluteUrl('page/view', array('id' => $this->id_page));
        return Yii::app()->createUrl('page/view', array('id' => $this->id_page));
    }

 

 

 

Соответственно неплохо бы также расширить метод adminSearch, чтобы сразу выводить ссылки на эти страницы:

array(
    'type' => 'raw',
    'value'=> 'CHtml::link("Открыть",$data->getUrl(true), array("target" => "_blank")'
)

 

MenuItem

В принципе, тут все тоже самое, и единственное, что мы попробуем сделать, так это не ручное указание url для элементов меню, а выбор страницы из выпадающего списка.

 

Для этого нужно создать уже знакомый нам метод attributeWidgets :

    public function attributeWidgets() {
        return array(
            array('id_menu_item', 'hide'),
            array('menu_title', 'text'),
            array('menu_url', 'chosen')  // тот самый выпадающий список
        );
    }

 

Помимо этого, список menu_url нужно еще чем-то заполнить, поэтому нужно добавить метод menu_itemChoices :

    // в YCM для насыщения chosen данными нужно реализовывать метод названиеАттрибутаChoices 
    public function menu_urlChoices() {
        $pages = Page::model()->findAll();
        $urls = array();
        foreach($pages as $page) {
            $urls[$page->getUrl()] = $page->title;
        }
        return $urls;
    }

 

Таким образом мы получаем список всех страниц сайта и возвращаем их в виде хэш-массива url_страницы => заголовок_страницы.

 

Шаг 4. Frontend

Для разработки фронтенда будем использовать стандартные возможности Yii Framework, а именно контроллеры(controller) и вьюхи(view).

 

Вообще для этого можно использовать инструмент Gii, но так как большинство генерируемых вьюх и экшенов(action) для этого сайта нам не понадобятся, предлагаю просто написать все это вручную. (тем меньше кода придется выпиливать)

 

Сперва набросаем простейший контроллер (protected/controllers/PageController.php):

    // контроллер страниц
    class PageController extends Controller {
        // шаблон для вьюх
        public $layout = 'main';
        // основной экшен
        public function actionView($id = null) {
            $model = $this->loadModel($id);
            // установим заголовок страницы
            $this->pageTitle = $model->title;
            $this->render('view', array(
                'model' => $model
            ));
        }
        // загружает модель из бд, либо бросает ошибку 404
        private function loadModel($id) {
            $model = Page::model()->findByPk($id);
            if($model == null)
                throw new CHttpException(404);
            return $model;
        }
    }

 

Тут ничего сверх-естественного нет - когда пользователь запрашивает такой адрес http://sitename.ru/page/view/id/2, то получает страницу 2, а если, допустим страницы с id_page = 2 не существует, то получает "от ворот поворот" в виде 404 ошибки.

 

Теперь нужно создать шаблон для вьюх. Я не стану приводить полный html, но покажу основные моменты.

В общем виде layout представляет собой нечно вроде такого (protected/views/layouts/main.php):

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title><?php echo $this->pageTitle; ?></title>
    <!-- тут стили и скрипты -->
</head>
<body>
    <div id="header">
        <?php echo CHtml::link("Сайт-визитка", Yii::app()->getBaseUrl()); ?>
    </div>
    <div id="content">
        <?php echo $content; ?>
    </div>
    <div id="footer">
        <p>Сайт визитка, разработанный за час!</p>
    </div>
</body>
</html>

 

Так вот, основной момент в лэйауте - переменная $content, в ней содержится отрендеренная вьюха. Переменная $this в любой вьюхе обычно является экземпляром текущего контроллера.

 

Теперь набросаем простую верстку для вьюхи (protected/views/page/view.php) :

<h1><?php echo $model->title; ?></h1>
<p><?php echo $model->description; ?></p>
<p><?php echo $model->content; ?></p>

 

Вот и весь процесс! Теперь перейдя по ссылке http://sitename.ru/page/view/1 вы увидите нечто вроде такого:

 

 

Навигация

Да, чуть не забыл - у любого уважающего себя сайта всегда есть навигация. В Yii это реализуется достаточно просто - мы можем использовать встроенный класс CMenu для этой цели, однако придется добавить еще один метод в модель MenuItem.

    // подгонка под формат CMenu
    public static function getItems() {
        $menuItems = self::model()->findAll();
        $items = array();
        foreach($menuItems as $menuItem) {
            $items[] = array(
                'label' => $menuItem->menu_title,
                'url' => $menuItem->menu_url
            );
        }
        return $items;
    }

 

Соответственно теперь можно использовать виджет CMenu в любом удобном месте, например, в лэйауте:

    <div id="header">
        <?php echo CHtml::link("Сайт-визитка", Yii::app()->getBaseUrl()); ?>
        <?php $this->widget('zii.widgets.CMenu', array(
            'items' => MenuItem::getItems()
        )); ?>
    </div>

 

А вот и результат, которого мы добились:

 

 

В принципе на этом можно и закончить, дальше остается только внедрить верстку и подключить css и js файлы, и очередной сайт визитка будет готов!