Как сделать FULLTEXT поиск в CMS на CodeIgniter

В этом уроке мы рассмотрим как сделать поиск по сайту в Code-Igniter.

1. Подготовка CodeIgniter.
Скачиваем и распаковываем дистрибутив CodeIgniter в какую-нибудь папку на вашем сервере. Следующим шагом создаем базу данных, пользователя с правами доступа к этой БД и прописываем настройки подключения в application/config/database.php.
Открываем application/config/config.php и выставляем настройку поля base_url.
2. Настройка Базы данных.
БД мы уже создали в первом шаге, но у нас нет таблиц. Создадим простую таблицуpages.
CREATE TABLE pages (
  id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  url text NOT NULL,
  title text NOT NULL,
  content text NOT NULL,
  updated datetime NOT NULL,
  PRIMARY KEY  (id),
  FULLTEXT KEY content (content)
) ENGINE=MyISAM

При использовании FULLTEXT необходимо указать все поля по которым мы будем производить поиск. В данной таблице поиск будет производиться только по полю content. Обратите внимание, что FULLTEXT поиск работает только с использованием MyISAM engine.
3. Тестовые данные
Для тестирования нашего поискового механизма в таблицу необходимо добавить некоторое количество тестовых данных. Я решил что Википедия может стать хорошим донором для нашей БД. Я написал небольшой скрипт, который берет несколько последних статей и потом с помощью экспорта Википедии заносит эти статьи в БД.
4. Поиск по базе
Для поиска мы будем использовать SQL запрос вида:

SELECT *
FROM pages
WHERE MATCH (content) AGAINST ('test') > 0

При использовании FULLTEXT поиска необходимо помнить о нескольких ограничениях.

Длина запроса должна превышать 3 символа, иначе поиск вернет пустой результат.
Существует набор стоп слов который MySQL игнорирует при поиске (‘the’, ‘however’, ‘hello’). Если вы попытаетесь искать по этим словам, результат поиска будет пустым.
Существует несколько моделей поиска (например boolean), а также различные операторы используемые при поиске. Более подробно это можно рассмотреть вдокументации.
Также доступен расширенный поиск. Полную информацию можно найти в документации к MySQL.
5. CodeIgniter и MVC паттерн.
Вернемся к нашему фреймворку. CodeIgniter предоставляет популярный паттерн проектирования модель-вид-контроллер (MVC model-view-controller).
Напомню основные правила:

Все изменения и работа с БД производиться из модели
Ничего не может быть выведено пользователю из контроллера или модели
Только одно выражение может быть внутри тегов сокращенную запись PHP)
Если вы нарушаете какое-либо из этих правил, то стоит вернуться назад и еще раз подумать над структурой вашего проекта.
Начнем разработку поиска с файла модели. Я использую только один метод, который выполняет поиск. Вы можете заметить, что здесь использован «прямой SQL запрос», хотя в CodeIgniter есть ActiveRecord позволяющий упростить формирование запросов.
Ниже представлен код модели располагающейся в application/models/page_model.php
class Page_model extends Model
{
    function Page_model()
    {
        parent::Model();
 
        // Делаем доступной БД во всех функциях
        $this->load->database();
    }
 
    function search($terms)
    {
        // Выполнение запроса и возврат результата
        $sql = "SELECT url, title
                    FROM pages
                    WHERE MATCH (content) AGAINST (?) > 0";
        $query = $this->db->query($sql, array($terms, $terms));
        return $query->result();
    }
}

Следующим шагом создадим файл вида:
<?php $this->load->helper('form'); ?>
 
<?php echo form_open($this->uri->uri_string); ?>
<?php echo form_label('Search:', 'search-box'); ?>
<?php echo form_input(array('name' => 'q', 'id' => 'search-box', 'value' => $search_terms)); ?>
<?php echo form_submit('search', 'Search'); ?>
<?php echo form_close(); ?>
 
<?php if ( ! is_null($results)): ?>
    <?php if (count($results)): ?>
        <ul>
        <?php foreach ($results as $result): ?>
            <li><a href="<?php echo $result->url; ?>"><?php echo $result->title; ?></a></li>
        <?php endforeach ?>
        </ul>
    <?php else: ?>
        <p><em>There are no results for your query.</em></p>
    <?php endif ?>
<?php endif ?>

Теперь пришла очередь создать файл контроллера, который свяжет модель и вид:
class Pages extends Controller {
 
    function search($search_terms = '')
    {
        // Если форма отправлена перепишем URL добавив строку запроса
        // обратите внимание, что с некоторыми символами 
        // могут быть проблемы.
        if ($this->input->post('q'))
        {
            redirect('/pages/search/' . $this->input->post('q'));
        }
 
        if ($search_terms)
        {
            // Загрузка модели и выполнение поиска по БД
            $this->load->model('page_model');
            $results = $this->page_model->search($search_terms);
        }
 
        // Загрзка файла вида и вывод на экран
        $this->load->view('search_results', array(
            'search_terms' => $search_terms,
            'results' => @$results
        ));
    }
}

Стоит заметить что с некоторыми символами могут возникнуть проблемы (Русские символы в URL сайта на CodeIgniter).
В начале этого метода мы использовали функции redirect. Это функция содержится в хелпере URL. Для использовании этой функции мы производили «ручную» загрузку хелпера с помощью $this->load->helper(‘url’); Т.к. мы будем часто использовать этот хелпер, то проще сразу прописать его в автозагрузку, чтобы не загружать все время его вручную. Для этого редактируем файл application/config/autoload.php. Дописываем в раздел $autoload['helper']:

$autoload['helper'] = array('url');
Теперь мы получили рабочий прототип. Не забудьте, что в вашей БД должны быть данные (не меньше 3 строк). Теперь наберите в вашем браузере что-то типа ‘http://localhost/index.php/pages/search’, введите в строку поиска какой-нибудь текст содержащийся в БД и нажмите поиск.

6. Добавление новых возможностей в наш поиск.
Для начала добавим постраничную навигацию для результатов поиска. Для вывода постраничной навигации в CodeIgniter используется класс Pagination.
Модифицируем нашу модель для вывода постраничной навигации. Для этого нам необходимо задать количество записей выбираемых из БД, номер записи с которой производить выборку, а также получить общее количество записей по данному запросу.
Вот что у нас получилось:
class Page_model extends Model {
 
    function search($terms, $start = 0, $results_per_page = 0)
    {
        // Зададим лимит выбираемых записей 
        //и стартовую позицию
        if ($results_per_page > 0)
        {
            $limit = "LIMIT $start, $results_per_page";
        }
        else
        {
            $limit = '';
        }
 
        // Выполнение SQL запроса
        $sql = "SELECT url, title, content
                    FROM pages
                    WHERE MATCH (content) AGAINST (?) > 0
                    $limit";
        $query = $this->db->query($sql, array($terms, $terms));
        return $query->result();
    }
 
    function count_search_results($terms)
    {
        // Run SQL to count the total number of search results
        $sql = "SELECT COUNT(*) AS count
                    FROM pages
                    WHERE MATCH (content) AGAINST (?)";
        $query = $this->db->query($sql, array($terms));
        return $query->row()->count;
    }
}

Следующим шагом модифицируем наш контроллер для работы с постраничной навигацией
class Pages extends Controller {
 
    function search($search_terms = '', $start = 0)
    {
        // Если форма отправлена перепишем URL добавив строку запроса
        // обратите внимание, что с некоторыми символами 
        // могут быть проблемы.
        if ($this->input->post('q'))
        {
            redirect('/pages/search/' . $this->input->post('q'));
        }
 
        if ($search_terms)
        {
            // Определим сколько результатов 
            //выводить на страницу
            $results_per_page = $this->config->item('results_per_page');
 
            // Загружаем модель, выполняем поиск, определяем
            // сколько всего результатов поиска
            $this->load->model('page_model');
            $results = $this->page_model->search($search_terms, $start, $results_per_page);
            $total_results = $this->page_model->count_search_results($search_terms);
 
            // Загрузка постраничной навигации
            $this->_setup_pagination('/pages/search/' . $search_terms . '/', $total_results, $results_per_page);
 
            // Определяем какие результаты выводить
            $first_result = $start + 1;
            $last_result = min($start + $results_per_page, $total_results);
        }
 
        // Загрузка вида и вывод результатов
        $this->load->view('search_results', array(
            'search_terms' => $search_terms,
            'first_result' => @$first_result,
            'last_result' => @$last_result,
            'total_results' => @$total_results,
            'results' => @$results
        ));
    }
 
    function _setup_pagination($url, $total_results, $results_per_page)
    {
        // Не забываем загрузить постраничную навигацию
        $this->load->library('pagination');
 
 
        $uri_segment = count(explode('/', $url));
 
        // Инициализация постраничной навигации и установка
        // необходимых параметров
        $this->pagination->initialize(array(
            'base_url' => site_url($url),
            'uri_segment' => $uri_segment,
            'total_rows' => $total_results,
            'per_page' => $results_per_page
        ));
    }
}

Давайте рассмотрим что у нас получилось. Мы добавили постраничную навигацию, а значит в URL у нас добавились новые сегменты. Этот сегмент является одновременно страницей результатов поиска и номером для определения стартовой позиции для выборки из БД.
Для задания количества результатов на странице поиска я использовал config library. Лучше всего добавить новый файл в папку application/config. Создадим файл application/config/application.php содержащий строчку:

$config['results_per_page'] = 10;
Добавим его в автозагрузку изменив опять файл application/config/autoload.php и добавим ‘application’ в $autoload['config'] секцию.
Также мы используем методы для обработки выводимой в результатах поиска информации (сниппет и подстветка в сниппете слов из запроса). Эти методы содержаться в нашем хелпере в файле application/helpers/search_helper.php. Первая функция search_highlight($text, $search_terms) выделяет слова из запроса в результатах поиска, вторая search_extract($content, $search_terms, $number_of_snippets = 3, $snippet_length = 60) извлекает из контента часть содержащую заданный в поиске запрос.
Так будет выглядеть наш доработанный файл вида:
<?php $this->load->helper(array('form', 'search')); ?>
 
<?php echo form_open($this->uri->uri_string); ?>
<?php echo form_label('Search:', 'search-box'); ?>
<?php echo form_input(array('name' => 'q', 'id' => 'search-box', 'value' => $search_terms)); ?>
<?php echo form_submit('search', 'Search'); ?>
<?php echo form_close(); ?>
 
<?php if ( ! is_null($results)): ?>
    <?php if (count($results)): ?>
 
        <p>Showing search results for '<?php echo $search_terms; ?>' (<?php echo $first_result; ?>&ndash;<?php echo $last_result; ?> of <?php echo $total_results; ?>):</p>
 
        <ul>
        <?php foreach ($results as $result): ?>
            <li><a href="<?php echo $result->url; ?>"><?php echo search_highlight($result->title, $search_terms); ?></a><br /><?php echo search_extract($result->content, $search_terms); ?></li>
        <?php endforeach ?>
        </ul>
 
        <?php echo $this->pagination->create_links(); ?>
 
    <?php else: ?>
        <p><em>There are no results for your query.</em></p>
    <?php endif ?>
<?php endif ?>


7. Еще больше вкусных фишек.
CodeIgniter предоставляет возможность вывода тестовых данных и различнойдополнительной информации
К пример так можно запустить работу теста для модели
// Mark the start of search
$this->benchmark->mark('search_start');
 
// Load the model, perform the search and establish the total
// number of results
$this->load->model('page_model');
$results = $this->page_model->search($search_terms, $start, $results_per_page);
$total_results = $this->page_model->count_search_results($search_terms);
 
// Mark the end of search
$this->benchmark->mark('search_end');
Теги:
FULLTEXT
Добавлено: 14 Апреля 2018 06:56:39 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...