Archive for the ‘ CakePHP関連 ’ Category

友達の詳細画面を作ってみる(CakePHP修行 #40)


さて友達系処理の続き。寝れないからもう一個書く。これ書いたら寝る(多分)。

■ 友達の詳細画面

友達の画像をクリックすると現れる画面を作成。

akiyan_1.gif

↑ こんな感じ。

そこからその友達の日記と友達の友達を見れるようにするのですが、それぞれ以下のようなURLにします。

  • /friends/showPosts/(id)
  • /friends/showFriends/(id)

これでいいよね、きっと。

あとは友達の友達を自分の友達にしたりとか、友達の日記を表示するだとかありますが、まぁ、それは明日考えましょう。

日記へのコメント機能、お友達の紹介コメント機能も作りたいところ。結構大変ですね、SNS作るのって・・・当たり前か。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

SNSっぽい友達系の処理をつくってみる(CakePHP修行 #39)


さて友達系処理の続き。

■ アソシエーション

そうだった。前回の記事でアソシエーションについて書くのを忘れた。

UsersとFriendsのアソシエーションですが、よくよく考えたら1:nなので$hasManyで定義していきます。

まずはUser側。

var $hasMany = array ('Post',
                      'Friend'=>array('className'=>'Friend',
                                      'conditions' => 'Friend.status = 1',
                                      'foreignKey' => 'user_id'
                                      )
                      );

ここではconditionsを使ってstatus=1のものだけ取り出すようにしてみました(ただ、これがうまく利いていないよう・・・あとで後述しますが)。

で、次にFriend側。

var $belongsTo = array('User' =>
                        array('className' => 'User',
                              'foreignKey' => 'friend_user_id'
                              )
                      );

こちら側から見るとアソシエーションする外部キーはuser_idではなくて、friend_user_idになるので注意・・・でいいんだよね?

こうした下準備を経て次へ移ります。

■ 友達一覧、送った招待状一覧を出す

さて/friends/の画面では友達一覧&送信済み招待状一覧を出すのですが、ここがちょっとわからない。

friends_top.gif

↑ 画面はこれね。

現在のコードはこんな感じ。まずはコントローラー側。

function index() {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  $this->pageTitle = 'My Friends';
  // set list
  $this->set('friends', $this->Friend->findAllByUser_id($me['User']['id']));
}

で、View側。

<h3>My Friends</h3>
<ul id="friends" class="clear">
<?php 
foreach ($friends as $friend) { 
  if ($friend['Friend']['status']==1) {
?>
<li><a href="/friends/detail/<?= $friend['User']['id']; ?>"><img src="<?= $friend['User']['pic_loc']; ?>" width="<?= $friend['User']['pic_width']; ?>" height="<?= $friend['User']['pic_height']; ?>" border="0" alt="<?= $friend['User']['name']; ?>" /></a></li>
<?php
  } 
}
?>
</ul>
<h3>Invitations sent</h3>
<ul>
<?php 
foreach ($friends as $friend) { 
  if ($friend['Friend']['status']==0) {
?>
<li><?= $friend['User']['email']; ?> <a href="">[resend!]</a></li>
<?php
  } 
}
?>

これをみるとわかるとおり、とってきた$friendsの配列の中からstatusで分けて表示しています。これってなんかだめな感じだよね?本当はfindActiveFriends()とかを作ったほうがいいのだよね・・・その作り方がいまいちわからない・・・。モデルを拡張するのだと思うのだけど。

うーん、明日考えてみます。と、とりあえず寝る。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

SNSっぽい友達系の処理をつくりはじめてみる(CakePHP修行 #38)


さてCakePHP。なんだかいろいろやりました。中途半端なところではありますが、とにかく途中経過報告。

これ書いたら寝ようかな・・・。

■ DEBUGは2に設定

青い人のおすすめに従い、開発中はDEBUGレベルを2にしてみました(/config/core.php)。これをやるとどういう動作が起きているかのSQL文を画面の下に出してくれます。なんと便利!いままで何でやらなかったのだろう。

debug_2.gif

↑ こういった感じでSQLの動きがわかります。

■ 友達系の処理

さていよいよSNSっぽい処理を作っていきます。まずは自分の友達一覧画面。以下のような感じになるように作ります。

friends_top.gif

↑ イメージはこんな感じっすかね。というか、もう動いているけど。

■ 友達を招待する

友達を招待画面は以下のとおり。メールアドレスとコメントを入れれば招待メールを出してくれます。

friends_inv.gif

↑ メールアドレスのみ必須ということで。

で、コードですが、ちょっとはまりましたが、完成系はこちら。getLastInsertID()の使い方がわからなかったのですが、データの挿入は$this->User->save()を使えばよいとわかってから理解しました(最初、$this->User->execute()使っていた・・・)。

あと気をつけるべきは、データ挿入時はidをnullにしてあげる点ですかね。これをやらないとあとでいろいろ大変らしいです(青い人談)。

function invite() {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  $this->pageTitle = 'Invite a new friend';
  // from start
  $this->set('error', false);
  if (!empty($this->data)) {
    if (!empty($this->data['User']['email'])) {
      // add user and update hash
      $this->User->id = null;
      $this->User->save($this->data['User']);
      $new_id = $this->User->getLastInsertID();
      $new_user['User']['id'] = $new_id;
      $new_user['User']['id_hash'] = sha1(SHA1_KEY.$new_id);
      $this->User->save($new_user['User']);
      // add friend relationship
      $this->Friend->id = null;
      $this->data['Friend']['user_id'] = $me['User']['id'];
      $this->data['Friend']['friend_user_id'] = $new_id;
      $this->data['Friend']['status'] = 0;
      $this->Friend->save($this->data['Friend']);
      // send invites
      $inviter = $me['User']['name'];
      $join_link = WWW_URL.DS."users".DS."accept".DS.sha1(SHA1_KEY.$new_id);
      $mail_file = VIEWS . 'mail/friend_invite.php';
      $msg = implode (file($mail_file));
      eval ("\$msg = \"$msg\";");
      $toName = $this->data['User']['email'];
      $subject = "[code*code] Invitation from ... ".$me['User']['name'];
      // mb_send_mail ($toName, $subject, $msg, "From: ".ADMIN_EMAIL);
      // write msg, jump
      $this->Session->write('sys_msg', 'Invitaion mail has been sent.');
      $this->redirect('/friends/');
    } else {
      $this->set('error', true);
    }
  }
}

あとエラー処理を全然していないとかいろいろつっこみどころはありますが、それは全体が出来たところでつぶしていきます。今は勢いが重要なのですよ!

さてちょっと長くなったので次エントリーに続きます。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

画像アップロード処理のセキュリティホールをふさぐ(CakePHP修行 #37)


さてさてCakePHP修行。青い人に指摘されたセキュリティホールを防ぎます。

そのエントリーでも言及されている『T.Teradaの日記』も読みましたが、どうやら完璧な防ぎ方はないような・・・しかし青い人の提案どおり、できるところまでやってみます。

簡単にいうと「画像形式を調べて、画像変換をかましつつ、拡張子を変えてアップ」です。

画像形式を調べるのはgetimagesizeでいいのかな。JPG、GIF、PNG以外ははじくように設定し、次のようなコードにします。

// file upload
if (!empty($this->data['User']['pic']['name'])) {
  // tmp file info
  $tmp_file = $this->data['User']['pic']['tmp_name'];
  $imginfo = getimagesize($tmp_file);
  // file error handling (file size / JPG,GIF,PNG)
  clearstatcache();
  if (filesize($tmp_file)>300000 || ($imginfo[2] < 1 || $imginfo[2] > 3)) { $this->set('file_error',true); return false; }
  // set file name
  $upload_path = WWW_ROOT . "pics" . DS;
  // set new width, height
  $width_old  = $imginfo[0];
  $height_old = $imginfo[1];
  $width_new  = PIC_WIDTH;
  $height_new = $height_old * ($width_new / $width_old);
  // create new file
  switch ($imginfo[2]) {
    case 2: // jpeg
      $filename = sprintf("%05d.jpg",$me['User']['id']);
      $jpeg = imagecreatefromjpeg($tmp_file);
      $jpeg_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($jpeg_new,$jpeg,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagejpeg($jpeg_new, $upload_path . $filename, 100);
      break;
    case 1: // gif
      $filename = sprintf("%05d.gif",$me['User']['id']);
      $gif = imagecreatefromgif($tmp_file);
      $gif_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($gif_new,$gif,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagegif($gif_new, $upload_path . $filename, 100);
      break;
    case 3: // png
      $filename = sprintf("%05d.png",$me['User']['id']);
      $png = imagecreatefrompng($tmp_file);
      $png_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($png_new,$png,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagepng($png_new, $upload_path . $filename, 100);
      break;
    Default:
      break;
  }
  $this->data['User']['pic'] = $filename;
} else {
  $this->data['User']['pic'] = $me['User']['pic'];
}

これで一応大丈夫かな・・・拡張子も変えたし。さて、では次にいよいよお友達っぽいSNS処理に移ります。複雑なアソシエーションをつくらないといけないですね。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

いろいろ細かい調整やら修正やら(CakePHP修行 #36)


さてCakePHP修行。ひさびさに青い人に添削してもらいました。うれしす。ありがとう!

さて、それもこれも含め、次のステップに進む前に気になる部分が出てきたので細かいところを直しちゃいます。

■ Password生成関数について

「パスワードを忘れた方は」を実装したい(CakePHP修行 #22)』のコメント欄にて高田さんに次のようなコメントをいただきました。

初期パスワードを作るときに、「1」と「l(小文字のL)」や「o(小文字のO)」と「O(大文字のO)」と「0(数字の0)」のような似てる文字(フォントによって判別が難しい・・・)は適用しないようにしておくと少し幸せかもです。

おお・・・なるほどです。早速パスワード生成関数を修正しました。確かに重要ですね。ありがとうございました!

■ DBに依存しないフィールドはHTMLヘルパーなしで

青い人にずっと前に指摘された「DBに依存しないフィールドは手打ちで」という教え(詳細はこちら)をすっかり失念していました・・・。普通に書いて$this->params['form']['なんたら']で取得できるのでしたね。

ログイン画面での「save my info?」のチェックボックス、それからプロフィール変更画面での画像削除チェックボックスは手打ちし、該当するコントローラーを修正しました。

■ Viewのテンプレート化をもちょっとすすめる

ページのテンプレートは現在/views/layout/default.thtmlでまかなっていますが、ログイン画面にあわせたレイアウトなので、ログイン画面以外では冗長なコードになってきました(メニューを何回も書いているとか)。そこでログイン系とそれ以外のテンプレートを分けて管理することにします。

なお、管理する際には下記のjakkさんのコメントを参考にさせていただきました。

もうご存知かもしれませんが
レイアウトは
$this->layout = “default”;
でかえれます。
挫折しないことをいのります(・人・

コントローラー内のlogin()、resetPwd()については以下の一行を追加。別のテンプレートを使うように指示します。

$this->layout = 'login_default';

そしてページ数の多いほうで使うdefault.thtmlはelementなども使い下記のように修正。その際にはHTMLヘルパーが良くまとまっているこちらのページも参考にしました(docTypeとか一部動かなかったけど)。

<!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 $html->charset('utf-8'); ?>
<meta name="keywords" content="">
<meta name="description" content="">
<script type="text/javascript" src="/js/jquery.js"></script>
<?php echo $html->css('core'); ?>
<link rel="shortcut icon" href="/favicon.ico" />
<title><?php echo $title_for_layout; ?></title>
</head>
<body>
<div id="container" class="clear"> 
<?php echo $this->renderElement('header'); ?>
<div id="my_page" class="clear">
<h2><a href="/users/home/"><?= h($me['User']['name']); ?></a></h2>
<div id="contents">
<?php if (isset($sys_msg)) { ?>
<div id="sys_msg"><?= $sys_msg; ?></div>
<script>
$(document).ready(function() { $("#sys_msg").fadeOut(2000); });
</script>
<?php } ?>
<?php echo $this->renderElement('menu'); ?>
<?php echo $content_for_layout ?>
</div><!-- /contents -->
</div><!-- /my_page -->
</div><!-- end of container -->
<?php echo $this->renderElement('footer'); ?>
</body>
</html>

■ モデルのafterFind()を使う

プロフィール画像の幅とか高さとかファイル名とか表示するたびにいちいち計算するのが面倒なのでafterFind()に突っ込んでみます。

次のコードでうまくいったぽい。これでいいのかな>青い人?

まずは/models/user.php。afterFind()を追加。

function afterFind($results) {
  foreach ($results as $key => $val) {
    if (!empty($val['User']['pic'])  && file_exists(WWW_ROOT."pics".DS.$val['User']['pic'])) {
      $imginfo = getimagesize(WWW_ROOT."pics".DS.$val['User']['pic']);
      $results[$key]['User']['pic_loc'] = DS."pics".DS.$val['User']['pic'];
      $results[$key]['User']['pic_width'] = $imginfo[0];
      $results[$key]['User']['pic_height'] = $imginfo[1];
    } else {
      $results[$key]['User']['pic_loc'] = DS."pics".DS."uploadyourphoto.gif";
      $results[$key]['User']['pic_width'] = 50;
      $results[$key]['User']['pic_height'] = 50;
    }
  }
  return $results;
}

次にControllers&Views。コントローラーから画像に関するコードを削除、Viewsの方は次のように変更。これですっきり。

<img src="<?= $me['User']['pic_loc']; ?>" width="<?= $me['User']['pic_width']; ?>" height="<?= $me['User']['pic_height']; ?>" border="0" alt="" />

モデルの中でこうやるの便利だな・・・コードがもうちょっと複雑になってきたら積極的に使ってみよう。

■ 重大なセキュリティホールをふさぐ

青い人に指摘されたセキュリティホール、ふさがなくちゃですね。ちょっと長くなったのであとでやる。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

日記投稿系の処理をガツンとつくる(CakePHP修行 #35)


さて続けてCakePHP修行。今度は日記投稿系の処理をつくっちゃいます。とりあえずSNSっぽい友達の日記はおいておいて、自分で投稿したり削除したりできるようにします。

pic_top.gif

↑ 今回はここのメニューをガツンと。

■ モデルの設定

まずはモデルから。今回設定しなくてはいけないのはUserとPostの紐付け。UserとPostは1:Nの関係なので$hasManyなどでアソシエーションを作ります。

まずは/models/user.phpの方。

class User extends AppModel {
  var $name = 'User';
  var $validate = array (
    'email' => VALID_EMAIL,
  );
  var $hasMany = array ('Post');
}

次にPostの方。

class Post extends AppModel {
  var $name = 'Post';
  var $belongsTo = array('User');
}

アソシエーションはもっと細かく設定できるはずですが、今回はざくっとこんな感じで。

■ コントローラー&Viewを作りこんでいく

今回は日記一覧を/posts/(index)、追加・更新系を/posts/add、/posts/edit、/posts/delで作っていきます。簡単、簡単!と思ったのですが最初にはまったのはposts_controller.phpからUserのデータが見えない問題。あれれ、と思いちょっと調べてギブアップ(=青い人と世間話するふりしてちょっとヒントもらう)、結局これが足りませんでした。

  var $uses = array('User','Post');

$usesで設定しておけば、そのコントローラー内から紐づいた形でモデルが参照できるようです。そのあとは普通にロジックを書いていきます。コードは以下。

function index() {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  $this->pageTitle = 'My Diaries';
  // set list
  $this->set('posts', $this->Post->findAllByUser_id($me['User']['id']));
}
function view($id) {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // set list
  $post = $this->Post->findById($id);
  $this->set('post', $post);
  $this->pageTitle = $post['Post']['title'];
  $this->set('owner',$this->User->findById($post['Post']['user_id']));
}
function add() {
  // check login
  $this->_checkLogin();
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // main
  $this->set('error', false);
  if (!empty($this->data)) {
    $this->data['Post']['user_id'] = $me['User']['id'];
    if ($this->Post->save($this->data['Post'])) {
      $this->Session->write('sys_msg', 'Your post has been added.');
      $this->redirect('/posts/');
    }
  }
}
function edit($id = null) {
  // check login
  $this->_checkLogin();
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // main
  $this->set('error', false);
  if (empty($this->data)) {
    $this->Post->id = $id;
    $this->data = $this->Post->read();
  } else {
    $this->data['Post']['user_id'] = $me['User']['id'];
    if (empty($this->data['Post']['title']) || empty($this->data['Post']['body'])) { $this->set('error', true); return false; }
    if ($this->Post->save($this->data['Post'])) {
      $this->Session->write('sys_msg', 'Your post has been updated.');
      $this->redirect('/posts/view/'.$this->data['Post']['id']);
    }
  }
}
function del($id) {
  // check login
  $this->_checkLogin();
  $this->Post->del($id);
  $this->Session->write('sys_msg', 'Your post has been deleted.');
  $this->redirect('/posts/');
}

なんかちょっとリファクタリングしたくなってきましたが、まぁ、こんな感じです。ちなみに自分の日記じゃないと削除や変更できないように権限チェックなどもするべきだと思いますが、まぁ、今はこんな感じです。

■ 動作テスト

テストも完了、ちゃんと動きます。

post_1.gif

↑ リスト画面。一覧が出てきます。

post_2.gif

↑ 変更もできます。

post_3.gif

↑ 削除もできます。

削除の際はhtmlヘルパーを使えば簡単に「Are you sure?」みたいな確認ダイアログを出せて便利。やるなぁ、CakePHP・・・。

さて次は何をするべきか・・・青い人が突っ込みを入れてくれたのでちょっとバグFixというか、細かいところを直したいと思います。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

「情報を保存する」オプションを有効にする(CakePHP修行 #34)


さて続けてCakePHP修行。今度はログイン画面での「情報を保存する」オプションを有効にしてみます。

save_info.gif

↑ 今回はこれがターゲット!

淡々と作業を続けます。

■ クッキーを使う

今までログイン情報の保持にはセッションを使っていましたが、それだとブラウザを閉じた後にも情報を保持してくれません。というわけでクッキーを導入。で、調べてみるとどうやらセッションのようにComponentはない模様(1.1系には)。そこでそのままsetcookieやら$_COOKIEやらを使っていきます。

なお、セッションから完全移行というわけではなく、簡単なメッセージは相変わらずセッションを使って表示することにします。

■ データベーススキームの変更

次にユーザー個別のCookieを保持するためのUsersテーブルを変更します。id_hashというカラムを追加します。なお、この変数には「適当な文字列」「ID」「パスワード」を連結してハッシュ化したものを使います。そしてこのid_hashをCookieとしてブラウザに保持、ログインしているかどうかのチェックに使います。

結局、Usersテーブルはこんな感じ。

CREATE TABLE users (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  id_hash VARCHAR(50),
  name VARCHAR(50),
  email VARCHAR(255) BINARY NOT NULL UNIQUE,
  pwd VARCHAR(255),
  pic VARCHAR(255),
  profile TEXT,
  created DATETIME DEFAULT NULL,
  modified DATETIME DEFAULT NULL
);

なお、適当な文字列にはパスワードを暗号化するために使ったPWD_KEYを使いまわします。よく考えたら使いまわすのよくないか、まぁ、いいや・・。

■ ログインチェックのロジックを変更

いままでログインチェックにはapp_controller.phpのcheckSession()を使っていましたが、セッションではなくなるので_checkLogin()を新しく作ることにします。変数の前には_をつけましたが、これでいいんすよね?

実際のコードはこんな感じ。

function _checkLogin() {
  // if cookie exists ...
  if (!isset($_COOKIE['my_id']) || !$this->User->FindById_hash($_COOKIE['my_id'])) {
    $this->Session->write('login_back_url',((getenv('SERVER_PORT')==443)?'https://':'http://').getenv('HTTP_HOST').getenv('REQUEST_URI'));
    $this->redirect('/users/login');
    exit();
  }
}

そしてusers_controller.php中のcheckSession()を_checkLogin()に変更します。

■ ログイン画面&ロジックの変更

まずはView(login.thtml)の変更。「情報を保存する」チェックボックスを有効にします。HTMLヘルパーを使って以下のように記述。

<tr><td width="40%">&nbsp;</td><td><?php echo $html->checkbox('User/save'); ?> save my info?</td></tr>

Usersのスキーマにないsaveってのを勝手に作ってしまったがいいのだろうか。ま、どちらにしろこれでコントローラー側で$this->data['save']から受け取れるようになりました。

それでもってlogin()も次のように変更。データが入ってきたときに以下のように処理します。

$someone = $this->User->findByEmail($this->data['User']['email']);
if(!empty($someone['User']['pwd']) && $someone['User']['pwd'] == sha1(PWD_KEY.$this->data['User']['pwd']))
{
  if ($this->data['User']['save']) {
    setcookie('my_id', $someone['User']['id_hash'], time()+60*60*24*365*5, '/'); // save it for 5 years
  } else {
    setcookie('my_id', $someone['User']['id_hash'], NULL, '/');
  }
  if (isset($login_back_url))
  {
    $this->redirect($login_back_url);
    $this->Session->delete('login_back_url');
  } else {
    $this->redirect('/users/home/');
  }
}

これによってブラウザ側にCookieが保存されますが、チェックボックスを入れていると5年間、問答無用で保存します。まぁ、これぐらいあればいいですかね。チェックを入れていない場合はブラウザが終了するまで保持することとします。

それからsetcookie中でパス指定(今回は”/”)をしたのですが、これってなんかセキュリティ上、やばいんじゃなかったかな?でもこれを指定しないと”/users”のみで有効になってしまうという結果に・・。これでよかったのかな?

■ コントローラー中の処理

今回の処理で「セッション中にUser IDを保持」から「Cookie中にid_hashを保持」に変わったのでコントローラーの各処理を次のような感じにしておきます。

$me = $this->User->findById_hash($_COOKIE['my_id']);

さて、ここまでで「情報を保存する」オプションがきちんと有効になるようになりました・・・。

cookie_view.gif

↑ クッキーを確認。ちゃんと有効期限が5年後になっています。

■ ログアウト処理

おっと、忘れるところだった。ログイン処理を変えたのでログアウト処理も変更します。コードは以下のとおり。

function logout()
{
  setcookie('my_id','',time()-60*60,'/');
  $this->redirect('/users/home/');
}

やれやれ(JOJO)、ログイン画面だけでだいぶ時間を使ってしまった。次はいよいよ日記の投稿へ行きましょう。アソシエーションとか早くやってみたい。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

デフォルト画像を用意する(CakePHP修行 #33)


さてCakePHP修行。いろいろやってますよ。

そういえば最近青い人がからんでくれない。どうしたのだろうか。一人異国の地でがんばっているのになにか寂しいっす・・・。

さて、気を取り直して。

■ デフォルトの写真を設定

細かいですが、写真がアップされていないときにデフォルトの画像を表示するようにしました。

uploadyourphoto.gif

↑ まずはこんな画像を用意。

pic_2.gif

↑ 画像が登録されていないときにはこんな感じに表示されるように。

コードは簡単。$me['User']['pic']がemptyだったら、デフォルト画像のURLを渡すようにしているだけです。

users_controller.phpのコードは以下のとおり。

if (empty($me['User']['pic'])) { $me['User']['pic'] = 'uploadyourphoto.gif'; $this->set('me', $me); }

なんか$meをまるごと再セットしているのが富豪的だな・・・$me['User']['pic']だけに新しい値をセットするのってどうすればいいのだろう。まぁ、細かいところはあとで悩むとしてとりあえず動くようにしちゃいます。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

Subversion+Tracデビュー!(CakePHP修行 #32)


さてちょっと間があきました、CakePHP修行。さぼっていたわけではなくて苦戦していましたよ・・・。

青い人から「Subversion+Tracがないとよくわからない」と言われたのでなんとかセットアップしました。あいかわらず青い人に教わりながらでしたが・・・。

なお、subversionは昔から使っていますが、Tracは初めて。どきどきです。

それから青い人からは「うちのサーバーで動いているTracで管理しましょーかー?」といわれましたが、これも修行のうちなので丁重にお断りしました。すっかりCakePHPじゃない連載になっています。

ちなみに使っているOSはCentOS4.4で、僕がもっている自宅サーバーにインストールしました。なお、subversionに関してはapache経由は面倒なので、svnserve経由です。クライアントからはTortoiseSVNでアクセスします。

さて、以下に簡単に作業手順をご紹介。全部書くと大変なので簡単に。需要あったらそのうち書きます。

  • subversionのインストール。yum installを使っても良いのですが、最新版じゃないのでソースから。apr、apr-utilも一緒にいれちゃいます。BerkeleyDBではなくてfsfsを使います。
  • リポジトリの作成、データのImportをします。
  • svnserveを起動。クライアントからアクセスをテストします。OKでした。ちょっと怖いので認証もかけます。
  • 次にTrac。ここで青い人に「yumからtracをインストールする方法」を教わります(ここらへん)。ひょっとして、これでsubversionもインストールできた(ry
  • 最初にtracdを試しましたが、認証かからないので(という理由だっけ?)、cgi-binでやります。httpd.confなんかをごにょごにょして基本認証をかまして起動。
  • とりあえず最初のチェックアウト。一時ファイル系にはigonore属性を付与。その後、ちょっと修正してコミット。無事に反映されました。ちなみにローカルではVMWare+CentOS4.4で動いています。ふー・・・
  • trac_top.gif

    ↑ 初Trac!

さて、簡単に書きましたが、かなり疲れました・・・すごい作業だったなぁ・・・。一人だと絶対!無理でした。

それにしてもソースからインストールとか昔こわくてできなかったけどもう慣れっこになりましたね。ちょっとうれしい。

つうわけで、あとはもう突っ走るだけですね。青い人も隅々までソースを見れるようになったので突っ込みいれてくれるはず!

さー・・・やるぞー・・・(眠い・・・)。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

画像のアップロード処理をつくる【III】(CakePHP修行 #31)


まいったね、ちょっと眠い。午前3時・・・今、目黒のネカフェ・・・。

さて、画像アップ処理の続きです。なんかgdgdなコードだけど晒します。明日直したい。

■ 画像アップ処理(users_controller.php)

edit()部分で画像処理部分を以下のように変更。簡単にいうと、エラーチェックして、だめならだめで、OKならファイル形式を見ながら横幅が50px(=PIC_WIDTH定数を/config/app.phpに定義)になるように調整して出力、というもの。

// file upload
if (!empty($this->data['User']['pic']['name']))
{
  // tmp file info
  $tmp_file = $this->data['User']['pic']['tmp_name'];
  $imginfo = getimagesize($tmp_file);
  // file error handling (file size / JPG,GIF,PNG)
  clearstatcache();
  if (filesize($tmp_file)>300000 || ($imginfo[2] < 1 || $imginfo[2] > 3)) { $this->set('file_error',true); return false; }
  // set file name
  $ext = low(preg_replace("!.*\.!", null, $this->data['User']['pic']['name']));
  $filename = sprintf("%05d.%s",$me['User']['id'], $ext);
  $upload_path = WWW_ROOT . "pics" . DS;
  // set new width, height
  $width_old  = $imginfo[0];
  $height_old = $imginfo[1];
  $width_new  = PIC_WIDTH;
  $height_new = $height_old * ($width_new / $width_old);
  // create new file
  switch ($imginfo[2]) {
    case 2: // jpeg
      $jpeg = imagecreatefromjpeg($tmp_file);
      $jpeg_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($jpeg_new,$jpeg,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagejpeg($jpeg_new, $upload_path . $filename, 100);
      break;
    case 1: // gif
      $gif = imagecreatefromgif($tmp_file);
      $gif_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($gif_new,$gif,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagegif($gif_new, $upload_path . $filename, 100);
      break;
    case 3: // png
      $png = imagecreatefrompng($tmp_file);
      $png_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($png_new,$png,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagepng($png_new, $upload_path . $filename, 100);
      break;
    Default:
      break;
  }
  $this->data['User']['pic'] = $filename;
} else {
  $this->data['User']['pic'] = $me['User']['pic'];
}
// file delete
if (empty($this->data['User']['pic']['name']) && file_exists(WWW_ROOT."pics".DS.$me['User']['pic']) && $this->data['User']['pic_del'])
{
  unlink(WWW_ROOT."pics".DS.$me['User']['pic']);
  $this->data['User']['pic'] = null;
}

ま、テストもしてみてうまく動いているのだけど、いくつかどうにかしたい点が。

  • この処理ってモデルの方で吸収したほうがいいの?beforeSave()とかで?
  • エラー処理ってこれでOK?なんかだめだったらreturn false;しちゃえばいいの?
  • エラー処理ってHTMLヘルパー使うべき?$validateとかモデルに書いたから使えばいいのかな?
  • WWW_ROOT.”pics”.DSって書くのが面倒。これってどっかで定数定義しちゃったほうがいいの?

■ 画像表示編(users_controller.php、home.thtml)

で、このようにアップした画像を/users/home/で表示させたいのだが、次のようにやっている。これがとてつもなく頭悪い感じ。なんとかしたい。横幅は50pxで固定なのでいいのだけど、縦幅をこのようにいちいち取得している。

function home ()
{
  $this->checkSession();
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  $me = $this->User->findById($this->Session->read('my_id'));
  $this->set('me', $me);
  $imginfo = WWW_ROOT . "pics" . DS . $me['User']['pic'];
  $this->set('pic_height', $imginfo[1]);
}

それからhome.thtmlの画像表示部分はこんな感じ。

<img src="/pics/<?= $me['User']['pic']; ?>" width="<?= PIC_WIDTH; ?>" height="<?= $pic_height; ?>" border="0" alt="" />

こ、これはモデルでなんとかできるんだよね、きっと・・・。$me['User']['pic_height']とかでアクセスしたいのだが・・。

ま、でも動作的にはこんなものですかね。思ったより時間かかったな・・・。ここでいったんすべてのコードを見直してみますかね。

pic_up.gif

↑ テスト完了。画像の縮小、JPEG/GIFでのアップも問題なし。削除もできました。

それができたら次はログイン時の「情報を保存する」をやってみたい。これはCookieを使わないとですね。Cookieへのアクセスの仕方勉強しなくちゃ。

それが終わったら日記の処理・・・次に友達の管理か・・・まだまだ先は長いな・・・。

・・・とりあえず寝るか・・・。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。