Codeigniter

Создание движка на Codeigniter 3 + HMVC. Часть 4. Создание модуля category

Приветствую всех новых гостей моего сайта а особенно постоянных читателей серии статей по созданию движка на CI3! Сегодня мы будем писать еще один важный модуль, без которого не обойдется ни один движок. Да, это модуль категории - он очень похож на модуль page, который мы программировали в предыдущий раз.

Наши действия и результат получим следующий:

image description
  • Сформированную и продуманную таблицу в бд для категорий сайта
  • Рендинг категорий в отдельном шаблоне
  • Вывод вложенных категорий
  • Вывод вложенных страниц категории
  • Создание пагинации на bootstrap, для объемных разделов
  • Проверку на существование страницы
  • Формирование мета данных, для сео продвижения и передачу данных общему шаблону
  • Вывод раздела по адресу http://website.name/category/meta_url_category
  • Настройка роутинга codeigniter для обработки модуля

Модуль category. Создание базы данных для всех разделов

Создадим таблицу category и создаем раздел "статьи" и 3 вложенных категории.

--
-- Структура таблицы `category`
--

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(300) NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `position` mediumint(5) DEFAULT NULL,
  `meta_title` varchar(300) DEFAULT NULL,
  `meta_h1` varchar(300) DEFAULT NULL,
  `meta_description` text,
  `meta_keywords` text,
  `meta_url` varchar(130) DEFAULT NULL,
  `content` text,
  `image` varchar(255) DEFAULT NULL,
  `template` varchar(50) DEFAULT NULL,
  `post_status` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;

--
-- Дамп данных таблицы `category`
--

INSERT INTO `category` (`id`, `name`, `parent_id`, `position`, `meta_title`, `meta_h1`, `meta_description`, `meta_keywords`, `meta_url`, `content`, `image`, `template`, `post_status`) VALUES
(1, 'Статьи', 0, 0, NULL, NULL, NULL, NULL, 'articles', '

Это наш новый раздел статьи

', '/assets/category/smile-macbook.jpg', NULL, 1), (2, 'Первый раздел', 1, 0, NULL, NULL, NULL, NULL, 'first', '

Первый подраздел статей

', '/assets/category/smile-macbook.jpg', NULL, 1), (3, 'Второй раздел', 1, 0, NULL, NULL, NULL, NULL, 'second', '

Второй подраздел статей

', '/assets/category/smile-macbook.jpg', NULL, 1), (4, 'Третий раздел', 1, 0, NULL, NULL, NULL, NULL, 'third', '

Третий подраздел статей

', '/assets/category/smile-macbook.jpg', NULL, 1);

Создание структуры модуля категории для движка на CI3

Отлично! У нас есть таблица для категорий - она не сильно отличается от таблицы pages. Разница как вы уже заметили в дополнительном поле parent_id - так мы сможем задавать родителя для категории и создавать сколько угодно уровней вложенности.

Создадим папку category в modules + вложенные папки controllers, models, config.

image description

Исходный код контроллера будет немного сложнее его предшественника модуля страниц. Давайте займемся подготовкой и для начала расширим нашу главную библиотеку main_lib.php новыми функциями: pagination, short_content, substrword. Допишем в конец файла /modules/common/libraries/main_lib.php следующий код


pagination - конфигурация для отображения и вывода пагинации в разделах. Стили заданные в функции готовы для вывода красиво оформленного блока с ссылками на уровень категории. В первой переменной per_page - можно задать с каким количеством страниц(pages) модулю следует работать.

short_content - вот это действительно интересная функция. Я немного расширил ее код, забегая на перед для работы с будущими модулями, но вкратце объясню логику работы:
С параметром $text - мы передаем функции содержание ячейки content таблицы pages. В базе храниться полное содержание страницы и нам нужно удалить все html теги, затем укоротить текст и вывести это все в категорию.

Зачем же тогда там еще два дополнительных параметра $position и $more_tag? В первом параметре мы указываем сколько символов следует оставить для вывода краткого содержания страницы. Если этот параметр не задан - то обрезаем текст до 400 символов, иначе указываем нужное нам число. А функция substword - обрабатывает текст и добавляет троеточие в конце текста для красивого оформления.

Третий параметр позволяет выводить весь текст до заданного нами тега. Работая с движками joomla и Wordpress вы наверняка обращали внимание на кнопку "Подробнее..." или "Разрыв статьи", в разных версиях по разному. Так вот текстовые редакторы типа TinyMCE по нажатию на эту кнопку вставляют закомментированный тег, в нашем случае - это . Скрипт находит в тексте данный кусок и обрезает все символы после указателя, далее текст так же обрабатывается и выводится в разделе. Скриншот для подробного разъяснения:

image description

Великолепная вещь, не правда ли? ;-)

Идем дальше и подготовим модель с запросами к бд для модуля категорий.

class Category_model extends CI_Model {
    
    function __construct()
    {
        parent::__construct();
    }

    //Функция выбирает каталог по meta_url
    function get_category_by_url($url)
    {
        $result = $this->db->where('meta_url', $url)->get('category')->row_array();
        return $result;
    }

    //выбираем все дочерние категории
    function get_child_categories($category_id){
        $result = $this->db->where('parent_id', $category_id)->where('post_status', 1)->get('category')->result_array();
        return $result;
    }

    //выборка всех страниц данного каталога для пагинации
    function get_all_category_page($num, $offset, $catalog_id = null){
        $this->db->select('*');
        if ($catalog_id != null){
            $this->db->where('cat_id', $catalog_id);
        }
        $this->db->where('post_status', 1);
        $this->db->order_by("pages.edited", "DESC");
        $result = $this->db->get("pages", $num, $offset)->result_array();
        return $result;
    }

    //Подсчет количества страницы категории по id для пагинации
    public function count_category_pages($id){
        $this->db->where('cat_id', $id);
        $this->db->from('pages');
        $result = $this->db->count_all_results();
        return $result;
    }

     
}

Стандартная работа active records. Получаем данные отдельной категории или выбираем все дочерние категории указывая parent_id в запросе. Для постраничной навигации нам следует задать указатель начала выборки и количество элементов для вывода. Если мы находимся на 3й странице то необходимо показать элементы с 11 по 15. Эти параметры мы и будем передавать из нашего контроллера category.php Подсчет страниц раздела - так же необходим для постраничной навигации, что бы рассчитать количество страниц для вывода.

Создание пагинации на codeigniter 3 + bootstrap. Исходный код контроллера category.php

Что бы наш движок полноценно функционировал и мы могли выводить адрес сайта и работать с url'ами - подключим в автозагрузку помощник url. Открываем файл /application/config/autoloader.php и добавляем в массив автозагрузки helper - помощник "url"

$autoload['helper'] = array('url');

Ну вот мы и приблизились к моменту истины данного урока. Следующий код я постарался хорошо закомментировать, что бы вам было легче разобраться. В место двух привычных методов класса category мы будем использовать три. Почему? ДА все из за постраничной навигации;) Код немного сложный, я постарался оптимизироваться запросы к бд до минимума, полный вывод категории использует всего 8 запросов - все запросы и время генерации страницы и затрачиваемое время на работу с бд можно посмотреть включив profiler в конструкторе библиотеки main_lib.

class Category extends MX_Controller {

	var $module_name = "category";

	function __construct()
	{
		parent::__construct();
        $this->load->library('common/main_lib');
		$this->load->model('category_model');
	}

    function index(){
        show_404();
    }

    //вывод всех статей в разделе статьи или вывод одной статьи
    function view($url = null){
        //проверяем существует ли раздел
        $this->main_lib->check_isset_page($url, 'category');
        //выбираем все данные о категории
        $category = $this->category_model->get_category_by_url($url);
        //выводим 404 если страница отключена
        if($category['post_status'] == '0'){
            show_404();
        }
        //Определяем массив с мета данными текущей категории
        $data = $this->main_lib->get_meta_data($url, 'category');
        //Формируем содержание категории
        $data["content"] = $this->get_category($category);
        //выводим раздел
        $this->main_lib->render_main_page($data);
    }

    //вывод сгенерированного содержания страницы для общего шаблона по url
    function get_category($category){
        //выбираем все страницы раздела и делаем пагинацию
        $data_pages_links = $this->get_category_pages_links($category);
        //помещаем в массив данные всех страниц раздела
        $category["pages"] = $data_pages_links['pages'];
        //помещаем в массив данные о навигации раздела
        $category['links'] = $data_pages_links['links'];
        //выбираем все дочерние категории
        $category["categories"] = $this->category_model->get_child_categories($category['id']);
        //проверяем не задан ли шаблон для этой страницы
        $category['template'] != '' ? $template = $category['template'] : $template = "category_full.tpl";
        $data = $this->load->view("site/".$template,  $category,  true);
        return $data;
    }

    function get_category_pages_links($category)
    {
        //загружаем библиотеку пагинация
        $this->load->library('pagination');
        //подключаем конфигурацию пагинации bootstrap
        $config = $this->main_lib->pagination();
        //задаем путь раздела для навигации
        $config["base_url"] = base_url().'category/'.$category['meta_url'].'/';
        //задаем какой сегмент url определяет текущий уровень
        $config["uri_segment"] = 3;
        //подсчитываем общее число страниц в разделе для генерации уровней пагинации
        $config['total_rows'] = $this->category_model->count_category_pages($category['id']);
        //выбираем текущее положение пагинации по url или оставим пустой
        $uri_segment = ($this->uri->segment(3)) ? $this->uri->segment(3) : 0;
        //делаем выборку всех страниц для текущего раздела с учетом текущего уровня
        $pages = $this->category_model->get_all_category_page($config["per_page"], $uri_segment, $category['id']);
        //если страницы существуют, подготавливаем краткое содержание страницы используем функцию общей библиотеки
        if (!empty($pages)){
            for($i=0; $imain_lib->short_content($pages[$i]['content']);
            }
        }
        //инициализируем постраничную навигации
        $this->pagination->initialize($config);
        //создаем список ссылок для перехода
        $data['links'] = $this->pagination->create_links();
        //возвращаем массив страниц для вывода в категории
        $data['pages'] = $pages;
        return $data;
    }

}

Перечислять все наши действия - повторяться с комментариями, но вкратце пройдемся:

  • Проверяем наличие страницы в бд и если такой раздел существует получаем все данные по url
  • методом get_category - генерируем содержание $content для вывода в главном шаблоне
  • Get_category_pages_links - не только подготавливает и запускает библиотеку pagination встроенную в codeigniter - но так же делает выборку всех страниц категории. Так же мы проходимся циклом по нашему массиву с выбранными данными и генерируем краткое содержание каждой отдельной страницы.
  • Инициализируем постраничную навигацию и возвращаем данные
  • Элемент links массива $category - содержит в себе сгенерированное отображение постраничной навигации.
  • Элемент categories - содержит в себе список с данными всех дочерних категорий
  • Элемент pages - данные всех страниц отображаемого раздела
  • Генерируем все данные в шаблоне category_full.tpl
  • Отдаем данные на рендинг главного шаблона

Продолжаем работать с созданным ранее на фреймворке bootstrap сайтом и создадим шаблон category_full.tpl

Здесь мы выводим заголовок раздела. Далее проверяем существуют ли у данного раздела дочерние категории, если да, то циклом с красивым оформлением - выводим.

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

В самом конце - изображение раздела и его описание.

Теперь наполним немного наш сайт пустыми тестовыми страницами, указав родительскую категорию - наш главный раздел "Статьи".

INSERT INTO `pages` (`id`, `name`, `cat_id`, `image`, `content`, `meta_title`, `meta_h1`, `meta_description`, `meta_keywords`, `meta_url`, `template`, `created`, `edited`, `post_status`, `short_content`, `author`, `post_viewed`) VALUES
(3, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second2', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (4, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second3', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (5, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second4', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (6, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second5', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (7, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second6', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (8, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second7', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (9, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second8', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (10, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second9', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (11, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second10', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (12, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second11', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1'), (13, 'Еще одна страница', 1, NULL, '

Содержание еще одной страницы

', NULL, NULL, NULL, NULL, 'second12', NULL, '2015-11-06 23:22:55', '2015-11-06 23:23:00', 1, NULL, 'Евгений Поляков', '1');

 

image description

Последний скриншот - это результат нашей с вами работы во всей ее красе) Как мы видим все дочерние разделы выводятся, все статьи раздела отображаются, пагинация работает а сайт уже похож на полноценный блог. У кого возникли проблемы - может скачать исходный код полностью рабочего движка на codeigniter3 + hmvc

Спасибо за внимание и как всегда жду ваших отзывов.

2654 Просмотров Комментариев: 12

Євген

Ответить

Ого, так скоро не чекав :) Дуже дякую, що додаєте код, бо не завжди все виходить з першого разу, а там можна все перевірити. Чи будемо розглядати галерею і адмінку?

Евгений Поляков

Ответить

Да, стараюсь писать когда выпадает свободное время. Галерея и админка для идентичного движка есть и даже больше... Конечно будем писать статьи и расписывать где что лежит, там уже совершенно другой уровень будет;) Нужно предварительно подготовить базу всего движка и потом создавать admin.php в модулях и напишем статьи со временем.

Sergey

Ответить

Спасибо за уроки! Ждём продолжения

Nepalex

Ответить

Спасибо Евгению за статьи. С удовольствием жду выхода статей о создании админки

Nepalex

Ответить

Евгений, возник такой вопрос - на локалке ваше приложение работает отлично, но после переноса приложения на хостинг по всем ссылкам на сайте выдает 404 Page Not Found The page you requested was not found. $config['base_url'] настроен

Nepalex

Ответить

Вопрос выше был решен путем замены нначальных букв названий файлов модели и контроллера на заглавные ( до этого были с маленькой буквы)

Евгений П.

Ответить

NEPALEX, спасибо большое за ценное замечание.

Действительно, в 3й версии фреймворка - имя файлов(контроллеров, библиотек, моделей) должно начинаться с Заглавной буквы. Только что внес данную правку и перезалил исходники на облако, что бы данной ошибки на хостинге ни у кого не возникало.

Так же будем выкладывать живую версию сайта на домене http://ci3.polyakov.co.ua

Павел

Ответить

Спасибо.. Продолжаю учиться..

Сергей

Ответить

Спасибо, Павел. Все очень понятно и на русском

Алексей

Ответить

Всё круто. Всё нравится. Но можно еще добавить вот что: 1. Рассказать о миграциях - работать через PhpMyAdmin это bad practice. Для проверки можно использовать, однако для соблюдения качества кода и удобства развёртывания миграции незаменимы. 2. На уроке 3, вроде, ты создавал класс Settings. Не лучше ли его перенести в autoload и сделать регистром настроек? чтобы можно было юзать настройки так - echo $this->settings->get('sitename');

Иван

Ответить

Спасибо большое за уроки! Подскажите, когда (и ждать ли вообще) выйдут уроки по админке. Больше всего интересует про авторизацию!

Александр Мороз

Ответить

$result = $this->db->get("pages", $num, $offset)->result_array(); В контроллере? А если кто будет писать другой шаблон? Беги в контроллеры, причем не один, и менять все. Не хорошо. Выход конечно есть.

« Предыдущая статьяСоздание движка на CodeIgniter 3 + HMVC. Часть 3. Пишем модуль page Следующая статья »Создание движка на Codeigniter 3 + HMVC. Часть 5. Создание модуля menu