WEBOPIXEL

CakePHPのhasAndBelongsToMany(HABTM)をチェックボックスで関連付ける

Posted: 2011.01.31 / Category: CakePHP 

CakePHPでブログみたいなことをやろうとすると、ポストとカテゴリーはhasAndBelongsToMany(HABTM)で関連付けすると思います。
で、操作的には記事を書いて、カテゴリーはチェックボックスで選択するよなーっと思ったのですが、僕くらいしょぼいレベルだとおもいっきりはまってしまったのでメモしておきます。
CakePHPのバージョンは1.3です。

Sponsored Link

データベース

記事のデータを入れる「Posts」、タグ(カテゴリー)データを入れる「Tags」、関連付け用の「PostsTags」があります。

Posts

id bigint(20)
name varchar(100)

Tags

id bigint(20)
name varchar(100)

PostsTags

post_id bigint(20)
tag_id varchar(100)

最低限こんなとこでしょうか。

Model

hasAndBelongsToMany(HABTM)はpostに設定します。

models > post.php

		class Post extends AppModel {
			public $name = 'Post';
			public $hasAndBelongsToMany = array(
				'Tag' => array(
				'className' => 'Tag',
				'joinTable' => 'posts_tags',
				'foreignKey' => 'post_id',
				'associationForeignKey' => 'tag_id',
				'fields'        => array('id','name'),
				'unique' => true
				),
			);
		}
	

models > tag.php

		class Tag extends AppModel {
			public $name = 'Tag';
		}
	

models > posts_tag.php

		class PostsTag extends AppModel {
			public $name = 'PostsTag';
		}
	

Controler

controllers > posts_controller.php

	class PostsController extends AppController {
		public $name = "posts";
		public $uses = array("Post", "Tag", "PostsTag");
		/* レコードの追加
		============================================== */
		function add() {
			if (!empty($this->data)) {
				$this->Post->save($this->data);
			}
			//Tagデータを渡す
			$tagData = $this->Tag->find('list');
			$this->set('tagData', $tagData);
		}
	}
	

チェックボックで配列になってるからうーん、、、とか深く考えないで「save($this->data)」だけでCakePHPが勝手に処理してくれるっぽいです。

View

views > posts > add.php

	echo $form->create('Post', array('type' => 'post'));
	echo $form->input('Tag', array(
		'type'=>'select',
		'multiple' => 'checkbox',
		'options' => $tagData,
		'label' => 'タグ));
	echo $form->end('投稿');
	

フォームヘルパーにはチェックボックス用の「$form->checkbox」もありますが、配列から一気に作りたいときは「$form->input」を使います。
「options」にコントローラーから受け取った「tagData」を入れます。

データ更新(アップデータ)

次にすでに登録してあるデータの更新(アップデータ)です。

controllers > posts_controller.php

	function admin_update($id = null) {
		$this->Post->id = $id;
		if (empty($this->data)) {
			$this->data = $this->Post->read();
			$this->set('result', $this->data);
			//Tagを渡す
			$tagData = $this->Tag->find('list');
			$this->set('tagData', $tagData);
		}
		elseif (!empty($this->data)) {
			$this->Post->save($this->data);
		}
	}
	

コントローラーは特に特別なことないですね。

views > posts > update.php

	echo $form->create(null, array('type' => 'post', 'action' => './update'));
	echo $form->input('Tag', array(
		'type'=>'select',
		'multiple' => 'checkbox',
		'options' => $tagData,
		'label' => 'タグ'));
	echo $form->end('投稿');
	

ビューはPostsTagsで関連してるところはselectedでチェックして、、、とか深く考える必要ありませんでした。
新規追加(add.php)と同じで、関連付けされているタグには勝手にチェックされて表示してくれます。

チェックボックスのバリデーション

最後にチェックボックスのバリデーションを行います。

postモデルに以下を追加します。

models > post.php

	public $validate = array(
		'Tag' => array(
			'multiple' => array(
				//'rule' => array('multiple',array('min' => 1, 'max' => 2)),
				'rule' => array('multiple',array('min' => 1)),
				'message' => '1つ以上選択してください。'
			),
		)
	);
	function beforeValidate() {
		foreach($this->hasAndBelongsToMany as $k=>$v) {
			if(isset($this->data[$k][$k])) {
				$this->data[$this->alias][$k] = $this->data[$k][$k];
			}
		}
		return true;
	}
	
引用元:HABTM form validation in CakePHP

コントローラーです。
何もしなかったらチェックボックのエラーが表示できなかったので、わざわざメッセージを取得しています。

controllers > posts_controller.php

	$this->Post->save($this->data);
		if ($this->Post->validates()) {
			$this->redirect('/posts/');
		} else {
			$this->set('result', $this->data["Post"]);
			//エラーメッセージを取得
			$errors = $this->Post->invalidFields();
			if(!empty ($errors['Tag'])) {
				$this->set('tagerror', $errors['Tag']);
			}
		}
	}
	

Viewのチェックボックの取得したエラーメッセージをチェックボックスの下に表示させています。

views > posts > add.php

	echo $form->input('Tag', array('type'=>'select','multiple' => 'checkbox', 'options' => $tagData,'label' => 'カテゴリー'));
	//タグエラーがあったら表示
	if(!empty ($tagerror)) {
		echo '<div class="error-message">';
		print_r($tagerror);
		echo '</div>';
	}
	

これで一応1つも選択されてなかったときにエラー表示されます。
もっと上手いやり方があると思いますが僕のスキルではこの程度でした。

COMMENTS

LEAVE A REPLY

コードを書く場合は<pre>で囲んでください。