CakePHPでの開発備忘録

20170210_01

Facebookのタイムラインのような仕組みを開発
自分のバイオリズムを日々登録できる仕組みも合わせて

ログイン後の画面構成は以下の通り

/layout/cocosup.ctp
2カラムレイアウト
 ヘッダー ->/element/header.ctp
 左カラム:メニュー、バイオリズム入力 ->/element/side.ctp
 右カラム:メインコンテンツ表示
 フッター ->/element/footer.ctp

ログイン後は
/chats/index
にリダイレクトするように設定

AppController.php
$this->Auth->loginRedirect = array(
‘controller’ => ‘chats’,
‘action’ => ‘index’
);
※前回投稿したACL制御を実装することでAppController.phpに上記記述が追加されるので、リダイレクト先を編集するのみ

■全ページ利用プログラムの設定
AppController.phpのpublic function beforeFilterに記述

ログインしたユーザーのセッション情報を全ページで利用できるようにセット
AppController.php

$this->set('userinfo', $this->Auth->user());

変数userinfoに$this->Auth->user()をセットしViewで利用できるようにする

バイオリズムは全ページで利用したいので、AppController.phpに全ページ利用できるようにセット
AppController.php

$this->set('Biorthythm', 'add');

各コントローラでpublic function beforeFilterが設定されている場合、各コントローラのbeforeFilterを優先する
このときAppControllerのbeforeFilterも使用したい場合は
parent::beforeFilter();
を記述する

■メニューの設定

20170210_menu

通常のメニュー設定はHTMLヘルパーを利用して以下のように設定
/element/side.ctp

<?php echo 
      $this->Html->link(
      $this->Html->image('ファイル名'),
      array(
        'controller' => 'リンク先コントローラ名',
        'action' => 'リンク先アクション名'
        ),
      array('escape' => false)
      );
 ?>

今回はマイページ(ユーザー情報の変更)ボタンがあり編集ページへの遷移が必要なため、IDを渡す必要があったので、以下のように設定
/element/side.ctp

<?php echo 
      $this->Html->link(
      $this->Html->image('ファイル名'),
      array(
        'controller' => 'リンク先コントローラ名',
        'action' => 'リンク先アクション名',
        $userinfo['id'] //ログインしているユーザー情報からidを取得
        ),
      array('escape' => false)
      );
 ?>

■バイオリズムの登録

20170210_bio

基本的にはFromヘルパーを使い通常通り設定していく。
気を付ける部分としてはcreate部分での記述。

通常は以下のような記述を行う

<?php echo $this->Form->create('Biorthythm'); ?>

Viewの部品(/element/side.ctp)に記述しているので、このままだと現在のアクションである/chats/index/にPOSTしてしまうのでactionの指定が必要
CakePHP2.8以降はactionが利用できないのでurlでプログラム先を記述
/element/side.ctp

<?php echo $this->Form->create('Biorthythm' , array(
  'url' => array('controller' => 'biorthythms', 'action' =>'add')
)); ?> 

その他フォームの部品はいつも通り記述
サンプルとして
/element/side.ctp

<?php echo 
	$this->Form->input('フィールド名', 
	array(
	'label' => false,
	'div' => false ,
	'type' => 'range' ,
	'min' => 1 ,
	'max' => 5
	));
 ?>

余計なタグになるlabelとdivは削除
今回はスライダーを利用したので、タイプとオプションを設定

入力はさせないが、裏側で値を取得しセットしたい場合はhiddenで処理を行う
/element/side.ctp

<?php echo $this->Form->hidden('user_id' ,array('value' => $userinfo['id'])); ?>

user_idフィールドにログインしているユーザーのIDをセット

■バイオリズム登録済み確認処理
バイオリズムの登録は1ユーザー1日1回としたいので、登録済の場合は登録した内容を表示する画面にリダイレクト設定する。
現在のアクションが/chats/indexのためChatController.phpのindexアクション部分に記述
他のページでも判別が必要な場合は同じ記述を入れるが、ページ数が多い場合はAppControllerで共通化したほうが効率がいいのか?

ChatController.php

$today = date("Y/m/d"); //今日の日付を取得
$this->set('today',$today); //$today変数をViewで利用できるようにセット
$users2 = $this->Auth->user('id'); //ログインユーザーIDを取得
$cond = array(
	'user_id' => $users2 , //user_idにログインユーザーIDを
	'today' => $today //todayに今日の日付をセット
);
$this->set('cond',$cond); //$cond変数をViewで利用できるようにセット

if ($this->Chat->Biorthythm->hasAny($cond)) { //Biorthythmテーブルのuser_id、todayフィールとcondが一致すれば
	$this->redirect(array('controller' => 'chats', 'action' => 'end')); //リダイレクト
}

よく考えたら以下のような記述でもいいのか?変数定義がかなり減らせてシンプル
ChatController.php

$cond = array(
	'user_id' => $this->Auth->user('id'),
	'today' => date("Y/m/d")
);

if ($this->Chat->Biorthythm->hasAny($cond)) {
	$this->redirect(array('controller' => 'chats', 'action' => 'end'));
}

/chats/end
では以下のレイアウトを使用するように指定

/layout/cocosup_end.ctp
2カラムレイアウト
 ヘッダー ->/element/header.ctp
 左カラム:メニュー、登録バイオリズム表示 ->/element/side_end.ctp
 右カラム:メインコンテンツ表示
 フッター ->/element/footer.ctp

■登録済みバイオリズムの表示
まずは登録したバイオリズムの情報をデータベースから取得する
BiorthythmController.phpのaddに以下を追記

BiorthythmController.php

$bio = $this->Biorthythm->find('first', array('conditions' => array(
		'user_id' => $users2,
		'today' => $today )));
$this->set('bio',$bio);

find(first)関数でデータベースから1行取得できる
conditionsはSQL文のWhereと同じで条件を設定できる

あとはViewでbio変数を指定しつつ値を表示
/element/side_end.ctp

<?php echo h($bio['テーブル名']['フィールド名']); ?>

バイオリズムの登録表示は以上

■タイムラインの表示

20170210_timeline

タイムラインではchatsテーブルの情報を一覧で表示します。
また、chatsテーブルのそれぞれの情報に対してコメント(レスポンス)があるものも表示します。
このためViewでの記述方法としては
foreach
 chatsテーブルの情報を1行取得
  foreach
   responsesテーブルのchat_idとchatsテーブルのidが一緒だったら1行取得
   これを繰り返す
  endforeach
 chatsテーブルの情報取得を繰り返す
endforeach
という記述になる

foreachで繰り返し処理が行えるようにそれぞれのデータベースよりすべてのデータを取得します。
記述はChatController.phpのindexとendアクションに

chatsテーブル
ChatController.php

$userchats = $this->Chat->find('all', array(
			'order' => array('Chat.created' => 'desc')
));

responsesテーブル
ChatController.php

$userresponses = $this->Chat->Response->find('all', array(
				'order' => array('Response.created' => 'desc')
));

データベースから情報を取得するfind()関数を使用
find(all)にすることで全データが取得できる
今回は並び順を指定したいためorderでcreatedフィールドをdesc(降順)に取得している
orderはSQL文ではorder byになる
また昇順に並ばせたい場合はdescの指定をascにすればよい

単純に表示させるだけはviewで以下のような記述になる

/chats/index.ctp
/chats/end.ctp

<?php foreach ($userchats as $chat): ?>
	<div class="chat-header">
	<div class="userinfoarea">
		<p class="user-img">
        	<?php echo 
        	$this -> Html->image('/files/'."{$chat['User']['img_file']}", 
        	array('width' => 50)); 
        	?>
      		</p>
			<p class="user-name">
			<?php echo h($chat['User']['name']); ?>さん<br>
			<?php echo h($chat['Chat']['created']); ?>
		</p>
	</div>
	</div>
	<p class="body"><?php echo h($chat['Chat']['body']); ?></p>

<!-- 投稿に対するレスポンスを表示 -->
<!-- もしレスが投稿に紐づいているなら -->
<?php foreach ($userresponses as $response):
if ($response['Response']['chat_id'] == $chat['Chat']['id']){ ?>
	<div class="myresponse">
		<div class="userinfoarea">
			<p class="user-img">
        	<?php echo 
        	$this -> Html->image('/files/'."{$response['User']['img_file']}", 
        	array('width' => 50)); 
        	?>
      		</p>
			<p class="user-name"><?php echo h($response['User']['name']); ?>さん<br>
			<?php echo h($chat['Chat']['created']); ?></p>
		</div>
		<p class="body"><?php echo h($response['Response']['body']); ?></p>
	</div>
<?php } ?>
	<?php endforeach; ?>
</div>
<?php endforeach; ?>

■タイムラインの追加
タイムラインの追加は純粋にフォームで情報を登録する流れになる

/chats/index.ctp
/chats/end.ctp

<?php echo $this->Form->create('Chat'); ?>
<?php echo 
	$this->Form->input('body',array(
		'label' => false,
		'div' => false,
		'placeholder' => "今の心境を入力してください"
	));
 ?>
<?php echo $this->Form->hidden('user_id' ,array('value' => $userinfo['id'])); ?>
<?php echo $this->Form->hidden('goodpoint' ,array('value' => 0)); ?>
<?php echo $this->Form->end(__('投稿する')); ?>

bodyがタイムライに表示される本文となる
hiddenでユーザーIDを取得する
goodpointは後述する「いいね」のクリック数になる。初期は0なのでvalueに0をセット

通常の登録作業では
入力フォーム→別のページへ遷移
という動きになるが、今回はタイムラインで完結するため各アクションのsave時のリダイレクト先を今のアクションを指定する

アクションindexの場合
ChatController.php

return $this->redirect(array('action' => 'index'));

アクションendの場合
ChatController.php

return $this->redirect(array('action' => 'end'));

■タイムラインの編集・削除・コメント登録
仕様としては、タイムライの編集・削除は自分で登録したもののみ可能とし、コメントの登録は自分以外が登録したもののみを可能とする
記述方法としては
もしこの投稿をしたのがあなたなら
 編集・削除ボタンを表示
そうじゃないなら
 コメント入力欄を表示
という感じになる

実際のソースは以下のようになる

/chats/index.ctp
/chats/end.ctp

<?php if ($chat['Chat']['user_id'] == $userinfo['id']) { ?>
	<div class="action">
		<p>
		<?php echo 
      	$this->Html->link(
      	$this->Html->image('ic_edit.png'),
      	array(
        'controller' => 'chats',
        'action' => 'edit'
        ),
      	array('escape' => false)
      	);
      	?>
    	</p>
    	<p>
		<?php echo 
		$this->Form->postLink(
		$this->Html->image('ic_del.png'),
		array(
        'action' => 'delete', $chat['Chat']['id']
        ),
		array('escape' => false,'class'=>'item_x'),
		__('本当に削除してもいいですか?')
		);
		 ?>
		</p>
	</div>

<?php }else{ ?>

	<?php echo 
		$this->Form->create('Response' , array(
			'url' => array(
				'controller' => 'responses',
				'action' =>'end')
			)
		);
	 ?> 
		<div class="response-add">
		<?php echo 
			$this->Form->input('body',array(
				'type' => 'textarea',
				'label' => false,
				'div' => false,
				'placeholder' => "コメントを入力してください")
			);
		 ?>
		<?php echo $this->Form->hidden('user_id' ,array('value' => $userinfo['id'])); ?>
		<?php echo $this->Form->hidden('goodpoint' ,array('value' => 0)); ?>
		<?php echo $this->Form->hidden('chat_id' ,array('value' => $chat['Chat']['id'])); ?>
		<?php echo $this->Form->end(__('投稿する')); ?>
		</div>
<?php } ?>

このソースで削除ボタン部分の処理が少し特殊になる
今回はフラグ処理ではなく直接データベースの中身を削除してしまう(物理削除)
FormヘルパーのpostLinkを使用しているが、画像を利用する場合は
array(‘escape’ => false,’class’=>’item_x’)
の記述が必須になる(よう)

コントローラでのPOSTに対する処理はResponseController.phpのendアクションを指定しているのでResponseController.phpにendアクションを追加

ResponseController.php

public function end() {
	if ($this->request->is('post')) {
		$this->Response->create();
		if ($this->Response->save($this->request->data)) {
			return $this->redirect(array('controller' => 'chats', 'action' => 'end'));
		} else {
			$this->Flash->error(__('The response could not be saved. Please, try again.'));
		}
	}
}

保存後、/chats/endにリダイレクトするように設定
indexアクションの場合も同じような処理

■レスポンスの編集・削除・コメント登録
タイムラインの編集・削除・コメント登録とまったく同じ
コメント登録時のPOST先もタイムラインのコメント登録時と同じで問題なし
responsesテーブルにどっちも登録されるので当たり前といえば当たり前

■いいねボタンの設置
仕様としては「いいね」ボタンクリック時にデータに+1加算し、現在のいいね数を横に表示する
POST先はindexやendはcreateで使用しているため、別のアクションに渡す

/chats/index.ctp
/chats/end.ctp

<?php echo 
	$this->Form->create('Chat' , array(
		'url' => array(
			'controller' => 'chats',
			'action' =>'goodpoint')
		)
	);
 ?>

次に現在forearchで回しているChatIDを渡してあげるので以下をhiddenで渡す

/chats/index.ctp
/chats/end.ctp

<?php echo $this->Form->hidden('chat_id' ,array('value' => $chat['Chat']['id'])); ?>

後はボタンの設置と件数表示
/chats/index.ctp
/chats/end.ctp

<?php echo $this->Form->input('goodpoint', array('type' => 'submit','label' => false,'div' => false)); ?>
<?php echo h($chat['Chat']['goodpoint']); ?>POST
<?php echo $this->Form->end(); ?>

以上で完了

コメントを書く