Archive for ‘ August, 2007

日記のコメントを残せるようにする(CakePHP修行 #44)


さてCakePHP修行。だいぶ終盤に近づいてきました。今回は日記にコメントを残す機能を作りこんでいきます。

■ モデルやらをいろいろ作る

日記のコメントは当初予定していなかったのでSQLから作り始めます。

まずはSQL。

CREATE TABLE comments (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    post_id INT UNSIGNED,
    user_id INT UNSIGNED,
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL,
    KEY post_id_created (post_id, created)
);

これにあわせてAssociationを設定します。UserとPostに対してhasManyの関係を記述します。またCommentモデル&コントローラーも作成します。

■ コメントの一覧

コメントはそれぞれの日記に対してつくので、posts_controller.phpのdetail()に記述します。えーと、コードは次の一行足しただけですね・・・フレームワークの威力発揮です。簡単すぎる・・・。

$this->set('comments', $this->Comment->findAll(array('Comment.post_id'=>$post['Post']['id']),null,'Comment.created DESC'));

ちなみに画面はこんな感じ。

comment.gif

↑ 日記の内容に対してコメントの一覧。自分のものに関しては削除できます。また新規追加もここから可能。

■ コメントの追加、削除

コメントの追加、削除に関しても基本的にはこの画面でできるので、CommentのViewは作っていません。作ったのはコントローラーの次の記述だけ。

function add($id) {
  $this->_checkLogin();
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  if (!empty($this->data)) {
    $this->data['Comment']['id'] = null;
    $this->data['Comment']['post_id'] = $id;
    $this->data['Comment']['user_id'] = $me['User']['id'];
    $this->Comment->save($this->data);
    $this->Session->write('sys_msg', 'Your comment has been added.');
  }
  $this->redirect('/posts/detail/'.$id);
}
function del($id) {
  $this->_checkLogin();
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $comment = $this->Comment->findById($id);
  $post = $this->Post->findById($comment['Comment']['post_id']);
  $this->set('me', $me);
  $this->Comment->del($id);
  $this->Session->write('sys_msg', 'Your comment has been deleted.');
  $this->redirect('/posts/detail/'.$post['Post']['id']);
}

超簡単・・・20分ぐらいだったのでは。

これでコメントの追加、削除ができるようになりました。なお、編集は面倒なので省きました。削除して追加する方向でw。こういう機能の追加がやたら簡単ですね。フレームワーク。なめてました。

さてこんなところですかね。アプリとしてはまだまだ甘いところがありますが(エラー処理とか、権限処理とか)、修行としてはいったんここで終了ということにしましょう。あとは最後に今回学んだことをまとめて終わりにします。

挫折する気まんまんでしたが、無事に終わってしまいそうですな・・・。

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

友達の招待・承認系の続きをつくる(CakePHP修行 #43)


さてさてCakePHP修行。ちょっと間が開きましたがさぼっていたわけではありません。細かいところで苦戦していました。

では作業報告。淡々といきます。

■ 友達の招待(メール編)

友達を自分の友達にするには二通りあります。メールで直接招待する方法と、友達の友達をみていて「この人とつながりたいな」と思ったときです。

まずはメールでの招待からいってみましょう。画面の流れは前回の「SNSっぽい友達系の処理をつくりはじめてみる(CakePHP修行 #38)」で紹介しました。

ここからメールを受け取った人がとる処理を考えます。

えーと、いきなりコード。簡単そうに見えていろいろ面倒でした。

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('email_error', false);
  if (!empty($this->data)) {
    // error handling
    if (empty($this->data['User']['email'])) { 
      $this->set('email_error', true); 
      return false; 
    }
    // user exists?
    if ($this->User->findCount(aa('User.email',$this->data['User']['email']))) {
      // user exists!
      $pal = $this->User->findByEmail($this->data['User']['email']);
      if ($this->Friend->findCount(array('Friend.user_id'=>$me['User']['id'],'Friend.friend_user_id'=>$pal['User']['id']))) { 
        $this->set('email_error', true); 
        return false;
      }
      $this->data['Friend']['id'] = null;
      $this->data['Friend']['user_id'] = $me['User']['id'];
      $this->data['Friend']['friend_user_id'] = $pal['User']['id'];
      $this->data['Friend']['status'] = 0;
      $this->Friend->save($this->data);
      $inviteLink = WWW_URL.'friends'.DS.'accept'.DS.$this->Friend->getLastInsertID();
      $inviteMail = VIEWS.'mail/friends_accept.php';
    } else {
      // new user, 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']);
      $inviteLink = WWW_URL.'users'.DS.'join'.DS.$this->Friend->getLastInsertID();
      $inviteMail = VIEWS.'mail/users_join.php';
    }
    // send invites
    $inviter = $me['User']['name'];
    $msg = implode (file($inviteMail));
    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/');
  }
}

面倒だったのは、今から招待しようとしている人のメールアドレスがすでに登録されているかどうか、また、すでに招待メールを送ったかどうかなどの判断をして場合分けしていくところ。

最初ぐちゃぐちゃとコードを書いていたのですが、わけがわからなくなったので、コーヒー飲んで気分をすっきりさせたあとにゼロから勢いで書きましたw。

なお、これで次のようなメールが送られます。まずは未登録の場合の招待メール。

Hello,
taguchi wants you!
click a link below to join in!
---

http://www.codexcode.com/users/join/6

---

次にすでに登録されている場合の招待メール。

Hello,
akiyan wants to join your network.
click a link below to accept him!
---

http://www.codexcode.com/friends/accept/8

IDもろだしでよくないのでは、という意見もありますが、それはそれ。面倒なのでこのままにしちゃいました。まぁ、良しとしましょう。

で、これを受け取った人は次のような画面に飛びます(未登録の場合。登録済みの場合は後述)。

join.gif

↑ ここで友達からのメッセージを見て登録します。

join_2.gif

↑ 無事登録できました!

■ 友達の友達を友達として登録する

さて次は友達の友達を見ていて「お、この人と友達になりたい!」と思った場合です。画面で処理の流れを見ていきましょう。

accept_1.gif

↑ まだ自分の友達でないユーザーをみるとこんな感じ。「Addしますか?」ときかれます。

accept_2.gif

↑ Addしたい場合はメッセージを入れられます。

accept_3.gif

↑ それを受け取ったほうはトップページでこのように「承認依頼が来ています!」と出ます。

accept_4.gif

↑ 承認するかどうか選べます。

さらりと書きましたが、なかなか大変でした。友達にAddする部分のコードはこちら。

function add($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);
  $pal = $this->User->findById($id);
  $this->set('pal', $pal);
  $this->pageTitle = 'Add a new friend';
  // process form
  if (!empty($this->data)) {
    $f = $this->Friend->findCount(array('Friend.user_id'=>$me['User']['id'],'Friend.friend_user_id'=>$id)) ?  $this->Friend->find(array('Friend.user_id'=>$me['User']['id'],'Friend.friend_user_id'=>$id),'id') : false;
    $this->data['Friend']['id'] =  $f ?  $f['Friend']['id'] : null;
    $this->data['Friend']['user_id'] = $me['User']['id'];
    $this->data['Friend']['friend_user_id'] = $id;
    $this->data['Friend']['status'] = 0;
    if ($this->Friend->save($this->data)) {
      $this->Session->write('sys_msg', 'Invitaion mail has been sent.');
      $this->redirect('/friends/');
    }
  }
}

次のその友達をAccept(承認)する場合はこちら。

function accept($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);
  $pal = $this->User->findById($this->Friend->field('user_id', "Friend.id=$id"));
  $this->set('pal', $pal);
  $this->pageTitle = 'Accept a new friend';
  // process form
  $this->set('memo', $this->Friend->field('memo', "Friend.id=$id"));
  $this->set('id', $id);
  // var_dump($this->params);
  if (!empty($this->params['form'])) {
    switch($this->params['form']['cmd']) {
      case 'yes':
        // accept
        $this->data['Friend']['id'] = $id;
        $this->data['Friend']['status'] = 1;
        $this->Friend->save($this->data);
        // link from me
        $this->data['Friend']['id'] = null;
        $this->data['Friend']['user_id'] = $me['User']['id'];
        $this->data['Friend']['friend_user_id'] = $pal['User']['id'];
        $this->data['Friend']['status'] = 1;
        $this->Friend->save($this->data);
        $this->Session->write('sys_msg', 'New member to your network!');
        $this->redirect('/friends/');
        break;
      case 'no':
        $this->Friend->del($id);
        $this->Session->write('sys_msg', 'Request declined!');
        $this->redirect('/users/home/');
        break;
      Default:
        break;
    }
  }
}

さすがにここらへんまでやるといろいろ慣れてきましたね。findやfield、findCountなどを使いこなせるようになってきました(かな?)。

さてこのままでもSNSっぽいとは思うのですがやはり日記のコメントぐらいは残したいもの。最後に日記コメント系の処理を作りこんでいきます。

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

WALKING TOURでしんみりしてしまった


ネットをうろうろしていたら久しぶりに発見。

外国にいるから、というわけではないですが、なんだかしんみりしてしまった。

いまさら感がありますが、やっぱりいい物語ですね。

walking.gif

↑ 2chってほんとすごいなぁ・・・。

人生考えちゃいますね。

» walkingtour

今日の朝食チャレンジ


さて外国に来るといつも「菓子パンまずそう・・・」と思っているのは僕だけではないはず。

個人的に日本の菓子パンは「世界イチィィィィryrwww」と思っているのですが、せっかくバンコクに来たので近くのファミリーマートで菓子パンにチャレンジしてみました。

thai_bread.jpg

↑ 選びに選んだ二品。スパイシーなツナのパン(初めて聞いたよ、それ)、と、あとはよくわからない不二家のパン(あ、いま見たら豚肉のパンだ)。

ツナのパンはなんとなくチャレンジできそうだったのと、それがあまりにひどかったときのための不二家、というチョイスです。でも、よく考えたら不二家もチャレン(ry。

さぁ!結果は!

というわけで一口、二口、パクパク・・・ぐぉ・・・。

・・・今は後悔している。すみませんでした。ちょっと口に合いませんでした・・・。

屋台はあんなにおいしいのになぁ・・・不思議だ。

友達の日記一覧を作る(CakePHP修行 #42)


さてmixiの中の人が「これが一番重いんですよー」と昔言っていたような気がする「友達の日記一覧」を作ります。ちょっと悩んだ。UserとFriendが紐づいているからそこからPostまで見にいけるか、など試行錯誤しましたが、面倒になったので二段階で処理しました。

これって一段階で処理できるようなものなのかな?アソシエーションと$recursiveとか使えばひょっとしてできる?(>青い人)。

ではコード。

// friends' posts
$my_pals = array();
foreach ($this->Friend->findAll(array('Friend.user_id'=>$me['User']['id'],'Friend.status'=>1),'friend_user_id') as $pal) {
  $my_pals[] = $pal['Friend']['friend_user_id'];
}
$this->set('posts', $this->Post->findAll(aa("Post.user_id", $my_pals),null,'Post.created DESC'));

$my_palsにまずは友達のIDを突っ込み、そのIDに該当する日記を引っ張ってきています。ついでに作成日順に並べ替えてみました。

できあがったのはこちら。

home.gif

↑ 無事に友達の日記を引っ張ってこれました。数が多くなってきたときの処理作らなくちゃなー。

さて、こんなところですかね・・・次は何だろ。

友達の招待&承認系の処理か・・・これが鬼門。

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

ヘルパーの使い方がわからなかったよ!(CakePHP修行 #41)


CakePHPにはやたら便利そうなヘルパーというものがあるのですが、マニュアルに載っている数々の便利機能が使えなくて四苦八苦してしまいました。

何が足りなかったかというと、コントローラー内における次の一行・・・。

var $helpers = array("time");

標準でHTMLヘルパーとか使えるからtimeヘルパーもそのままいけるかと思ったじゃないか!自分が気づかなかっただけだけとこれでかれこれ30分悩んでしまったよ・・・。

timeago.gif

↑ これで$time->timeAgoInWords()が使えるようになった!

それにしてもタイまで来て食事も抜いた状態で開発ってどういうことだよ・・・でも今夜は報告会。それまでになんとか形にしたい。まぁ、なんとかなりそうかな。夜ぐらいは出かけようかな。

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

友達の詳細画面を作ってみる(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で作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

本日のランチとか(&携帯事情その2)


オリーブが入っているらしいチャーハンかな。35バーツ(140円ぐらい)也。うまい。そういえばここ何日かお酒呑んでいない。いい傾向だ。

thai_1.jpg

↑ おいしい・・・つか、安すぎるぞ。こっちの食費。

泊まっている部屋の回線が細くてブログの更新もままならないわけですが、まぁ、のんびり行きます。

ちなみに1階にあるカフェまで来れば(今もいる)速度は2倍!7時から23時まで開いているのでなかなか快適です。

thai_2.jpg

↑ カフェもなかなかおしゃれでいい感じ。コーヒー一杯40B。チャーハンより高いぞ・・・。

それはそうと、こちらの携帯、撮った写真を携帯上で加工できてちょっとおもしろい。枠つけたり、クリップアートつけたり、テキスト入れたり。こういうのって日本の携帯でもできたっけ?

kao_1.jpg

↑ こういう写真を・・・。

kao_2.jpg

↑ こう加工してみた。これぐらいだったら携帯だけで出来る。

さてさて、作業に戻りますかね。また生活で気づいたことがあったらレポートしますねー。