編輯頁面與表單

接下來,我們要建立單數的 Article 編輯頁面,這樣才能讓單篇文章被編輯。

建立單數 MVC

先從 View 開始,放在 views/article 下面:

<?php
// administrator\components\com_blog\views\article\view.html.php

use Joomla\CMS\MVC\View\HtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;

defined('_JEXEC') or die;

class BlogViewArticle extends HtmlView
{
    public function display($tpl = null)
    {
        $this->addToolbar();

        parent::display($tpl);
    }

    public function addToolbar()
    {
        ToolbarHelper::title('Article Edit', 'pencil');

        ToolbarHelper::save('save');
    }
}
 
<?php
// administrator\components\com_blog\views\article\tmpl\edit.php

defined('_JEXEC') or die;
?>
<h1>Article Edit</h1>
 

與 Articles 相差不多,但我們先不取任何資料,然後在 addToolbar() 內加上 Title 與 save 按鈕,至於 Template 部分,則改成 edit.php (別再用 default 囉)。

接著建立一個空的 Article Model:

<?php
// administrator\components\com_blog\models\article.php

use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticle extends BaseDatabaseModel
{
}
 

Controller 必須稍作修改,支援動態改變 View 與 Template

<?php
// administrator\components\com_blog\controller.php

use Joomla\CMS\MVC\Controller\BaseController;

defined('_JEXEC') or die;

class BlogController extends BaseController
{
    protected $default_view = 'articles';

    public function display($cachable = false, $urlparams = array())
    {
        $view = $this->input->get('view', $this->default_view);
        $layout = $this->input->get('layout', 'default');

        // Get model & view
        $model = $this->getModel($view);
        $view = $this->getView($view, 'html');

        // Push model into view
        $view->setModel($model, true);

        $view->setLayout($layout);

        $view->display();
    }
}
 

在 controller 中,我們用 input 拿出網址參數中的 view 值,藉由這個值決定要選擇哪個 view,另外當網址沒有提供 view 時,在給予一個預設值 articles。接著我們又取得 layout 的值,set 進 view 中,layout 名稱就是 template 名稱。

現在我們得要想辦法進入這個頁面,可以直接輸入 {SITE}/administrator/index.php?option=com_blog&view=article&layout=edit ,就能看到了

p-2014-09-02-15.jpg

建立按鈕導向到編輯頁面

要讓使用者別這麼麻煩輸入網址,我們就需要導向連結。Joomla 提供了一組標準化的按鈕集,我們可以善用這個按鈕集來做事情。

首先,回到 Articles View,我們在 addToolbar() 中加上新增按鈕,第一個參數 add 代表按鈕要送出的 task 值。

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

// ...

public function addToolbar()
{
    ToolbarHelper::title('Articles');

    ToolbarHelper::addNew('add');
}
 

接著要在頁面加上 form,這個按鈕才能作用,更改 default template,加上 form 元素並將 id 與 name 都命名為 adminForm,這樣 Joomla 就會自動操作這個 form。

<?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">
    <table class="table table-striped">
        <!-- ... -->
    </table>

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

注意一下最下方還加上了兩個 Hidden inputs,每個頁面都要有 option 與 task 兩個 hidden inputs,這樣 post 出去時,才能正確導向我們想要的 Blog 元件。其中 task input 一旦缺少了,所有按鈕都會無法運作。

接下來,在 controller 內加上 add 這個 task,在 add() 之中,我們讓系統 Redirect 到剛剛寫的單數 view:

<?php
// administrator\components\com_blog\controller.php

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;

defined('_JEXEC') or die;

class BlogController extends BaseController
{
    protected $default_view = 'articles';

    public function display($cachable = false, $urlparams = array())
    {
        // ...
    }

    public function add()
    {
        $this->setRedirect(JRoute::_('index.php?option=com_blog&view=article&layout=edit', false));
    }
}
 

\Joomla\CMS\Router\Route::_() 是一個可以幫我們自動處理網址前綴的功能,比如我們的 joomla 裝在子資料夾下(http://site.com/joomla/)時,Route 會自動把 /joomla/ 加在你的網址前面。第二個參數用 false 是因為預設會把 & 轉換成 &amp; ,這是印在頁面上時必要的 XHTML 標準,可以我們直接 redirect 時不需要轉換,所以就用 false。

p-2014-09-02-16.jpg

好了,現在我們可以按下「新增」按鈕,這個按鈕會把 add 字串塞進 task input 中,並 post form 出去,我們就會被導向到 controller 的 add() task 內,然後 controller 再把我們 redirect 到 {SITE}/administrator/index.php?option=com_blog&view=article&layout=edit 裡面。

p-2014-09-02-15.jpg

建立表單

現在我們可以在 edit.php 建立表單了,請依照如下範例編寫:

<?php
// administrator\components\com_blog\views\article\tmpl\edit.php

defined('_JEXEC') or die;
?>
<form action="<?php echo JUri::getInstance(); ?>" id="adminForm" name="adminForm" method="post">
    <fieldset class="form-horizontal">
        <legend>Blog Info</legend>

        <!-- Title -->
        <div class="control-group">
            <label for="form-title" class="control-label">Title</label>
            <div class="controls">
                <input type="text" id="form-title" name="title" value="" />
            </div>
        </div>

        <!-- Alias -->
        <div class="control-group">
            <label for="form-alias" class="control-label">Alias</label>
            <div class="controls">
                <input type="text" id="form-alias" name="alias" value="" />
            </div>
        </div>

        <!-- Created -->
        <div class="control-group">
            <label for="form-created" class="control-label">Created Time</label>
            <div class="controls">
                <?php echo JHtml::calendar('now', 'created', 'form-created'); ?>
            </div>
        </div>
    </fieldset>
</form>
 

html 比較繁雜一些,因為 Joomla 是用 Bootstrap2 作為主要 CSS 框架。應該會看到這樣的畫面

p-2014-09-02-17.jpg

我們來加上編輯器吧,先在 View 物件內準備好編輯器的本體,再到 template 內印出來

<?php
// administrator\components\com_blog\views\article\view.html.php

use Joomla\CMS\Editor\Editor;

// ...

public function display($tpl = null)
{
    // 取得全站設定中的編輯器設定檔
    $config = JFactory::getConfig();

    // 呼叫編輯器物件,直接 render 出來
    $this->introEditor = JEditor::getInstance($config->get('editor'))->display('introtext', '', '600px', '300px', 50, 15);
    $this->fullEditor = JEditor::getInstance($config->get('editor'))->display('fulltext', '', '600px', '300px', 50, 15);

    $this->addToolbar();

    parent::display($tpl);
}

// ...
 
<?php
// administrator\components\com_blog\views\article\tmpl\edit.php

defined('_JEXEC') or die;
?>
<form action="<?php echo JUri::getInstance(); ?>" id="adminForm" name="adminForm" method="post">
    <fieldset class="form-horizontal">
        <legend>Blog Info</legend>

        <!-- ... -->
    </fieldset>

    <fieldset>
        <legend>Text</legend>

        <!-- Intro text -->
        <div class="control-group row-fluid">
            <label for="form-title" class="control-label">Intro Text</label>
            <div class="controls span6">
                <?php echo $this->introEditor; ?>
            </div>
        </div>

        <hr />

        <!-- Full text -->
        <div class="control-group row-fluid">
            <label for="form-alias" class="control-label">Full text</label>
            <div class="controls span6">
                <?php echo $this->fullEditor; ?>
            </div>
        </div>
    </fieldset>
</form>
 

這樣應該就會出現內建的 TinyMCE 編輯器了(如果你有安裝其他編輯器,可能會出現你所選擇的編輯器)

p-2014-09-02-18.jpg

儲存我們的內容

接下來我們要儲存這些資訊進到資料庫。

首先,加上 hidden inputs 讓這個頁面的按鈕可以正常運作:

<?php
// administrator\components\com_blog\views\article\tmpl\edit.php

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

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

接著到 controller 加上 save task

<?php
// administrator\components\com_blog\controller.php

// ...

class BlogController extends BaseController
{
    // ...

    public function save()
    {
        // 只取得 POST 的資料
        $post = $this->input->post;

        // 將 POST 資料塞進一個陣列中,用 getString() 避免不合法字元
        $data['title']   = $post->getString('title');
        $data['alias']   = $post->getString('alias');
        $data['created'] = $post->getString('created');

        // HTML 資料必須用 getRaw(),不然會被過濾掉
        $data['introtext'] = $post->getRaw('introtext');
        $data['fulltext']  = $post->getRaw('fulltext');

        // 取得 Article Model 並執行 save()
        $model = $this->getModel('Article');

        $model->save($data);

        // save() 完成後我們跳回 Article List 頁面
        $this->setRedirect(JRoute::_('index.php?option=com_blog&view=articles', false));
    }
}
 

再替 Article Model 加上 save method:

<?php
// administrator\components\com_blog\models\article.php

use Joomla\CMS\MVC\Model\BaseDatabaseModel;

defined('_JEXEC') or die;

class BlogModelArticle extends BaseDatabaseModel
{
    public function save($data)
    {
        $db = $this->_db;

        // 把 $data 內的每個元素用單引號包起來,並且跳脫不合法字元
        foreach ($data as &$value)
        {
            $value = $db->quote($value);
        }

        $sql = "INSERT INTO #__blog_articles"
            . " (title, alias, created, introtext, `fulltext`) "
            . " VALUES (" . implode(', ', $data) . ")";

        $db->setQuery($sql);

        return $db->execute();
    }
}
 

這段要特別注意因為 fulltext 是 SQL 保留字,我們要用反引號 (`) 把 fulltext 包起來才能當做欄位名稱。而送進去的 data 我們也用 array_map() 來替每個值做引號包裹與跳脫。

現在你可以稍微填寫一些內容到表單裡面,並按下儲存按鈕。

p-2014-09-03-2.jpg

儲存按鈕會把 save 字串送進 task input,然後再 POST 表單出去,於是我們的 controller 就會接下來並執行 save() method。如果你把 SQL 內容印出來應該會長這樣。

INSERT INTO #__blog_articles(title, alias, created, introtext, `fulltext`) VALUES ('Test', 'test', '2014-09-01', '<p>一婦人說</p>\r\n<p>還聽得到幾聲行人的咳嗽</p>\r\n<p> </p>', '<p>孩子們辯說,在他們社會裡</p>\r\n<p>所以街上還見得鬧熱,透過了衣衫,說了不少好話</p>')
 

儲存成功後,controller 會重新導向,於是我們就回到 Articles List 頁面並看到一筆新資料了。

p-2014-09-03-3.jpg


本章節的教學內容可以在 GitHub 上找到: Code / Commits

ukash birim çevirme saç ekimi estetik