開発メモ,主に補足by子連れ親父プログラマー

2011-09-15

CakePHP1.3で作る会員管理システム(12) 確認画面その2

確認画面その2

ではビューについて、順番に整理していきます。

confirm.ctp から管理メニュー部分

<div class="actions">
....
</div>
で囲まれたところをざくっと削除します。いらないので。

Birthday(誕生日)のところ、

<?php echo $member['Member']['birthday']; ?>
こうなっているのを、以下に変更します。

<?php echo $member['Member']['birthday']['year'] .'/' . $member['Member']['birthday']['month'] .'/'. $member['Member']['birthday']['day']; ?>

同じく “hidden” の Birthday 部分、

echo $this->Form->input('birthday', array('type'=>'hidden'));
これを以下に変更します。

echo $this->Form->input('birthday.year', array('type'=>'hidden'));
echo $this->Form->input('birthday.month', array('type'=>'hidden'));
echo $this->Form->input('birthday.day', array('type'=>'hidden'));

Type(会員種別) のところ、

<?php echo $this->Html->link($member['Type']['name'], array('controller' => 'types', 'action' => 'view', $member['Type']['id'])); ?>
こうなっているのを、以下に変更します。

<?php echo $types[$member['Member']['type_id']]; ?>

次に「好きな物」に関して、

<div class="related">
....
</div>
ではさまれたの関連テーブル部分をごっそり削除し、その代わりにメインのテーブルの Img2 の表示部分(tr で囲まれた一角)の下に以下を追加します。

  <dt<?php if ($i % 2 == 0) echo $class;?>><?php __('好きな物'); ?></dt>
  <dd<?php if ($i++ % 2 == 0) echo $class;?>>
   <?php if (!empty($member['Favorite']['Favorite'])):?>
   <ul>
    <?php foreach ($member['Favorite']['Favorite'] as $favorite): ?>
     <li><?php echo $favorites[$favorite['id']];?></li>
    <?php endforeach; ?>
   </ul>
   <?php endif; ?>
    
  </dd>

members_controller.php に戻って、 function add() の、一番下の

  $types = $this->Member->Type->find('list');
  $favorites = $this->Member->Favorite->find('list');
  $this->set(compact('types', 'favorites'));
3行を、一番上に持ってきます。

こうなります。

 function add() {
  $types = $this->Member->Type->find('list');
  $favorites = $this->Member->Favorite->find('list');
  $this->set(compact('types', 'favorites'));
  
  if (!empty($this->data)) {
   if ($this->data['Member']['mode'] == 'confirm') {
    $this->Member->set($this->data);

ブラウザで確認します。
ここまでで、Type(会員種別)、Birthday(誕生日)、好きな物 が表示されるようになったはずです。
ボタン「送信」をクリックで、一覧画面に戻り、

The Member has been saved
と表示されればOKです。membersテーブルのデータは保存されています。

2011-09-14

CakePHP1.3で作る会員管理システム(11) 確認画面その1

確認画面その1

まず、確認画面のビューを作ります。 bake で自動生成した、 view.ctp がそのまま使えそうなので、 view.ctp を開いてから「名前を付けて保存」で confirm.ctp を作成します。

今回確認画面はhidden方式でいくので、

<div class="members view">
となっている所の、直下にフォームを作ります。

<?php echo $this->Form->create('Member', array('id'=>'MemberConfirm')); ?>

こんな感じです。 ただの

$this->Form->create('Member');
だけでもいいっちゃいいんですが、そうすると、以下のような add の画面と全く同じ id でフォームタグが生成されてしまうので、
<form id="MemberAddForm" accept-charset="utf-8" method="post" action="/~myname/cake/members/add">
これはなんとなく気持ち悪いので、
array('id'=>'MemberConfirm')
を指定して、確認画面用のフォームの id を指定しておきます。

で、その下に add.ctp のフォーム部分、

<?php
    echo $this->Form->input('email');
    echo $this->Form->input('password');
    echo $this->Form->input('type_id');
    echo $this->Form->input('birthday');
    echo $this->Form->input('img1');
    echo $this->Form->input('img2');
    echo $this->Form->input('Favorite');
?>
をごっそりそのままコピーして貼り付けます。

hidden にするので

array('type'=>'hidden')
を一個一個全部に指定して、一応全体を div で囲んでおきます。 こんな感じになりました。

<div class="members view">
<?php echo $this->Form->create('Member', array('id'=>'MemberConfirm'));?>
<div style="display:none;">
<?php
    echo $this->Form->input('email', array('type'=>'hidden'));
    echo $this->Form->input('password', array('type'=>'hidden'));
    echo $this->Form->input('type_id', array('type'=>'hidden'));
    echo $this->Form->input('birthday', array('type'=>'hidden'));
    echo $this->Form->input('img1', array('type'=>'hidden'));
    echo $this->Form->input('img2', array('type'=>'hidden'));
    echo $this->Form->input('Favorite', array('type'=>'hidden'));
?>
</div>

<h2><?php  __('Member');?></h2>

ただ、アクションとしては同じ add で処理するので、なにかで識別するようにしないと「今は確認画面だ!」というのが分かりません。

ということで、 mode = ‘confirm’ というのを作り出して、これを add.ctp (フォーム入力画面)の方に、 hidden で入れておきます。

add.ctp 側

<div class="members form">
<?php echo $this->Form->create('Member');?>
<div style="display:none;">
    <?php echo $this->Form->hidden('mode', array('value'=>'confirm')); ?>
</div>

    <h2>
        <?php __('Add Member'); ?></h2>

confirm.ctp 側はvalueなしで。以下を追加。

    echo $this->Form->input('mode', array('type'=>'hidden','value'=>''));

これで、

  • add → confirm へは mode = ‘confirm’ で、
  • confirm → complete へは mode = ” で
行く事になります。 最後に、ボタンを追加しておきます。

<div class="submit">
    <?php echo $this->Form->button('戻る', array('id'=>'back')); ?>
</div>
<?php echo $this->Form->end('送信'); ?>

これはどっか適当に下の方に入れておきます。 一応ただの id=”back” ボタンをつけておきます。

あと、view.ctp との違いで、そっちには id がありますが、こっちにはまだ id は決まっていない、存在していないので、以下の id に関する表示部分を削除しておきます。

        <dt<?php if ($i % 2 == 0) echo $class;?>><?php __('Id'); ?></dt>
        <dd<?php if ($i++ % 2 == 0) echo $class;?>>
            <?php echo $member['Member']['id']; ?>
             
        </dd>

同様に、 Created と Modified も要らないので削除しておきます。

        <dt<?php if ($i % 2 == 0) echo $class;?>><?php __('Created'); ?></dt>
        <dd<?php if ($i++ % 2 == 0) echo $class;?>>
            <?php echo $member['Member']['created']; ?>
             
        </dd>
        <dt<?php if ($i % 2 == 0) echo $class;?>><?php __('Modified'); ?></dt>
        <dd<?php if ($i++ % 2 == 0) echo $class;?>>
            <?php echo $member['Member']['modified']; ?>
             
        </dd>

ここでもうそろそろ画面を見たいのでコントローラをいじります。

/app/controllers/members_controller.php を開いて、function add() の以下の部分、

        if (!empty($this->data)) {
            $this->Member->create();
            if ($this->Member->save($this->data)) {
                $this->Session->setFlash(__('The member has been saved', true));
                $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash(__('The member could not be saved. Please, try again.', true));
            }
        }
この1行目からのif文の中にさらにもう一個if文を入れて、もとからあった部分はelseのあとにつっこみます。

構造としてはこんな感じ。

if (!empty($this->data)) {
    if () {
     
    } else {
    // もとからあった部分
    }
}

で、その新しいif文を以下のようにします。

    function add() {
        if (!empty($this->data)) {
            if ($this->data['Member']['mode'] == 'confirm') {
                $this->Member->set($this->data);
                $this->set('member', $this->data);
                $this->render('confirm');
            } else {
                $this->Member->create();
                if ($this->Member->save($this->data)) {
                    $this->Session->setFlash(__('The member has been saved', true));
                    $this->redirect(array('action' => 'index'));
                } else {
                    $this->Session->setFlash(__('The member could not be saved. Please, try again.', true));
                }
            }
        }

これで確認画面が出るはずなのでブラウザで /members/add を開いて適当に入力して進むと・・・
ぼこぼこにエラーが出てますが、入力したEmailの値とかは表示されてるはずです。
エラーは配列とかの問題だけなので今は気にしなくていいです。

2011-09-13

CakePHP1.3で作る会員管理システム(10) レイアウトその2

レイアウトその2

続いて、一般の人が登録したり、ログインして編集する画面(公開側)のレイアウトを作ります。 通常公開側はデザイナーさんがカッチリデザインしてくるものですから、どっちみち最終的にそのデザインに合わせて調整することになると思います。 ということで、ここでは admin 側をそのまま流用してファイルだけ作っておくことにします。

まず、先ほど作った

/app/views/layouts/admin.ctp
をそのまま同じ場所にコピーして、member.ctp にリネームします。
/app/views/layouts/member.ctp

そして左メニューの部分を以下のように編集します。

            <div class="actions">
                <h3><?php __('メニュー'); ?></h3>
                <ul>
                    <li><?php echo $this->Html->link(__('登録情報変更', true), array('controller'=>'members','action' => 'edit'));?></li>
                    <li><?php echo $this->Html->link(__('退会', true), array('controller' => 'members', 'action' => 'delete')); ?> </li>
                    <li><?php echo $this->Html->link(__('ログアウト', true), array('controller' => 'users', 'action' => 'logout')); ?> </li>
                </ul>
            </div>

member.ctp のヘッダー部の以下の部分、

<head>
    <?php echo $this->Html->charset(); ?>
    <title>
        <?php echo $title_for_layout; ?>
    </title>
    <?php
        echo $this->Html->meta('icon');

        echo $this->Html->css('cake.generic');

        echo $scripts_for_layout;
    ?>
</head>
cssを指定しているところを以下のように書き換えます。

        echo $this->Html->css('member');

これで出力されるソースは

<link href="/~myname/cake/css/member.css" type="text/css" rel="stylesheet">
となります。

css の置き場所は /app/webroot/css/ ですので、

/app/webroot/css/cake.generic.css
をそのまま同じ場所にコピーして、member.css にリネームします。

/app/webroot/css/memmber.css

最後に、admin の場合と、member の場合で、自動でレイアウトが切り替わるように設定します。

まず、前回 member_controller.php に入れた、

    function beforeFilter() {
        $this->layout = 'admin';
    }
を削除しておきます。

そして、/app 直下に app_controller.php を配置します。

cd Sites/cake
cp cake/libs/controller/app_controller.php app/

今コピーした、/app/app_controller.php に以下を追加します。

<?php
/**
 * Application level Controller
 *
 * This file is application-wide controller file. You can put all
 * application-wide controller-related methods here.
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       cake
 * @subpackage    cake.cake.libs.controller
 * @since         CakePHP(tm) v 0.2.9
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

/**
 * This is a placeholder class.
 * Create the same file in app/app_controller.php
 *
 * Add your application-wide methods in the class below, your controllers
 * will inherit them.
 *
 * @package       cake
 * @subpackage    cake.cake.libs.controller
 * @link http://book.cakephp.org/view/957/The-App-Controller
 */
class AppController extends Controller {

    function beforeFilter(){
        if (!empty($this->params['admin'])) {
            $this->layout = "admin";
        }else{
            $this->layout = "member";
        }
    }

}

これで admin の時と admin でない時でレイアウトが切り替わります。

2011-09-11

CakePHP1.3で作る会員管理システム(9) レイアウトその1

レイアウトその1

で、ついでなので管理画面っぽく、レフトメニューがあって作業を選べるようなページレイアウトに変更します。

/cake/libs/view/layouts/default.ctp がもともとのレイアウトファイルなので、これを開いて参考にしながら、自分の admin 用のレイアウト admin.ctp を作ります。 それは /app/views/layouts に保存します。

/app/views/layouts/admin.ctp

今まで add.ctp などをいじってきて気がついていると思いますが、フォームなどのメイン部分の下に、左メニュー部分が Actions として入っています。

つまり全体としては、こんな構造になっていて、

<div id="header"></div>
<div id="content">
    <div class="members form"></div>
    <div class="actions"></div>
</div>

各ページのテンプレート($content_for_layout の中に入る部分)が、
    <div class="members form"></div>
    <div class="actions"></div>
になっている、という訳です。

ということで、このactionsを分離して、レイアウトの方に入れてしまいます。

add.ctp から

<div class="actions">
 <h3><?php __('Actions'); ?></h3>
 <ul>

  <li><?php echo $this->Html->link(__('List Members', true), array('action' => 'index'));?></li>
  <li><?php echo $this->Html->link(__('List Types', true), array('controller' => 'types', 'action' => 'index')); ?> </li>
  <li><?php echo $this->Html->link(__('New Type', true), array('controller' => 'types', 'action' => 'add')); ?> </li>
  <li><?php echo $this->Html->link(__('List Favorites', true), array('controller' => 'favorites', 'action' => 'index')); ?> </li>
  <li><?php echo $this->Html->link(__('New Favorite', true), array('controller' => 'favorites', 'action' => 'add')); ?> </li>
 </ul>
</div>
この部分をカットし、admin.ctp (もともとはdefault.ctp)の $content_for_layout の下ににコピーします。

<?php
/**
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       cake
 * @subpackage    cake.cake.libs.view.templates.layouts
 * @since         CakePHP(tm) v 0.10.0.1076
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
?>
<!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>
 <?php echo $this->Html->charset(); ?>
 <title>
  <?php __('CakePHP: the rapid development php framework:'); ?>
  <?php echo $title_for_layout; ?>
 </title>
 <?php
  echo $this->Html->meta('icon');

  echo $this->Html->css('cake.generic');

  echo $scripts_for_layout;
 ?>
</head>
<body>
 <div id="container">
  <div id="header">
   <h1><?php echo $this->Html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
  </div>
  <div id="content">

   <?php echo $this->Session->flash(); ?>

   <?php echo $content_for_layout; ?>

   <div class="actions">
    <h3><?php __('Actions'); ?></h3>
    <ul>
     <li><?php echo $this->Html->link(__('List Members', true), array('action' => 'index'));?></li>
     <li><?php echo $this->Html->link(__('List Types', true), array('controller' => 'types', 'action' => 'index')); ?> </li>
     <li><?php echo $this->Html->link(__('New Type', true), array('controller' => 'types', 'action' => 'add')); ?> </li>
     <li><?php echo $this->Html->link(__('List Favorites', true), array('controller' => 'favorites', 'action' => 'index')); ?> </li>
     <li><?php echo $this->Html->link(__('New Favorite', true), array('controller' => 'favorites', 'action' => 'add')); ?> </li>
    </ul>
   </div>
  </div>
  <div id="footer">
   <?php echo $this->Html->link(
     $this->Html->image('cake.power.gif', array('alt'=> __('CakePHP: the rapid development php framework', true), 'border' => '0')),
     'http://www.cakephp.org/',
     array('target' => '_blank', 'escape' => false)
    );
   ?>
  </div>
 </div>
 <?php echo $this->element('sql_dump'); ?>
</body>
</html>

こんな感じになります。

そして、<div class="actions"> の中を書き換えます。

   <div class="actions">
    <h3><?php __('メニュー'); ?></h3>
    <ul>
     <li><?php echo $this->Html->link(__('会員管理', true), array('controller'=>'members','action' => 'index'));?></li>
     <li><?php echo $this->Html->link(__('担当者アカウント管理', true), array('controller' => 'users', 'action' => 'index')); ?> </li>
     <li><?php echo $this->Html->link(__('ログアウト', true), array('controller' => 'users', 'action' => 'logout')); ?> </li>
    </ul>
   </div>

で、一旦このレイアウトを確認します。 /app/controllers/members_controller.php を開いて、 var $name = 'Members'; の下に、

class MembersController extends AppController {

    var $name = 'Members';

    function beforeFilter() {
        $this->layout = 'admin';
    }
とレイアウトの指定を追加します。(上記5〜7行目)

ブラウザで見てみます。

CakePHP1.3で作る会員管理システム(8) フォームの見た目を変える

フォームの見た目を変える

ということで無事 members の会員登録部分が動くようになりました。 すぐに気づくことですが、デフォルトのテンプレートの見た目が気に入らないです。 会員の一覧画面はともかく、 /members/add の登録画面とか。 見た目の整形は最後にまとめてやっても構わない訳ですが、なんだかこのまま作業するのも気が重い。 ここでちょっといじりたい、というのが人情でしょう。 とりあえず、アクション add の場合で言うと、今自動生成されたテンプレートはこうなっています。

 <fieldset>
  <legend><?php __('Add Member'); ?></legend>
 <?php
  echo $this->Form->input('email');
  echo $this->Form->input('password');
  echo $this->Form->input('type_id');
  echo $this->Form->input('birthday');
  echo $this->Form->input('img1');
  echo $this->Form->input('img2');
  echo $this->Form->input('Favorite');
 ?>
 </fieldset>

fieldset とか legend が気に入らないですね。 ということで、本家サイトのドキュメントにあるとおり、デフォルトのテンプレートをコピーして、編集します。

9.3 Modify default HTML produced by "baked" templates

先に、/app/vendors/shells/templates/ 以下に、自分が作るテンプレート用のディレクトリを作っておきます。 ここでは

/app/vendors/shells/templates/mytheme
としました。

そして、

cd Sites/cake/cake/console/templates/default
cp -r views /Users/myname/Sites/cake/app/vendors/shells/templates/mytheme/views
として、デフォルトのテンプレートを自分のところへコピーします。

上記 views の中にある form.ctp を開いて、

 <fieldset>
  <legend><?php printf("<?php __('%s %s'); ?>", Inflector::humanize($action), $singularHumanName); ?></legend>
<?php
  echo "\t<?php\n";
  foreach ($fields as $field) {
   if (strpos($action, 'add') !== false && $field == $primaryKey) {
    continue;
   } elseif (!in_array($field, array('created', 'modified', 'updated'))) {
    echo "\t\techo \$this->Form->input('{$field}');\n";
   }
  }
  if (!empty($associations['hasAndBelongsToMany'])) {
   foreach ($associations['hasAndBelongsToMany'] as $assocName => $assocData) {
    echo "\t\techo \$this->Form->input('{$assocName}');\n";
   }
  }
  echo "\t?>\n";
?>
 </fieldset>
こうなっているところを、
 <h2><?php printf("<?php __('%s %s'); ?>", Inflector::humanize($action), $singularHumanName); ?></h2>
<?php
  echo "\t<?php\n";
  foreach ($fields as $field) {
   if (strpos($action, 'add') !== false && $field == $primaryKey) {
    continue;
   } elseif (!in_array($field, array('created', 'modified', 'updated'))) {
    echo "\t\techo \$this->Form->input('{$field}');\n";
   }
  }
  if (!empty($associations['hasAndBelongsToMany'])) {
   foreach ($associations['hasAndBelongsToMany'] as $assocName => $assocData) {
    echo "\t\techo \$this->Form->input('{$assocName}');\n";
   }
  }
  echo "\t?>\n";
?>
とします。

fieldset と legend を削除して、h2 に変えたってだけです。 再度 cake bake でビューを作り直します。

前回同様yes-noで進んでいくと、今度は、以下のようにテンプレートが選べるメニューが出ます。

---------------------------------------------------------------
You have more than one set of templates installed.
Please choose the template set you wish to use:
---------------------------------------------------------------
1. mytheme
2. default
Which bake theme would you like to use? (1/2) 
[1] > 1
自分が作ったテンプレートを選びます。

/app/views/members/add.ctp を確認すると、

 <h2>
  <?php __('Add Member'); ?></h2>
 <?php
  echo $this->Form->input('email');
  echo $this->Form->input('password');
  echo $this->Form->input('type_id');
  echo $this->Form->input('birthday');
  echo $this->Form->input('img1');
  echo $this->Form->input('img2');
  echo $this->Form->input('Favorite');
 ?>
自分用のテンプレートが適用されています。


CakePHP1.3で作る会員管理システム(7) Validate

Validate

ということで無事 members の会員登録部分が動くようになりました。
ブラウザから、

http://localhost/~myname/cake/members

を開いて、いろいろクリックしたりしてみます。

で、会員の新規登録

http://localhost/~myname/cake/members/add

をやってみると、custom エラーになって登録ができません。

最初に bake でモデルを作った時に、バリデーションの設定で、email と password に custom をセットして、その内容を入れていないのが原因です。
ということで validate をちゃんと作ります。

/app/models/member.php の、 今こうなっているところを

  'email' => array(
   'custom' => array(
    'rule' => array('custom'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'email' => array(
    'rule' => array('email'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'notempty' => array(
    'rule' => array('notempty'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),

以下のように書き換えます。

  'email' => array(
   'custom' => array(
    'rule' => array('isUnique'),
    'message' => 'このメールアドレスは既に登録されています',
    'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'email' => array(
    'rule' => array('email'),
    'message' => 'メールアドレスを正しく入力してください',
   ),
   'notempty' => array(
    'rule' => array('notempty'),
    'message' => 'メールアドレスを入力してください',
   ),
  ),

パスワードの部分は、以下のように書き換えます。

  'password' => array(
   'custom' => array(
    'rule' => array('custom', '/^[a-zA-Z0-9\_\-]{6,10}$/i'),
    'message' => 'パスワードは6文字以上10文字以内で入力してください',
   ),
   'notempty' => array(
    'rule' => array('notempty'),
    'message' => 'パスワードを入力してください',
   ),
  ),

ソース中に日本語が入りましたので文字コードをutf-8に設定して保存します。
会員の新規登録が出来ることを確認します。


http://localhost/~myname/cake/members/add

2011-09-09

CakePHP1.3で作る会員管理システム(6) bakeする(controller,view)

bakeする(controller,view)

次に bake のメニューから、 C を選んでコントローラを作ります。

> C
---------------------------------------------------------------
Bake Controller
Path: /Users/myname/Sites/cake/app/controllers/
---------------------------------------------------------------
Possible Controllers based on your current database:
1. Favorites
2. Members
3. MembersFavorites
4. Types
5. Users
Enter a number from the list above,
type in the name of another controller, or 'q' to exit  
[q] > 2

モデルの時と同様の選択画面になるので、

2. Members
を選択します。

---------------------------------------------------------------
Baking MembersController
---------------------------------------------------------------
Would you like to build your controller interactively? (y/n) 
[y] > y

とりあえずYESですね。

Would you like to use dynamic scaffolding? (y/n) 
[n] > n

スキャフォルドをするかどうかですが、しないのでNOです。

Would you like to create some basic class methods 
(index(), add(), view(), edit())? (y/n) 
[n] > y

indexとかaddとかの基本のアクションを作るかどうかですね。ここでYESです。

Would you like to create the basic class methods for admin routing? (y/n) 
[n] > y

adminルーティングを使うかどうかです。これはYESです。

Would you like this controller to use other helpers
besides HtmlHelper and FormHelper? (y/n) 
[n] > n
Would you like this controller to use any components? (y/n) 
[n] > n
Would you like to use Session flash messages? (y/n) 
[y] > y

---------------------------------------------------------------
The following controller will be created:
---------------------------------------------------------------
Controller Name:
 Members
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y

その後はそのままです。 /app/controllers/members_controller.php が作られました。

次に、 V を選んでビューを作ります。

> V
---------------------------------------------------------------
Bake View
Path: /Users/myname/Sites/cake/app/views/
---------------------------------------------------------------
Possible Controllers based on your current database:
1. Favorites
2. Members
3. MembersFavorites
4. Types
5. Users
Enter a number from the list above,
type in the name of another controller, or 'q' to exit  
[q] > 2

やはり2番のMembersを選んで進みます。

Would you like bake to build your views interactively?
Warning: Choosing no will overwrite Members views if it exist. (y/n) 
[n] > y

ここはYESにします。

Would you like to create some CRUD views
(index, add, view, edit) for this controller?
NOTE: Before doing so, you'll need to create your controller
and model classes (including associated models). (y/n) 
[y] > y

ここもとりあえずYESにします。

Would you like to create the views for admin routing? (y/n) 
[n] > y

admin 用のviewを作るかどうかです。これもYESにします。

/app/views/ 以下に members というフォルダーが作られ、 index.ctp 他の各アクション用ビューが自動生成されます。 登録フォームの view は以下のようになりました。

<div class="members form">
<?php echo $this->Form->create('Member');?>
 <fieldset>
  <legend><?php __('Add Member'); ?></legend>
 <?php
  echo $this->Form->input('email');
  echo $this->Form->input('password');
  echo $this->Form->input('type_id');
  echo $this->Form->input('birthday');
  echo $this->Form->input('img1');
  echo $this->Form->input('img2');
  echo $this->Form->input('Favorite');
 ?>
 </fieldset>
<?php echo $this->Form->end(__('Submit', true));?>
</div>
<div class="actions">
 <h3><?php __('Actions'); ?></h3>
 <ul>

  <li><?php echo $this->Html->link(__('List Members', true), array('action' => 'index'));?></li>
  <li><?php echo $this->Html->link(__('List Types', true), array('controller' => 'types', 'action' => 'index')); ?> </li>
  <li><?php echo $this->Html->link(__('New Type', true), array('controller' => 'types', 'action' => 'add')); ?> </li>
  <li><?php echo $this->Html->link(__('List Favorites', true), array('controller' => 'favorites', 'action' => 'index')); ?> </li>
  <li><?php echo $this->Html->link(__('New Favorite', true), array('controller' => 'favorites', 'action' => 'add')); ?> </li>
 </ul>
</div>

users についても同じようにしてコントローラとビューを作ります。

2011-09-05

CakePHP1.3で作る会員管理システム(5) bakeする(model)

bakeする(model)

続いてbakeしてモデル、コントローラ、ビューを作ります。
作るんですが、その前にマスターにデータを入れておきます。
テーブルの types と favorites は、値が入ってるだけのマスターテーブルです。
今回これの管理機能は提供しません。
ということで最初にテーブルにデータを入れておきます。
別に普通にSQLのinsert文で入れておくだけです。

INSERT INTO types (id, name) VALUES (1, '一般');
INSERT INTO types (id, name) VALUES (2, '専門');
INSERT INTO favorites (id, name) VALUES (1, 'apple');
INSERT INTO favorites (id, name) VALUES (2, 'orange');
INSERT INTO favorites (id, name) VALUES (3, 'grape');

で、最初にmembersテーブルのモデルから作ります。

cd Sites/cake/app
cake bake
と打つと、
Welcome to CakePHP v1.3.11 Console
---------------------------------------------------------------
App : app
Path: /Users/myname/Sites/cake/app
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> 
と表示されて入力待ちになるので、 M を入れます。
> M
---------------------------------------------------------------
Bake Model
Path: /Users/myname/Sites/cake/app/models/
---------------------------------------------------------------
Possible Models based on your current database:
1. Favorite
2. Member
3. MembersFavorite
4. Type
5. User
Enter a number from the list above,
type in the name of another model, or 'q' to exit  
[q] >
となるのでここで2番を選びます。

あとは対話しながら選択していくだけです。

A displayField could not be automatically detected
would you like to choose one? (y/n) 
> y
displayField が自動で見つけられなかった、自分で選ぶかい? と聞かれるのでyesとすると、
1. id
2. email
3. password
4. type_id
5. birthday
6. img1
7. img2
8. created
9. modified
Choose a field from the options above:  
> 2
members テーブルのカラム名が一覧されるので、2番の email を選択します。

Would you like to supply validation criteria 
for the fields in your model? (y/n) 
[y] > y
続いてバリデーションの設定をする。
Field: id
Type: integer
---------------------------------------------------------------
Please select one of the following validation options:
---------------------------------------------------------------
1 - alphanumeric
2 - between
3 - blank
4 - boolean
5 - cc
6 - comparison
7 - custom
8 - date
9 - decimal
10 - email
11 - equalto
12 - extension
13 - inlist
14 - ip
15 - maxlength
16 - minlength
17 - money
18 - multiple
19 - notempty
20 - numeric
21 - phone
22 - postal
23 - range
24 - ssn
25 - time
26 - url
27 - userdefined
28 - uuid
29 - Do not do any validation on this field.
... or enter in a valid regex validation string.
  
[29] > 
というように、カラム id について1から29までのルールが表示されるので、その中から適用するルールを選びます。 [] 内に表示されているのはcakephpが推測する値です。 一回選ぶ度に、
Would you like to add another validation rule? (y/n) 
[n] > 
と、追加のルールがあるかどうか聞いてくるので、ルールは複数選べます。 とりあえず、
id:
29
email:
7, 10, 19
password:
7, 19
type_id:
20
birthday:
8
img1:
29
img2:
29
created:
29
modified:
29
と、番号を選択して進みます。

Would you like to define model associations
(hasMany, hasOne, belongsTo, etc.)? (y/n) 
[y] > 
関連づけをするかどうかの確認です。YESです。
One moment while the associations are detected.
---------------------------------------------------------------
Please confirm the following associations:
---------------------------------------------------------------
Member belongsTo Type? (y/n) 
[y] > y
Member hasAndBelongsToMany Favorite? (y/n) 
[y] > y
Would you like to define some additional model associations? (y/n) 
[n] > n
とすると、
---------------------------------------------------------------
The following Model will be created:
---------------------------------------------------------------
Name:       Member
DB Table:   `members`
Validation: Array
(
    [email] => Array
        (
            [custom] => custom
            [email] => email
            [notempty] => notempty
        )

    [password] => Array
        (
            [custom] => custom
            [notempty] => notempty
        )

    [type_id] => Array
        (
            [numeric] => numeric
        )

    [birthday] => Array
        (
            [date] => date
        )

)

Associations:
 Member belongsTo Type
 Member hasAndBelongsToMany Favorite
---------------------------------------------------------------
Look okay? (y/n) 
[y] > 
とでるので、yesすると、
Baking model class for Member...

Creating file /Users/myname/Sites/cake/app/models/member.php
Wrote `/Users/myname/Sites/cake/app/models/member.php`
モデルが作られました。

最後にtestも作っておきます。

SimpleTest is not installed. Do you want to bake unit test files anyway? (y/n) 
[y] > y

You can download SimpleTest from http://simpletest.org

Baking test fixture for Member...

Creating file /Users/myname/Sites/cake/app/tests/fixtures/member_fixture.php
Wrote `/Users/myname/Sites/cake/app/tests/fixtures/member_fixture.php`
Bake is detecting possible fixtures..

Creating file /Users/myname/Sites/cake/app/tests/cases/models/member.test.php
Wrote `/Users/myname/Sites/cake/app/tests/cases/models/member.test.php`

作成された /app/models/member.php の中味はこんな感じです。

class Member extends AppModel {
 var $name = 'Member';
 var $displayField = 'email';
 var $validate = array(
  'email' => array(
   'custom' => array(
    'rule' => array('custom'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'email' => array(
    'rule' => array('email'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'notempty' => array(
    'rule' => array('notempty'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
  'password' => array(
   'custom' => array(
    'rule' => array('custom'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
   'notempty' => array(
    'rule' => array('notempty'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
  'type_id' => array(
   'numeric' => array(
    'rule' => array('numeric'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
  'birthday' => array(
   'date' => array(
    'rule' => array('date'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
 );
 //The Associations below have been created with all possible keys, those that are not needed can be removed

 var $belongsTo = array(
  'Type' => array(
   'className' => 'Type',
   'foreignKey' => 'type_id',
   'conditions' => '',
   'fields' => '',
   'order' => ''
  )
 );

 var $hasAndBelongsToMany = array(
  'Favorite' => array(
   'className' => 'Favorite',
   'joinTable' => 'members_favorites',
   'foreignKey' => 'member_id',
   'associationForeignKey' => 'favorite_id',
   'unique' => true,
   'conditions' => '',
   'fields' => '',
   'order' => '',
   'limit' => '',
   'offset' => '',
   'finderQuery' => '',
   'deleteQuery' => '',
   'insertQuery' => ''
  )
 );

}
よさそうな感じですね。

あとは同様にusersテーブルについても作っておきます。関連づけはなしですね。

class User extends AppModel {
 var $name = 'User';
 var $displayField = 'name';
 var $validate = array(
  'username' => array(
   'notempty' => array(
    'rule' => array('notempty'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
  'password' => array(
   'notempty' => array(
    'rule' => array('notempty'),
    //'message' => 'Your custom message here',
    //'allowEmpty' => false,
    //'required' => false,
    //'last' => false, // Stop validation after this rule
    //'on' => 'create', // Limit validation to 'create' or 'update' operations
   ),
  ),
 );
}


CakePHP1.3で作る会員管理システム(4) schemaを作る

schemaを作る

ここでついでなので schema を作っておきます。 自分は今 app フォルダーの中にいるので、そのまま

cake schema generate -f
と打ちます。
Welcome to CakePHP v1.3.11 Console
---------------------------------------------------------------
App : app
Path: /Users/myname/Sites/cake/app
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------
Generating Schema...
Schema file: schema.php generated
とメッセージが出て終了。

/app/config/schema/schema.php というファイルが生成されます。 中味はこんな感じ。

class AppSchema extends CakeSchema {
 var $name = 'App';

 function before($event = array()) {
  return true;
 }

 function after($event = array()) {
 }

 var $favorites = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
 );
 var $members = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'type_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'),
  'birthday' => array('type' => 'date', 'null' => true, 'default' => NULL),
  'img1' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'img2' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
  'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'type_id' => array('column' => 'type_id', 'unique' => 0)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
 );
 var $members_favorites = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'member_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'),
  'favorite_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'member_id' => array('column' => 'member_id', 'unique' => 0), 'favorite_id' => array('column' => 'favorite_id', 'unique' => 0)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
 );
 var $types = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
 );
 var $users = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'name' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'email' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
 );
}
これを作っておけば、環境が変わって、データベースがMySQLからPostgreSQLに変わったりしても、すぐにデータベースを構築できます。

その場合は、

cake schema create
と打ちます。

例えばこいつをPostgreSQLの環境で構築したデータベースはこうなりました。

cake_db=# ¥d
                    List of relations
 Schema |           Name           |   Type   |  Owner
--------+--------------------------+----------+----------
 public | favorites                | table    | postgres
 public | favorites_id_seq         | sequence | postgres
 public | members                  | table    | postgres
 public | members_favorites        | table    | postgres
 public | members_favorites_id_seq | sequence | postgres
 public | members_id_seq           | sequence | postgres
 public | types                    | table    | postgres
 public | types_id_seq             | sequence | postgres
 public | users                    | table    | postgres
 public | users_id_seq             | sequence | postgres
(10 rows)

cake_db=# ¥d users
                                  Table "public.users"
  Column  |          Type          |                     Modifiers

----------+------------------------+----------------------------------------------------
 id       | integer                | not null default nextval('users_id_seq'::regclass)
 username | character varying(100) | not null
 password | character varying(100) | not null
 name     | character varying(100) | default NULL::character varying
 email    | character varying(100) | default NULL::character varying
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)

cake_db=# ¥d members
                                    Table "public.members"
  Column  |            Type             |                      Modifiers
----------+-----------------------------+------------------------------------------------------
 id       | integer                     | not null default nextval('members_id_seq'::regclass)
 email    | character varying(100)      | not null
 password | character varying(100)      | not null
 type_id  | integer                     | not null
 birthday | date                        |
 img1     | character varying(255)      | default NULL::character varying
 img2     | character varying(255)      | default NULL::character varying
 created  | timestamp without time zone | not null
 modified | timestamp without time zone | not null
Indexes:
    "members_pkey" PRIMARY KEY, btree (id)
    "type_id" btree (type_id)

cake_db=# ¥d favorites
                                 Table "public.favorites"
 Column |          Type          |                       Modifiers
--------+------------------------+--------------------------------------------------------
 id     | integer                | not null default nextval('favorites_id_seq'::regclass)
 name   | character varying(100) | not null
Indexes:
    "favorites_pkey" PRIMARY KEY, btree (id)

cake_db=# ¥d types
                                 Table "public.types"
 Column |          Type          |                     Modifiers
--------+------------------------+----------------------------------------------------
 id     | integer                | not null default nextval('types_id_seq'::regclass)
 name   | character varying(100) | not null
Indexes:
    "types_pkey" PRIMARY KEY, btree (id)

cake_db=# ¥d members_favorites
                            Table "public.members_favorites"
   Column    |  Type   |                           Modifiers
-------------+---------+----------------------------------------------------------------
 id          | integer | not null default nextval('members_favorites_id_seq'::regclass)
 member_id   | integer | not null
 favorite_id | integer | not null
Indexes:
    "members_favorites_pkey" PRIMARY KEY, btree (id)
    "favorite_id" btree (favorite_id)
    "member_id" btree (member_id)

MySQL からPostgreSQL へデータベースが変わるなんてことはないかもしれないし、後でやっても構わない訳ですが、作っておけば後々便利ということでせっかく今ターミナルを開いてる訳ですから schema だけは作っておいて損はないだろうと思います。
データベースは MySQL のままでも、開発用環境を移す時にコマンド一発でデータベースを作れてしまうのでとても楽ですね。

ちなみに、

cake schema create
の実行は、まず既存のテーブルを削除してから、新規にテーブルを作成する、という処理になるので、テーブルが存在していないのに、「削除しますか?」 で「はい」と返事すると、だーっとエラーが出ます。 が、特に問題はありません。
Welcome to CakePHP v1.3.11 Console
---------------------------------------------------------------
App : app
Path: /Users/myname/Sites/cake/app
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------

The following table(s) will be dropped.
favorites
members
members_favorites
types
users
Are you sure you want to drop the table(s)? (y/n) 
[n] > y
Dropping table(s).

ここでWarning発生!

The following table(s) will be created.
favorites
members
members_favorites
types
users
Are you sure you want to create the table(s)? (y/n) 
[y] > 
Creating table(s).
favorites updated.
members updated.
members_favorites updated.
types updated.
users updated.
End create.

このブログを検索

Powered by Blogger.

ラベル

php (17) jQuery (13) OSX (10) MySQL (8) Javascript (7) Postgres (7) port (7) apache (6) Java (3) Smarty (2) html (2) pear (2) FCKEditor (1) XAMPP (1) css (1) git (1) perl (1) ruby (1)

Facebookバナー