FuelPHPでセレクトラベルの生成(実践編その7)

今日は、フォーム内でデータベースからリストを引っ張ってきて、セレクト用のラベルを生成する方法を調べてみたいと思います。まず最初にcollectionsテーブルと種別を記述したテーブルcol_assortsをリレーションでつなげてみたいと思います。

1. Ormのリレーションの設定は、collectionsテーブルのcol_assort_idにcol_assortsテーブルのidをつなげますので、$_belongs_toを使います。リレーションの種類については、『FuelPHPのOrm』を参照して下さい。

2. まず先にモデルcolassort.phpを作成して、下記のように記述します。データベーステーブル名及びフィールド名は、CakePHPの命名規則に則って作成していますので、その内容に応じてモデル内に記述します。

app/classes/model/colassort.php

<?php
 class Model_Colassort extends Orm\Model{
 //使用するフィールド名をセット
 protected static $_properties = array(
 'id',
 'as_name',
 );
 //テーブル名をセット
 protected static $_table_name = 'col_assorts';
 //プライマリーキーをセット
 protected static $_primariy = array('id');
 }

3. 基本となるモデルcollection.phpを修正します。今回の変更は、20行目から27行目です。

app/classes/model/collection.php

<?php
 class Model_Collection extends Orm\Model{
 //使用するフィールド名をセット
 protected static $_properties = array(
 'id',
 'title',
 'created',
 'modified',
 'col_code',
 'save_space',
 'col_assort_id',
 'col_image_id',
 'note',
 );
 //テーブル名がモデル名の複数形なら省略可
 protected static $_table_name = 'collections';
 //プライマリーキーがidなら省略可
 protected static $_primariy = array('id');
 //belongsToの設定
 protected static $_belongs_to = array(
 'colas'=>array(
 'key_from'=>'col_assort_id',
 'model_to'=>'Model_Colassort',
 'key_to'=>'id',
 'cascade_save'=>true,
 'cascade_delete'=>false,
 ));
 //バリデーションの設定
 public static function validate($factory)
 {
 $val = Validation::forge($factory);
 $val->add_field('title', 'タイトル', 'required|max_length[255]');
 $val->add_field('col_code', 'コード番号', 'required');
 $val->add_field('save_space', '保管場所', 'required|max_length[100]');
return $val;
 }
 }

4. ビューファイルindex.phpも修正します。修正箇所は26行目です。

app/views/collection/index.php

<h3><?php echo Session::get_flash('success','コレクション一覧');?></h3>
 <?php echo Pagination::create_links();?>
 <?php if($sort='asc'){
 $sort='desc';
 }else{
 $sort='asc';}?>
 <table width="100%" border="1">
 <tr>
 <th scope="col">ID</th>
 <th scope="col">タイトル</th>
 <th scope="col" class="sp_none">作成日</th>
 <th scope="col" class="sp_none">コード</th>
 <th scope="col">保管場所</th>
 <th scope="col">種別</th>
 </tr>
 <?php foreach($collections as $row): ?>
 <tr>
 <td><?=$row->id?></td>
 <td><?php echo Html::anchor('collection/detail/'.$row->id,
 $row->title,array(
 'class'=>'btn btn-success','id'=>'a_btn',
 ))?></a></td>
 <td><?=$row->created?></td>
 <td><?=$row->col_code?></td>
 <td><?=$row->save_space?></td>
 <td><?php echo (empty($row->colas->as_name)) ? '' : $row->colas->as_name;?></td>
 </tr>
 <?php endforeach;?>
 </table>

5. ブラウザで確認してみます。indexページには、col_assortsテーブルのas_nameを表示できるようになりました。

6. それでは、本日の本題の、入力フォームのセレクトラベルに、col_assortsテーブルのas_nameを一覧として表示するように作成してみましょう。入力用のaddアクションを下記のように修正します。修正箇所は38行目から44行目です。

app/classes/controller/collections.php 

public function action_add($id=null){
 //もしPOST送信されたら
 if (Input::method() == 'POST')
 {
 $val = Model_Collection::validate('add');

 if ($val->run())
 {
 $collection = Model_Collection::forge(array(
 'title' => Input::post('title'),
 'created' => date('Y-m-d H:i:s'),
 'modified' => date('Y-m-d H:i:s'),
 'col_code' => Input::post('col_code'),
 'save_space' => Input::post('save_space'),
 'col_assort_id' => Input::post('col_assort_id'),
 'picture_id' => Input::post('picture_id'),
 'note' => Input::post('note'),
 ));

 //もしデータが保存されたら
 if ($collection and $collection->save())
 {
 Session::set_flash('success', '<span class="btn btn-primary span8">ID-'.$collection->id.'の『'.$collection->title.'』を追加しました</span><br>');
 //indexページへ移動
 Response::redirect('collection/index');
 }
 else //データが保存されなかったら
 {
 Session::set_flash('error', '保存できませんでした');
 }
 }
 else //バリデーションNGなら
 {
 Session::set_flash('error', $val->show_errors());
 }
 }
 //モデルColassrotからデータを取得
 $colassorts=Model_Colassort::find('all');
 foreach($colassorts as $row):
 //id番号をキーにして配列assortsを作成
 $data['assorts'][$row->id]=$row->as_name;
 endforeach;
 //テンプレートファイルにデータを引き渡す
 $this->template->content = View::forge('collection/add',$data);
 }

7. ビューファイルadd.phpも下記のように修正します。修正箇所は12行目です。

app/views/collection/add.php

<h3>新規作成</h3>
<?php echo Form::open(array('name'=>'add','method'=>'post')); ?>
<?php echo '<div class="alert-error">'.Session::get_flash('error').'</div>'?>
 <table width="100%" border="0">
 <tr>
 <th align="right" scope="row">タイトル:</th>
 <td class="input"><?php echo Form::input('title', Input::post('title', isset($collection) ? $collection->title : ''), array('class' => 'span6')); ?>
&nbsp;</td>
 </tr>
 <tr>
 <th align="right" scope="row">種別:</th>
 <td><?php echo Form::select('col_assort_id', null,$assorts,array('class'=>'span3')); ?>
&nbsp;</td>
 </tr>
 <tr>
 <th align="right" scope="row">コード番号:</th>
 <td><?php echo Form::input('col_code', Input::post('col_code', isset($collection) ? $collection->col_code : ''), array('class' => 'span6')); ?>
&nbsp;</td>
</tr>
 <tr>
 <th align="right" scope="row">保管場所:</th>
 <td><?php echo Form::input('save_space', Input::post('save_space', isset($collection) ? $collection->save_space : ''), array('class' => 'span6')); ?>
&nbsp;</td>
 </tr>
 <tr>
 <th align="right" scope="row">備考:</th>
 <td><?php echo Form::textarea('note', Input::post('note', isset($collection) ? $collection->note : ''), array('class' => 'span6', 'rows' => 8)); ?>
&nbsp;</td>
 </tr>
 <tr>
 <th colspan="2" scope="row">
 <?php echo Form::submit('submit', '保存', array('class' => 'btn btn-primary span4')); ?>
 </th>
 </tr>

</table>
<?php echo Form::close(); ?>

8. それでは、ブラウザで確認してみます。

9. ブラウザのソースを確認してみます。col_assortsテーブルのidがvalueに指定されています。

10. それでは、データを入力してみましょう。

11. きちんとデータが入力されています。

ここまで、結構かかっていますが、CakePHPでは、命名規則に則って作成していれば、参照用モデル(Bモデル)も作成する必要は無いですし、ほんの数分で完成しました。モデルが絡む処理は、CakePHPが格段に簡単ですね。

本日のコード解説

  •  $_belongs_to=array()
    一対複数のリレーションを設定します。CakePHPでは、アソシエーションという仕組みでbelongsTo名で定義されていました。関連づけを表す外部キーは主モデル(Aモデル)にあります。つまりBモデル内の行をAモデル内のデータとしてリレーションします。
  • ‘colas’=>array()
    リレーション名(ここでは、colasとしていますが、FuelPHPの命名規則に則れば、テーブル名と同じになると思うのですが、そこまでは調べていませんので、悪しからず)を指定して、配列内に条件を付加します。
  • ‘key_from’=>’col_assort_id
    $key_fromに主モデルのキーとなるフィールド名を指定します。
  • ‘model_to’=>’Model_colassort’
    $model_toに、データを参照してくるモデル名を指定します。
  • ‘key_to’=>’id’
    主モデル(Aモデル)のキーとJOINする参照モデル(Bモデル)のキーを指定します。基本的には、ここは、idになると思います。
  • ‘cascade_save’=>true
    主モデル(Aモデル)の保存時に、参照モデル(Bモデル)も保存する場合は、true、そうでないときはfalseを指定します。Aモデルからも変更できるようにtrueにしています。
  •  ‘cascade_delete’=>false
    主モデル(Aモデル)の削除時に、 参照モデル(Bモデル)も削除する場合は、true、そうでないときはfalseを指定します。テーブルAの1行を削除したときに、テーブルBの関連行を削除すると困るので、基本的にここはfalseだと思います。
  • $row->colas->as_name
    主モデルのデータは$row->save_spaceの用に直接フィールドを指定できますが、参照モデルのデータを表示する場合は、$row->リレーション名->フィールド名(プロパティ)のように、間にリレーション名を挟んで指定する必要があります。尚、ビューファイルの26行目は、Aモデルのcol_assort_idがBモデルのidと一致しない場合はデータ自体が存在しないので、エラーが表示されます。そこで、データ自体が存在しないときは、空白を表示して、データが存在するときは、Bモデルのas_nameを表示するようにしました。
  • $colassorts=Model_Colassort::find(‘all’)
    一端、参照用Bモデル(Colassort.php)から全データを取得します。
  • $data[‘assorts’][$row->id]=$row->as_name;
    foreach構文で、取得したデータをid番号をキーにした値(as_name)を配列assortsにセットし直します。
  • Form::select(‘col_assort_id’,null,$assorts,array(‘class’=>’span3’))
    Formクラスのselectメソッドは、第1引数に、<select>タグのname属性に入るフィールド名を指定します。第2引数は、最初から選択された値のデフォルト値です。今回は、nullを指定していますが、ここを2に指定しますと、value=2の箇所にselected=”select”が付加されます。第3引数に連想配列を指定することで、連想配列の数だけ<option>タグを生成します。つまり、$assortsは、コントローラcollection.phpの44行目で作成した配列により、value=1に対して、値1の『洋画』、value=2に対して、値2の『邦画』を表示する<option>を配列の数だけ(value=1からvalue=10まで)生成しています。第4引数は、配列でHTMLタグを指定します。ここでは、’class’=>’span3’を指定していますが、これは、class=span3のタグを生成します。ちなみにspan3はtwitterBootstrapで12スパンの内の3スパン分の大きさを表示します。

本日は、以上です。

このエントリーを含むはてなブックマーク Buzzurlにブックマーク livedoorクリップ Yahoo!ブックマークに登録

トラックバック&コメント

この投稿のトラックバックURL:

トラックバック

  1. […] こちらの記事などを参考にさせていただき、まずOrm\Morelからextendしたモデルクラスで$_belongs_toなどを設定するのはわかりました。 記事ではそれだけで出来るように書いてあるんですが、 […]

コメント


コメントをどうぞ

このページの先頭へ