ajaxで入力する

そしてupdateする。すんませんが1.x系です

前準備

とりあえずいつものようにprojectでも作りますワ

% cake bake project ajaxinput

今回用意するスキーマ

DROP TABLE IF EXISTS products;
CREATE TABLE products(
 id INT auto_increment PRIMARY KEY,
 item VARCHAR(255) NOT NULL,
 published DATETIME,
 created DATETIME,
 updated DATETIME
) ENGINE=InnoDB DEFAULT CHARSET utf8;

とりあえずcake bakeでDBの接続設定書いて、cake bake all。 http://server/path/to/productsでproductsコントローラのindexアクションが呼ばれる所まで確認。

フォームを書く

addメソッドからも書けるのだけど、今回はあくまでindexからモリモリ追加する事を想定しているのでindexに書いてまいます。addで生成されたviewからペチッと貼ってまいましょう。

<div class="products index">
<h2><?php __('Products');?></h2>
<p>
<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
));
?></p>
 
<div class="products form">
<?php echo $form->create('Product');?>
  <fieldset>
    <legend><?php __('Add Product');?></legend>
  <?php
    echo $form->input('item');
    echo $form->input('published');
  ?>
  </fieldset>
<?php echo $form->end('Submit');?>
</div>

これで、ajaxとかを使わない何かが取り敢えずできたわけですな

ajaxに対応していく

いろいろと流儀があるのだが、まずデフォではprototypeを呼びこまないとどうにもならないようなので、これをviewに追加してみる

views/products/index.ctp

<?php echo $javascript->link('prototype', false) ?>
<?php echo $javascript->link('scriptaculous', false) ?>
 
 
<div class="products index">
// 略

prototypeとかscripaclousとかがデフォルトではついてこないのでどうにかして手に入れてwebroot/jsに入れる

さらにはフォームをajax対応に書き換える、のだがajaxヘルパを使う宣言をしておかないと駄目なので、まずコントローラを書き換える

controllers/products_controller.php

<?php
class ProductsController extends AppController {
 
        var $name = 'Products';
        var $helpers = array('Html', 'Form', 'Ajax'); // <- これを追加

views/products/index.ctpにて、$form→createを$ajax→formに書き換える

<div class="products form">
<?php echo $ajax->form('update', 'post', array('update' => 'demo'));?>
  <fieldset>

とりあえず、中身は置いておいて、update⇒demoが重要で、これはどのIDフィールドにupdateをかけるか、という事になる。

とりあえずリストのテーブルの間にでも作ってみよう

views/products/index.ctp

// 略
</div>
 
<div id="demo"></div> // <-- これ
 
<table cellpadding="0" cellspacing="0">
<tr>
  <th><?php echo $paginator->sort('id');?></th>
// 略

とりあえずこれでボタンを押してみる。<div id="demo"></div>に設定した所にMissing Controllerとか出れば成功である。

updateメソッドの追加

ここでproducts_controller.phpにupdateメソッドが必要な事が解る。

単純に追加してみよう

controllers/products_controller.php

        function update() {
                ;
        }

これでボタンを押すとmissing viewとか出るかと思う。

どこにviewを書くか

さて、ここからが本題といってもいい問題。indexコントローラでまずリストを表示させておきたいのだが、ajaxでupdateを書けた時はupdateメソッド側から再描画したい。とりあえずエレメント化を考えてみる。

リストのエレメント化

index viewの内容をガバっとエレメントにしてみる

<table></table>の内容をそのままviews/elements/list.ctpにコピーし、indexのtableがあった部分にはこのようにしておく

<?php echo $this->element('list'); ?>

さて、ここでviews/products/update.ctpに先程エレメント化したものをそのまま書く

<?php echo $this->element('list'); ?>

updateメソッドはindexと同じようなものを書く(再描画)

controllers/products_controller.php

        function update() {
                $this->Product->recursive = 0;
                $this->set('products', $this->paginate());
        }

これでフォームのボタンを押すと2つの問題が生じると思う

  1. なんかレイアウト的に同じものが2つ出ている
  2. ソートのボタン(リンク)のコントローラがupdateとかに向いてしまっている

これらをとりあえず改善していこう

ajaxレイアウトにする

これはもう単純にこの一行を書けばOK

        function update() {
                $this->layout = 'ajax'; // これ
                $this->Product->recursive = 0;
                $this->set('products', $this->paginate());
        }

何てことはない、出力の雛形をviews/layouts/ajax.ctpに向けるぞ、という事です。気になる人は中身開いてみてネ

さらにデバッグ表示をやめときます

        function update() {
                $this->layout = 'ajax';
                Configure::write('debug',0); // これ
                $this->Product->recursive = 0;
                $this->set('products', $this->paginate());
        }

で、indexビューもちょっと変えておきます。今は二つ出てると思うので、ボタンが押された時はupdate側のリストで完全に更新するように。

<div id="demo">
<?php echo $this->element('list'); ?>
</div>

id="demo"ってのもアレなんですが、まあとりあえずこんな具合で。demoを変える場合は$ajax→formの引数のupdatesも変更する事。

paginatorのurlを合わせる

viewにこんな具合で書いておきます

views/elements/list.ctp

<?php
$paginator->options(array('url' => array('action' => 'index')));
?>
<table cellpadding="0" cellspacing="0">

これで、一見同じものが出てくるように見える所まで来ました(実際はちょっと間違ってるけど)

とりあえずview側はこれくらいにしときます

データを更新する

、っとその前に、フォームが崩れてるのが解ります。これはモデルを指定していない為。viewを修正しま。

views/products/index.ctp

<div class="products form">
<?php echo $ajax->form('update', 'post', array('update' => 'demo', 'model' => 'Product'));?>

model⇒Productなんてのを追加するとそのモデルに応じたフォームが描画

で、コントローラも変更。これはaddのものをパクってきて、あと問題な所をちょっと修正したもの

        function update() {
                if (!empty($this->data)) {
                        $this->Product->create();
                        if ($this->Product->save($this->data)) {
                                // $this->Session->setFlash(__('The Product has been saved', true));
                        } else {
                                // $this->Session->setFlash(__('The Product could not be saved. Please, try again.', true));
                        }
                }
                $this->layout = 'ajax';
                Configure::write('debug',0);
                $this->Product->recursive = 0;
                $this->set('products', $this->paginate());
        }

これでsaveされる、しかしidが昇順ですよね。。こんな具合で修正

                $this->paginate = array(
                        'order' => 'id DESC'
                );
                $this->set('products', $this->paginate());

で、このままだとページングが効かないので、ページャ自体もelement化せんといかんようですね。。

views/elements/list.ctp

追記
<div class="paging">
  <?php echo $paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>
 |  <?php echo $paginator->numbers();?>
  <?php echo $paginator->next(__('next', true).' >>', array(), null, array('class' => 'disabled'));?>
</div>

細かい事

  1. 追記する時にエラーがあった時に表示していない
  2. 追記した時にフォームがクリアされない

1. の問題に対してはAjaxのview側で頑張るしか無いでしょうねえ… setFlashは使っても意味がないので、適当にエラーを表示させるエリアをdivでくくっておいて、そこにjsなりで注入するくらい。もしくはupdate側のviewに書く。でもこれだとフォームとの兼ね合いでよく無いかもしれません。

2.に関してはこんな強引なjavascriptで解決…

<div class="products form">
<?php echo $ajax->form('update', 'post', array('update' => 'demo', 'model' => 'Product', 'complete' => 'document.getElementById("updateform").reset()', 'id' => 'updateform'));?>
cakephp/ajaxinput.txt · 最終更新: 2012/04/07 10:32 (外部編集)
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0