FMCakeMixのUser Guide.pdfを参考に、CakePHP x FileMakerでWebアプリの作成方法を紹介する。FX.phpに添付されているFileMakerファイルをベースに、Paginationを使った一覧画面の実装方法を。5回目よりCakePHP, FMCakeMixの規約を紹介。前回までに規約にしたがったFileMakerデータベース、database.php、モデル、Booksの登録処理・一覧・詳細表示におけるコントローラ・ビューの作成を紹介した。今回は編集・削除処理といったほかの画面について触れていこう。

Hello, FMCakeMix! チュートリアル / 編集・削除におけるコントローラ、ビューの記述例

Booksテーブルの登録処理をFMCakeMixで実装した。今回は登録したレコードの編集、および削除処理を実装してみよう。今回作成するアクションは次の3つ。

  • edit: レコードの編集画面を表示、および編集処理
  • delete: レコードの削除処理。一覧(index)から実行。削除後、一覧に戻る
  • delete_all: 全レコードの削除処理。一覧(index)から実行。削除後、一覧に戻る

まずは編集画面から実装してみよう。

booksコントローラ記述例(edit) - /app/controllers/books_controller.php

function edit($id = null)
{
    if (!$id && empty($this->data))
    {
        $this->Session->setFlash(__('不正なidです', true));
    }
    if (!empty($this->data))
    {
        if ($this->Book->save($this->data))
        {
            $this->Session->setFlash(__('書籍情報を保存しました', true));
            $this->redirect(array('action'=>'index'));
        }
        else
        {
            $this->Session->setFlash(__('書籍情報の保存に失敗しました', true));
        }
    }
    if (empty($this->data))
    {
        $this->data = $this->Book->read(null, $id);
    }
}

booksビュー記述例 - /app/view/books/edit.ctp

<h1>書籍編集</h1>

<?php
    echo $form->create('Book', array('action' => 'edit'));
    echo $form->input('title',array('label'=>'書籍名'));
    echo $form->end('書籍情報を編集');
?>
<ul>
    <li><?php echo $html->link('書籍情報詳細に戻る', array ( 'controller' => 'books', 'action' => 'view', 'id' => $this->data['Book']['id'] ) ); ?></li>
</ul>

詳細画面と同様、いったんread()を使用してレコード情報を取得。フォームから入力内容が送信され、$this->dataが空欄でなければその内容をsave()し、保存をおこなう。保存に成功した場合は、setFlash()でメッセージを埋め込み、redirect()で一覧画面(index)にリダイレクトさせるように。

続けて削除処理の実装だ。

booksコントローラ記述例(delete) - /app/controllers/books_controller.php

function delete($id=null)
{
    $this->Book->find
    (
        'first',
        array
        (
            'conditions' => array
            (
                'id' => $id 
            ),
            'recursive' => 0
        )
    );
    if ($this->Book->del())
    {
        $this->Session->setFlash(__('書籍情報を削除しました', true));
        $this->redirect(array('action'=>'index'));
    }
    else
    {
        $this->Session->setFlash(__('書籍情報の削除に失敗しました', true));
        $this->redirect(array('action'=>'view', 'id'=>$id));
    }
}

このアクションは画面を表示する必要がないので、ビューは作成しない。

コントローラの修正が終わったら、詳細画面から編集と削除をおこなえるように、view.ctpに修正をおこなう。

booksビュー記述例 - /app/view/books/view.ctp

<h1>書籍詳細</h1>
<table cellpadding="0" cellspacing="0">
    <tr>
        <th>
            Id
        </th>        <td>
            <?php echo h($book['Book']['id']); ?>
        </td>
    </tr>
    <tr>
        <th>
            Title
        </th>
        <td>
            <?php echo h($book['Book']['title']); ?>
        </td>
    </tr>
    <tr>
        <th>
            Author
        </th>
        <td>
            &nbsp;
        </td>
    </tr>
    <tr>
        <th>
            Created
        </th>
        <td>
            <?php echo h(date('Y/m/d H:i:s', $book['Book']['created'])); ?>
        </td>
    </tr>
    <tr>
        <th>
            Modified
        </th>
        <td>
            <?php echo h(date('Y/m/d H:i:s', $book['Book']['modified'])); ?>
        </td>
    </tr>
</table>

<ul>
    <li><?php echo $html->link('書籍情報を編集', array ( 'controller' => 'books', 'action' => 'edit', 'id' => $book['Book']['id'] ) ); ?></li>
    <li><?php echo $html->link('書籍情報を削除', array ( 'controller' => 'books', 'action' => 'delete', 'id' => $book['Book']['id'] ), null, '「' . $book['Book']['title'] . '」を削除してもよろしいですか?'); ?></li>
    <li><?php echo $html->link('書籍情報一覧', array ( 'controller' => 'books', 'action' => 'index' ) ); ?></li>
</ul>

削除処理では最初に-recidを渡して削除するのではなく、いったんidフィールドの値で検索(find)ををおこなっている。その後、該当したレコードをdelete()してレコードの物理削除をおこなう。また、前回紹介した$confirmMessageで削除前に確認のメッセージを表示するようにしている。

ここまで追加・修正をおこなった画面をWebブラウザで表示してみよう。

書籍詳細画面(books/view)。赤枠部が今回修正した箇所。この画面から編集画面への遷移と削除がおこなえるように

書籍編集画面(books/edit)。登録画面(booka/add)とほぼ同じレイアウト

レコードの編集に成功した場合は、書籍一覧画面(books/index)にリダイレクト。赤枠部が更新されていることがわかる

書籍詳細画面(books/view)から削除をおこなう。削除前に$confirmMessageで確認をおこなっている

レコードの削除に成功した場合は、編集同様、書籍一覧画面(books/index)にリダイレクト

CakePHP x FileMakerでのdeleteAll代替手段

このコラムでも何回か述べてきたとおり、FileMakerのWeb公開機能には次の制約がある。

  • 編集処理(-edit)と削除処理(-delete)には、レコードID(-recid)の指定が必須
  • 上記2処理で操作できるレコードは基本的に1つ

ポータル内に操作したいレコードを参照させることで複数のレコードを編集・削除することも可能だが、操作したいポータル内のレコードIDも要求され、どちらもかなりトリッキー実装となる。PHP側で複数のレコードIDを配列に持たせてループで処理することも可能だが、FileMaker Serverに大きな負荷がかかってしまう。

CakePHP(FMCakeMix)のdeleteAllを実現したい場合は、FileMakerスクリプトで「全レコードを表示→対象レコードを削除」を作成しておき、それをWeb経由で実行するのがベストだろう。開発時間、処理時間、サーバにかかる負荷3点をカバーできる実装方法だ。

まずはFileMaker側にスクリプトを作成しておく。名前は「deleteAll」とした。

FileMakerスクリプト - deleteAll

全レコードを表示
対象レコード削除 [ ダイアログなし ]

FileMakerスクリプト「deleteAll」。作業レイアウトを明示しないことで、モデルやコントローラ内でレコードを削除するテーブルを変更できるようにしておく

スクリプト内に作業するレイアウトを明示しないことで、Web上からレコードを削除できるテーブルを指定できるようにしている。続いてコントローラ側で、このFileMakerスクリプトを実行するアクションを定義する。

booksコントローラ記述例(delete_all) - /app/controllers/books_controller.php

function delete_all()
{
    $this->Book->find
    (   
        'first',
        array
        (   
            'conditions' => array
            (   
                '-script' => 'deleteAll'
            ),  
            'recursive' => 0
        )   
    );  
    $this->Session->setFlash(__('書籍情報を削除しました', true));
    $this->redirect(array('action'=>'index'));
}

検索条件とおなじ要領でconditionsの-scriptに対してスクリプト名を定義し、モデルやコントローラで指定しているレイアウト上でスクリプトが動作するように。このアクションを一覧画面から実行できるように、/app/views/books/index.ctpにリンクを追加する。

booksビュー記述例 - /app/view/books/index.ctp

<h1>書籍一覧</h1>

<p style="text-align: center">
<?php
    echo $paginator->counter
    (
        array
        (
            'format' => __('%page% / %pages% ページ,  <span class="foundCount">%count%</span>件中、%start% - %end% 件目を表示', true)
        )
    );
    echo '<br>';
    echo $paginator->prev('<< '.__('前のページ', true), array(), null, array('class'=>'disabled', 'tag' => 'span'));
    echo ' | ' . $paginator->numbers() . ' | ';
    echo $paginator->next(__('次のページ', true).' >>', array(), null, array('tag' => 'span', 'class' => 'disabled'));
?>
</p>

<table cellpadding="0" cellspacing="0">
    <tr>
        <th>
            <?php echo $paginator->sort('Id' , 'id');?>
        </th>
        <th>
            <?php echo $paginator->sort('Title' , 'title');?>
        </th>
        <th>
            <?php echo $paginator->sort('Author', 'author');?>
        </th>
        <th>
            <?php echo $paginator->sort('Created', 'created');?>
        </th>
        <th>
            <?php echo $paginator->sort('Modified', 'modified');?>
        </th>
    </tr>
    <?php
    $i = 0;
    foreach ($books as $book)
    {
        ?>
        <tr>
            <td>
                <?php
                echo $html->link
                (
                    h($book['Book']['id']),
                    array
                    (
                        'controller' => 'books',
                        'action' => 'view',
                        'id' => $book['Book']['id']
                    )
                ); ?>
            </td>
            <td>
                <?php echo h($book['Book']['title']); ?>
            </td>
            <td>
                &nbsp;
            </td>
            <td>
                <?php echo h(date('Y/m/d H:i:s', $book['Book']['created'])); ?>
            </td>
            <td>
                <?php echo h(date('Y/m/d H:i:s', $book['Book']['modified'])); ?>
            </td>
        </tr> 
        <?php
    }
    ?>
</table>

<p style="text-align: center">
<?php
    echo $paginator->counter
    (
        array
        (
            'format' => __('%page% / %pages% ページ,  <span class="foundCount">%count%</span>件中、%start% - %end% 件目を表示', true)
        )
    );
    echo '<br>';
    echo $paginator->prev('<< '.__('前のページ', true), array(), null, array('class'=>'disabled', 'tag' => 'span'));
    echo ' | ' . $paginator->numbers() . ' | ';
    echo $paginator->next(__('次のページ', true).' >>', array(), null, array('tag' => 'span', 'class' => 'disabled'));
?>
</p>

<ul>
    <li><?php echo $html->link('書籍情報を登録する', array ( 'controller' => 'books', 'action' => 'add') ); ?></li>
    <li><?php echo $html->link('書籍情報を全削除', array ( 'controller' => 'books', 'action' => 'delete_all' ), null, '書籍情報をすべて削除します。よろしいですか?'); ?></li>
</ul>

一覧画面(books/index)でdeleteAllの動作を確認する。

レコードの削除同様、確認ダイアログを表示するように

deleteAllが実行され、全レコードが削除された

次回、著者情報(Author)の画面や関連付けについて紹介する。