CakePHP1.3.6:Media Plugin1.3RCに挫折して、ファイルアップロードを自作(その2)

前回の続き。
まず、以下のアップロード画面を作る。これがメイン。

プログラム名はDocとするので、/app/controllersにdoc_controller.phpを作成する。

ビュー

/app/views/docsにadd.ctpを作る。
ファイルを複数uploadするには、DocImage.0.fileと同じ部分をコピーして、DocImage.1.fileとする。

<?php 
echo $form->create(null,array('type'=>'file','action'=>'add'));
?>
<table>

<?php 
echo "<tr><th>商品名</th>";
echo "<td>{$form->text('Doc.title',array('size'=>'20'))}{$form->error('Doc.title')} </td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$form->textarea('Doc.description')}</td>";
echo "</tr>";
echo "<tr><th>ファイル</th>";
echo "<td>{$form->file("DocImage.0.file")}{$form->error('DocImage.path')}</td>";
echo "</tr>";
echo $form->end('登録');
?>
</table>

アクション

doc_controller.phpにadd()アクションを追加する。面倒くさいのでコントローラー全部を以下に書く。
以下では、120x90に画像を圧縮するようにしている。hasManyを定義しているので、Doc->savaAllでDocImageも保存される。

<?php
require_once '../vendors/MyImageConv.class.php';
require_once '../vendors/MyConverter.class.php';

class DocsController extends AppController{
	
	public $name = 'Docs';
	public $uses = array('Doc','DocImage');
	public $layout = 'myznala';
	public $autoLayout = true;
	public $autoRender = true;
	
	public static $tmp_dir 	= './tmp/';	// 画像データの一時保管先
	public static $save_dir 	= './img/';	// 画像データの保管先
	public static $width 	= 120;			// 画像データの幅
	public static $height 	= 90;			// 画像データの高さ
	
	/*
	 * Paginatorの定義
	 */
	public $paginate = array(
			'page'=>1,
			'conditions'=>array(),
			'fields'=>array(),
			'order'=>array('Doc.timestamp'=>'desc'),
			'limit'=>5,
			'recursive'=>1 // has manyのときは1
	);
	
	/**
	 * 
	 * 初期画面(一覧表示)
	 */
	function index(){
		$this->set('title_for_layout', "画像の一覧");
		$req=null;
		if(!empty($this->data)){
			//サニタイズ
			$req = MyConverter::getRequestParams($this->data["Doc"]);
			//絞り込みの場合には、コンディションを書き換える。
			$this->paginate['conditions'] = array('Doc.title like ?' => array("%{$req["title"]}%"));
		}
		$data = $this->paginate();
		
		$this->set('data',$data);
	}
	
	/**
	 * 
	 * 登録
	 * 
	 * (注意);ファイルの保存場所とDocImageのパスについて
	 *  このサンプルでは、ファイルをwebroot/imgに保存する。このとき、$save_dirは./imgとするが、DocImage
	 *   にはファイル名のみを保存する。これは、$html->image()が、./imgまでを自動で補完するため。
	 *   もし、webroot/img/[some dir]/に配置する場合には、$save_dirを./img/[some dir], DocImage
	 *   には[somedir]/[filename]を保管すること。
	 * 
	 */
	function add(){
		$this->set('title_for_layout', "画像の登録");
		
		if(!empty($this->data)){
			// サニタイズ
			$this->data["Doc"] = MyConverter::getRequestParams($this->data["Doc"]);

			// 写真の処理			
			$filename	= $this->data['DocImage'][0]['file']['name'];
			// ファイルのアップロードチェック
			if(strlen(trim($filename))==0){
				$this->DocImage->invalidate('path','画像を選択してください');

			//(注意) 事前にinvalidateを使ってエラーチェックをする場合には、if..elseで分岐していかないと、
			// save時のバリデーションがきかなくなってしまう。
			}else{
			
				$filename		= $this->data['DocImage'][0]['file']['name'];
				$from_path 	= self::$tmp_dir.$filename;
				$to_path 		= self::$save_dir.$filename;
				
				if(move_uploaded_file($this->data['DocImage'][0]['file']['tmp_name'],$from_path)){
					
					// イメージの圧縮
					$ret = MyImageConv::imageConv($from_path, $to_path, self::$width, self::$height);
					
					if(!$ret) {
						$this->DocImage->invalidate('path','画像の形式が違います');
						//(注意) 事前にinvalidateを使ってエラーチェックをする場合には、if..elseで分岐していかないと、
						// save時のバリデーションがきかなくなってしまう。
					}else{
						$this->data['DocImage'][0]['path'] = $filename;
						// 登録
						$this->Doc->saveAll($this->data);
						if($this->Doc->validates()){
							// 登録できたらリダイレクト
							$this->redirect('.');
						}
					}
				}
			}
		}
	}
	
	/**
	 * 
	 * データの照会
	 * $paramsにIdが入ってくる。
	 * 
	 */
	function show($param){
		$this->set('title_for_layout', "画像の詳細");
		$result=null;
		if(!empty($param)){
			// Idフィールドによる検索
			$this->data=$this->Doc->findById($param);
			if(!$this->data){
				// 存在しないIDを参照された場合。
				$this->Session->setFlash('存在しません');
				$this->redirect('.');
			}
		}
		$this->set('data',$this->data);
	}

	/**
	 * 
	 * データの更新
	 * 
	 */
	function edit($param){
		$this->set('title_for_layout', "画像情報の更新");
		// データの更新
		if(!empty($this->data)){
			// サニタイズ
			$this->data["Doc"] = MyConverter::getRequestParams($this->data["Doc"]);
			// Idフィールドによる検索
			$check=$this->Doc->findById($param);
			if(!$check){
				// 存在しないIDを更新しようとした場合。
				$this->Session->setFlash('存在しません');
				$this->redirect('.');
			}else{
				// 更新
				$this->Doc->save($this->data);
				if($this->Doc->validates()){
					// 更新できたらリダイレクト
					$this->redirect('.');
				}else{
					// 入力エラーの時、再検索して入力画面に戻す。
					// これをやらないと、画像が表示できなくなる。
					$this->data=$this->Doc->findById($param);
				}
			}
		// 更新データのセット(初期値)
		}else{
			if(!empty($param)){
				// Idフィールドによる検索
				$check=$this->Doc->findById($param);
				if(!$check){
					// 存在しないIDを更新しようとした場合。
					$this->Session->setFlash('存在しません');
					$this->redirect('.');
				}else{
					// Idフィールドによる検索
					$this->data=$this->Doc->findById($param);
				}
			}
		}
		
		/*
		 * 入力エラーの場合も通るようにする
		 */ 
		$this->set('data',$this->data);
	}

	/**
	 * 
	 * データの削除
	 * 
	 */
	function delete($param){
		if(!empty($param)){
			$check=$this->Doc->findById($param);
			if(!$check){
				// 存在しないIDを削除しようとした場合。
				$this->Session->setFlash('存在しません');
			}else{
				// Idフィールドによる削除
				$this->data=$this->Doc->findById($param);
				$ret=$this->Doc->delete($param);
				// ファイルの削除
				$path = self::$save_dir.$this->data['DocImage'][0]['path'];
				unlink($path);				
			}
		$this->redirect('.');
		}
	}
}
?>

インデックス画面

インデックス画面は以下。

ビュー

index.ctpは以下。

<h2>画像の一覧</h2>
<br>
タイトルで絞り込みをします。
<?php 
// 絞り込み用フォーム
echo $form->create(null,array('type'=>'post','action'=>'.'));
echo $form->text('Doc.title', array('size' => 10));
echo $form->end('送信');
?>

<?php 
// ページネーション
echo $paginator->numbers(
	array(
		'before'=>$paginator->hasPrev() ? $paginator->first('<<').' ' : '',
		'after'=>$paginator->hasNext() ? ' '.$paginator->last('>>') : '',
		'modulus'=>4,
		'separator'=>' '
	)
);
?>

<table>
<tr>
	<th><?php echo $paginator->sort('ID','Doc.id') ?></th>
	<th><?php echo $paginator->sort('タイトル','Doc.title') ?></th>
	<th>メモ</th>
	<th>画像</th>
	<th><?php echo $paginator->sort('更新日','Doc.timestamp') ?></th>
</tr>
<?php 
	foreach ($data as $arr){
		echo '<tr>';
		echo '<td>'.$html->link($arr['Doc']['id'],
  				array('controller'=>'docs', 'action'=>'show',$arr['Doc']['id'])).'</td>';
		echo "<td>{$arr['Doc']['title']}</td>";
		echo "<td>".mb_strimwidth($arr['Doc']['description'],0,50,'...')."</td>";
		echo "<td>";
		foreach ($arr['DocImage'] as $img){
			echo $html->image($img['path']);
			echo "&nbsp;";
		}
		echo "</td>";
		echo "<td>{$arr['Doc']['timestamp']}</td>";
		echo '</tr>';
	}
?>
</table>
<?php
echo $html->link('登録',
  array('controller'=>'docs', 'action'=>'add'));
?>

参照画面

上の一覧表示画面のIDにあるリンクをクリックすると以下の照会画面に遷移する。

ビュー

show.ctpは以下。

<h2>画像の詳細</h2>
<br>
<table>

<?php 
echo "<tr><th>名前</th>";
echo "<td>{$data['Doc']['title']}</td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$data['Doc']['description']}</td>";
echo "</tr>";
echo "<tr><th>画像</th>";
echo "<td>";
	foreach ($data['DocImage'] as $img){
		echo $html->image($img['path']);
		echo "&nbsp;";
	}
echo "</td>";
echo "</tr>";
echo "<tr><th>最新更新日</th>";
echo "<td>{$data['Doc']['timestamp']}</td>";
echo "</tr>";
?>
</table>
<a href="../">リストに戻る</a>
&nbsp; &nbsp;
<?php
echo $html->link('更新',
  array('controller'=>'docs', 'action'=>'edit', $data["Doc"]["id"]));
?>

更新画面

更新画面は以下。画像の入れ替えは面倒なのでできない仕様とした。その場合には削除後に再登録することになる。

ビュー

edit.ctpは以下。

<h2>画像情報の更新</h2>
<?php 
echo $form->create('Doc',array('type'=>'post','action'=>'edit'));
echo $form->hidden('Doc.id');
?>

<table>

<?php 
echo "<tr><th>タイトル</th>";
echo "<td>{$form->text('Doc.title',array('size'=>'20'))}{$form->error('Doc.title')} </td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$form->textarea('Doc.description')}</td>";
echo "</tr>";
echo "<tr><th>画像</th>";
echo "<td>";
	foreach ($data['DocImage'] as $img){
		echo $html->image($img['path']);
		echo "&nbsp;";
	}
echo "</td>";
echo "</tr>";
echo $form->end('更新');
?>
</table>
<a href="../">リストに戻る</a>
&nbsp; &nbsp;
<?php
echo $html->link('削除',
  array('controller'=>'docs', 'action'=>'delete', $data["Doc"]["id"]),
  array(),
 "削除してもいいですか?");
?>