製作可排序、搜尋的View

我們的清單頁面現在長這個樣子,作為一個內容管理系統,我們會希望元件能有強大的搜尋、排序等功能,且資料量多的時候可以分頁。

p-2015-01-17-1.jpg

接下來將會一步一步的把這些功能實作出來。

Search 搜尋功能

先建立搜尋框

我們把搜尋框放在 Articles View 的 default.php 內,這個搜尋框我們預先採用 Bootstrap 的格式建立好了。

<?php
// administrator\components\com_blog\views\articles\tmpl\default.php

defined('_JEXEC') or die;
?>
<form action="<?php echo JUri::getInstance(); ?>" id="adminForm" name="adminForm" method="post">

    <div class="filter-bar">
        <div class="btn-wrapper input-append">
            <input type="text" name="filter_search" id="filter_search" value="" placeholder="搜尋">
            <button type="submit" class="btn">
                <i class="icon-search"></i>
            </button>
        </div>
    </div>

    <!-- 下略 -->
 

應該會長這樣

p-2015-01-17-2.jpg

然後我們在 Articles Model 中加入 populateState() 方法,並在 getItems() 中增加 Search 的 query:

// administrator\components\com_blog\models\articles.php

use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticles extends BaseDatabaseModel
{
    protected function populateState()
    {
        $app = Factory::getApplication();
        $input = $app->input;

        // 我們把 filter_search 從 request 中拿出來,暫存在 filter.search 的 state 中
        $this->setState('filter.search', $input->getString('filter_search'));
    }

    public function getItems()
    {
        $db = $this->_db;

        $query = $db->getQuery(true);

        // 接下來從 state 中把 search 內容拿出來
        $search = $this->getState('filter.search');

        // 如果有的話,就加上 LIKE 的 SQL 來做搜尋
        if ($search)
        {
            $query->where('title LIKE "%' . $search . '%"');
        }

        $query->select('*')
            ->from('#__blog_articles')
            ->order('id ASC');

        $db->setQuery($query);

        // If not thing found, return empty array.
        return $db->loadObjectList() ? : array();
    }
}
 

我們把 filter_search 從 request 中拿出來,暫存在 filter.search 的 state 中。Model 的 State 是一種狀態,我們可以隨時改變這個狀態內儲存的值,就能改變Model 的行為。

現在你可以到搜尋頁面搜尋「流」看看,應該只會出現一筆資料:

p-2015-01-17-3.jpg

但搜尋完以後我的搜尋文字沒有重新出現在搜尋框內阿,這樣怎麼知道之前搜尋了什麼呢?那麼我們就加上下面這行,把 State 拿出來用

// administrator\components\com_blog\views\articles\view.html.php

use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticles extends BaseDatabaseModel
{
    public function display($tpl = null)
    {
        // 加上下面這行,把 State 拿出來用
        $this->state = $this->get('State');
        $this->items = $this->get('Items');

        $this->addToolbar();

        parent::display($tpl);
    }
 

還有在 template 檔案中替 input 加上 value,value 內容就是拿出 State 內的 filter.search。

<?php
// administrator\components\com_blog\views\articles\tmpl\default.php

defined('_JEXEC') or die;
?>
<form action="<?php echo JUri::getInstance(); ?>" id="adminForm" name="adminForm" method="post">

    <div class="filter-bar">
        <div class="btn-wrapper input-append">
            <input type="text" name="filter_search" id="filter_search" value="<?php echo $this->state->get('filter.search'); ?>" placeholder="搜尋">
            <button type="submit" class="btn">
                <i class="icon-search"></i>
            </button>
        </div>
    </div>
 

現在搜尋結果就會出現之前的輸入文字了

p-2015-01-17-4.jpg

如何使用 MySQL LIKE 模糊搜尋

一次搜尋多個欄位

我們可以用 OR 把多個欄位的搜尋組合起來,這樣一個字串就能搜尋不同欄位,而不再只限定 title 而已

// administrator\components\com_blog\models\articles.php

if ($search)
{
    $conditions = '(`title` LIKE "%' . $search . '%"';
    $conditions .= ' OR `introtext` LIKE "%' . $search . '%"';
    $conditions .= ' OR `fulltext` LIKE "%' . $search . '%")';

    $query->where($conditions);
}
 

搜尋內文的結果

p-2015-01-17-5.jpg

fulltext 一定要用反引號 ( `) 包起來,不然會跟 MySQL 的保留字衝突

讓瀏覽器保留搜尋條件

現在還有一個問題,如果我重新整理,或是重新點近這個頁面,搜尋結果會消失。如果你希望搜尋條件能夠保存在瀏覽器中,直到你清空搜尋條件為止。我們可以改用 Session 來記錄搜尋欄位。使用方法很簡單,把原本從 input 拿search字串的方法,改用 $app->getUserStateFromRequest() 來取值,這個功能會自動從 request 拿資料並存放到 session,如果下一次發現 request 內沒有值的時候,就會拿 session 內的當預設值,如果 request 有值的時候,就會覆蓋掉 session。

// administrator\components\com_blog\models\articles.php

use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticles extends BaseDatabaseModel
{
    protected function populateState()
    {
        $app = Factory::getApplication();

        $this->setState('filter.search', $app->getUserStateFromRequest('blog.articles.search', 'filter_search'));
    }

    // ...
 

那個 blog.articles.search 是 session 的儲存 key最好加上元件名稱以免跟其他元件的 session 資料衝突。

現在你可以離開頁面再回來看看,之前的搜尋內容應該會被保留住,搜尋空值則會清空搜尋條件恢復正常。

排序

接下來我們要建立表格上的排序功能,請一步一步照著操作。

在 view template 把表格的 <th> 都改用 sort 功能,最下方的 hidden inputs 加上 filter_orderfilter_order_Dir 兩個欄位。

<?php
// administrator\components\com_blog\views\articles\tmpl\default.php

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\String\StringHelper;

defined('_JEXEC') or die;
?>
    <!-- ... -->

    <table class="table table-striped">
        <thead>
        <tr>
            <!-- 改用 JHtmlGrid::sort() -->
            <th><?php echo HTMLHelper::_('grid.sort', 'ID', 'id'); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Title', 'title'); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Intro', 'introtext'); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Delete', 'delete'); ?></th>
        </tr>
        </thead>

    <!-- ... -->

    <div class="hidden-inputs">
        <input type="hidden" name="option" value="com_blog" />
        <input type="hidden" name="task" value="" />

        <!-- 加入 hidden inputs -->
        <input name="filter_order" type="hidden" value="" />
        <input name="filter_order_Dir" type="hidden" value="" />
    </div>
</form>
 

接著更改 Articles Model

// administrator\components\com_blog\models\articles.php

use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticles extends BaseDatabaseModel
{
    protected function populateState()
    {
        $app = Factory::getApplication();

        $this->setState('filter.search', $app->getUserStateFromRequest('blog.articles.search', 'filter_search'));

        // 加入這兩個 state
        $this->setState('list.ordering', $app->getUserStateFromRequest('blog.articles.ordering', 'filter_order'));
        $this->setState('list.direction', $app->getUserStateFromRequest('blog.articles.direction', 'filter_order_Dir'));
    }

    public function getItems()
    {
        $db = $this->_db;

        $query = $db->getQuery(true);

        // 把 order 用的 state 拿出來(第二個參數是不存在時的預設值)
        $ordering   = $this->getState('list.ordering', 'id');
        $direction = $this->getState('list.direction', 'asc');

        $search     = $this->getState('filter.search');

        if ($search)
        {
            $conditions = '(`title` LIKE "%' . $search . '%"';
            $conditions .= ' OR `introtext` LIKE "%' . $search . '%"';
            $conditions .= ' OR `fulltext` LIKE "%' . $search . '%")';

            $query->where($conditions);
        }

        $query->select('*')
            ->from('#__blog_articles')
            // 放進 Query Builder 的 order() 中
            ->order($ordering . ' ' . $direction);

        $db->setQuery($query);

        // If not thing found, return empty array.
        return $db->loadObjectList() ? : array();
    }
}
 

現在可以排序了,我們去瀏覽器上按下 ID 看看,會發現成功變成 3, 2, 1 這樣的反向排序。

p-2015-01-17-6.jpg

不過我們無法再次按回正常排序,而且箭頭的方向也是錯誤的,因為還少了一些動作,我們把這些程式補上去。

在 view template 中拿出 ordering 與 direction 並一個一個塞到 sort() 中:

<?php
// administrator\components\com_blog\views\articles\tmpl\default.php

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\String\StringHelper;

defined('_JEXEC') or die;

// 取得目前的排序狀態
$currentOrder = $this->state->get('list.ordering', 'asc');
$currentDir   = $this->state->get('list.direction', 'asc');
?>
<form action="<?php echo JUri::getInstance(); ?>" id="adminForm" name="adminForm" method="post">

    <!-- ... -->

    <table class="table table-striped">
        <thead>
        <tr>
            <th><?php echo HTMLHelper::_('grid.sort', 'ID', 'id', $currentDir, $currentOrder); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Title', 'title', $currentDir, $currentOrder); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Intro', 'introtext', $currentDir, $currentOrder); ?></th>
            <th><?php echo HTMLHelper::_('grid.sort', 'Delete', 'delete', $currentDir, $currentOrder); ?></th>
        </tr>
        </thead>

    <!-- ... -->

    <div class="hidden-inputs">
        <input type="hidden" name="option" value="com_blog" />
        <input type="hidden" name="task" value="" />

        <!-- 也塞到 hidden inputs 中才能保留結果 -->
        <input name="filter_order" type="hidden" value="<?php echo $currentOrder; ?>" />
        <input name="filter_order_Dir" type="hidden" value="<?php echo $currentDir ?>" />
    </div>
</form>
 

現在可以嘗試看看,每個欄位都可以正反向排序。

p-2015-01-17-7.jpg

其實到目前為止,我們做的事情只要理解原理都不困難。其實就是從 list 頁面把想要的搜尋排序等功能用 Form post 到 Model 中,然後重新組合我們的 SQL 查詢而已。因為有了 Query Builder,我們組合 SQL 的過程變得較為輕鬆。而排序功能則是因為 Joomla 幫我們處理好前端 UI 的排序辨認,讓我們可以專注在編寫 Model 內的邏輯就好。


本章節原始碼可以在 GitHub 上找到: Code / Commits

ukash birim çevirme saç ekimi estetik