CakePHPで高速Webアプリ開発

第10回CakePHPで作るToDoアプリ(5)

第8回では、タスクの追加をAjax化しました。今回はアプリケーションとしての体裁を整えるため、以下の作業を行います。

  • レイアウトの作成
  • ページタイトルの設定
  • アクセスしやすいURLの設定(Routes)

レイアウトの作成

「レイアウト」とは、ビューを取り囲むコードです。ヘッダーやフッターなど常に表示させたいコードがあれば、レイアウトファイルに記述すると良いでしょう。

レイアウトとビューの関係は以下のイメージです。

画像

レイアウトファイルは、app/views/layouts/以下に配置します。デフォルトのレイアウトファイルはapp/views/layouts/default.thtmlです。しかし、app/views/layouts/の中にはdefault.thtmlファイルはありません。今までのレイアウトは何が使われていたかというと、cake/libs/view/templates/layouts/default.thtmlが使われています。

CakePHPは、app/views/layouts/内にレイアウトファイルがない場合は、cake/libs/view/templates/layouts/内を探します。このファイルを書き換えても動作はしますが、cakeディレクトリ以下のファイルはフレームワークの根幹部分なので、基本的に書き換えてはいけません。

cakeディレクトリ以下のファイルとを書き換えてしまうと、色々と問題が発生します。たとえば、CakePHPのバージョンアップ時にはcakeディレクトリ以下をすべて置き換えるので、バージョンアップの度に同じ内容の書き換え作業が発生してしまい、かなり面倒です。バージョンアップを行わなくても、せっかくapp以下にまとめたアプリケーション部分が分散してしまい、あとから修正するときに調査範囲が全体に及んでしまいます。とにかく、cakeディレクトリ以下のファイルの修正はやめておきましょう。

なお、フレームワーク自体にバグがあったときの修正は、致し方ありません。バグを修正し、CakePHP開発チームにバグ内容を報告すると良いでしょう。

では、app/views/layouts/default.thtmlを作成し、以下のコードを記述してください。

app/views/layouts/default.thtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><?php echo h($title_for_layout); ?></title>
<?php echo $html->css('cake.generic');?>
</head>
<body>
  <div id="container">
    <div id="header">
      <h1>ToDo App</h1>
    </div>
    <div id="content">
      <?php echo $content_for_layout; ?>
    </div>
  </div>
</body>
</html>

このレイアウトファイルでページを開くと以下のように表示されます。

default.thmlのイメージ
default.thmlのイメージ

変化は少ないですが、ヘッダーの表示が変わり、フッターのCakePHPアイコンがなくなっています。

では、レイアウトの内容を上から順に、PHPコードが記述されている部分に絞って解説します。PHPコードは3ヵ所です。

<title><?php echo h($title_for_layout); ?></title>

ここではページタイトルを定義しています。h関数はCakePHPが定義したhtmlspecialchars関数へのエイリアスです(第2引数がないので正確には違います⁠⁠。$title_for_layout変数には、タイトル文字列が設定されています。ちなみにcake/libs/view/templates/layouts/default.thtmlでの$title_for_layoutの出力は、h関数で囲まれていません。今回自作したレイアウトでは 表示時にh関数で実態参照化するようにしました。

<?php echo $html->css('cake.generic');?>

こちらはcssファイルを読み込むlink要素を出力しています。今回のコードでは、たとえば以下のように出力されます。

<link rel="stylesheet" type="text/css" href="/~gihyo/todo/css/cake.generic.css" />

cake.generic.css の実体は app/webroot/css/cake.generic.css です。cssを変更したいときは、このcssを書き換えるか、読み込むファイルを変更・追加してください。

      <?php echo $content_for_layout; ?>

$content_for_layoutにはビューの実行結果の文字列が格納されており、ここでビューを出力しています。余談ですが、このことからビューはレイアウトよりも先に実行されていることがわかります。

レイアウトのPHPコードは以上です。

レイアウトと表示出力のコントロール

デフォルト以外のレイアウトファイルは、たとえばapp/views/layouts/example.thtmlなどです。example.thtml を使用したいときは、コントローラで以下のように記述します。

$this->layout = 'example';

また、コントローラのrenderメソッドを使って、明示的にビューとレイアウトを指定することができます。今回作成したToDoアプリではアクション名のビューを自動で表示させていましたが、以下の文法でビューとレイアウトを変更することができます。

$this->render('ビュー名', 'レイアウト名');

たとえば、Tasksコントローラ内のアクションで、renderメソッドを使ってビューをapp/views/tasks/index2.html、レイアウトをapp/views/layouts/example.thtmlで表示させたいときは以下のように記述します。

$this->render('index2', 'example');

ビューだけを変更したいときは以下のように記述します。

$this->render('index2');

なお、renderメソッドを実行すると、即座に表示出力が行われます。今回のToDoアプリではアクションメソッドを終了後に、自動で表示出力を行っていました。自動出力を止めたいときは、以下のようにコントローラのautoRenderプロパティにfalseを設定します。

$this->autoRender = false;

このあとにrenderメソッドを実行しなければ、何も表示されません。

ページタイトルの設定

ページタイトルはコントローラの$pageTitleプロパティに設定します。たとえば、以下のように記述します。

    $this->pageTitle = 'タスク一覧';

今回のアプリではTasksコントローラのindexアクション(タスク一覧)とeditアクション(タスクの編集)でページタイトルを設定します。強調された行が追加コードです。

app/controllers/tasks_controller.php
<?php
class TasksController extends AppController {
  var $name = 'Tasks';
  var $uses = array('Task');
  var $helpers = array(
    'Javascript',
    'Ajax',
  );
  function index() {
    $this->set('yet_tasks', $this->Task->findAllByStatus('yet', null,'Task.created ASC'));
    $this->set('done_tasks', $this->Task->findAllByStatus('done',null, 'Task.modified DESC'));
    $this->pageTitle = 'タスク一覧'; 
  }
  function add() {
    if (empty($this->data)) return;
    $this->Task->save($this->data, true, array('content', 'created','modified'));
    $this->set('yet_tasks', $this->Task->findAllByStatus('yet', null,'Task.created ASC'));
    $this->layout = 'ajax';
  }
  function done($id) {
    if ($this->Task->findById($id)) {
      $this->Task->id = $id;
      $this->Task->save(array('status' => 'done'));
    }
    $this->redirect('/tasks');
  }
  function edit($id) {
    $task = $this->Task->findById($id);
    if (!$task) {
      $this->redirect('/tasks');
      return;
    }
    if (!empty($this->data)) {
      $task['Task']['content'] = $this->data['Task']['content'];
      $this->Task->save($task);
    }
    $this->set('task', $task);
    $this->pageTitle = 'タスクの編集 Id:' . $id; 
  }
  function del($id) {
    $this->Task->del($id);
    $this->redirect('/tasks');
  }
}

コードを追加したらブラウザのタイトル表示部分を確認してみてください。⁠タスク一覧」⁠タスクの編集 Id:1」などと表示されるはずです。

アクセスしやすいURLの設定(Routes)

これまで、Tasksコントローラへのアクセスは「http://example.com/~gihyo/todo/tasks」というURLで行ってきました。これを「http://example.com/~gihyo/todo/」でアクセスできるようにします(末尾のtasksがなくなっています⁠⁠。

URLの設定はmod_rewriteでもできますが、ここではCakePHPのRoutes機能を使って行います。

app/config/routes.php の以下の行を、

app/config/routes.php
…
$Route->connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
…

以下に変更します。

app/config/routes.php
…
$Route->connect('/', array('controller' => 'tasks', 'action' => 'index'));
…

これで「http://example.com/~gihyo/todo/」へアクセスしてみてください。tasksつきのURLと同じように、タスク一覧が表示されるはずです。

今回設定したRoutesは単純なものですが、複雑な設定も可能です。Routesについて解説するとかなり長くなってしまうので、http://manual.cakephp.org/chapter/configurationのSection 3を参照しつつ、試行錯誤をする方が効率的に習得できるのではないかと思います。

ToDoアプリの開発は以上です

今回の内容で、アプリケーションの体裁はほぼフルカスタマイズできる知識は習得できました。ToDoアプリの開発はひとまず終了です。

次回以降は、より効率的に開発するために、CakePHP上のデバッグ手法や開発中のつまずきポイントの解説、コンポーネントの作り方、ヘルパーの作り方などを解説していきたいと思います。

おすすめ記事

記事・ニュース一覧