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

さて画像アップ処理の続き・・・だけどCakePHPじゃなくてPHPの方に問題が。 いろいろ作っていたらGDでJPEG Supportがなかった。つうわけでlibjpegをインストール後、PHP5のconfigureからやり直し。ついでにPHPも5.2.3にバージョンアップ。 参考にしたサイトはこちら。 PHPにてGDライブラリの関数が使用できす、 Fatal error: Call to undefined function ImageCreateFromJpeg() とエラーが帰ってきます。 (コマンドライン、Apache経由でも同.. – 人力検索はてな cloned.log – PHPで画像を扱えるようにする(インストール手順など) 勉強になりました!ありがとうございました。インターネットってすごいなぁ・・。 ↑ 無事にGDが使えるようになりました。 さて下準備はよしとして、次はいろいろ画像処理を行います。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

プロフィール画像の削除処理をつくるぞ(CakePHP修行 #29)

さて次にプロフィール画像の削除処理を作ります。仕様は↓のような感じ。 写真がアップされている場合、設定変更画面(/users/edit/)で「削除する?」のチェックボックスを表示。 写真がアップされていない場合、設定変更画面(/users/edit/)でチェックボックスを表示しない。 ■ 画面を作る 画面はこんな感じで。画像がアップされているときだけチェックボックスが出ます。なお、現在の画像も合わせて表示させます。 ↑ 画像がアップされていないときは画像とチェックボックスは非表示。 edit.thtmlはこんな感じ(該当部分だけ)。 <?php if (!empty($me[‘User’][‘pic’])) { ?> <tr> <tr valign="top"> <td align="right">&nbsp;</td> <td><img src="/pics/<?= $me[‘User’][‘pic’]; ?>" width="50" height="50" border="0" alt="" /> <?= $html->checkbox(‘User/pic_del’, array(‘value’=>’1’)); ?> delete this?</td> </tr> <?php } else { ?> <?= $html->hidden(‘User/pic_del’, array(‘value’=>’0’)); ?> <?php } ?> ちなみに画像がアップされていないときはhidden属性でUser/pic_delを0にしています。これはusers_controller.phpで吸収したほうがよかったのだろうか。どうだろ? ■ コントローラーの処理 で、そのusers_controller.phpですが、次のとおりです。 // 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; } 画像のファイル名が入っていなくて、該当するファイルが存在して、pic_del項目がTrueの場合に削除処理をするようにしています。逆をいえば、削除チェックボックスにチェックがあっても、画像のファイル名が指定されていれば画像のアップ処理を優先させるということですね。これはこれでいいすよね。エラーを出そうとも思いましたが、まぁ、いいでしょう。 そんなところですかね。テストもうまくいったし、これはこれでいいかな。次は画像のエラー処理にいきたい。ファイルサイズチェックして大きなファイルははじきたいと思います。そのあとは画像縮小処理かな。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

データの取り出し方ってどうやるの?(CakePHP修行 #28)

青い人に「チャットで質問されるとブログで書くことがなくなるので」とやんわりとチャット拒否されました。悲しいです。 したがってここで質問することにしてみます。答えてくれるうれしいなぁ・・・。 知りたいのは、指定したユーザーのデータからパスワードだけ取り出すやり方。 まずやってみたのはこちら。コントローラー内で次のようなコードを書いてみます。 $this->User->id = 1; $me = $this->User->read(); pr ($me[‘User’][‘pwd’]); もしくは、 $me = $this->User->findById(1); pr ($me[‘User’][‘pwd’]); かな。 でもこの場合って、$meを介さなくちゃいけないのがいまいち・・・と感じていたり。 $this->User->id = 1; $pwd = $this->User->read(【ここがわからん!】); とかってできないのかな・・・?教えて青い人! ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

バグつぶし他(emptyとisset、WWW_ROOT、low関数)・・・(CakePHP修行 #27)

さてCakePHP修行。ちなみに昨日はサイゼリヤで深夜1時から4時まで作業しました。今朝、あらためてコードを見てみるとバグ&穴だらけでした。 次の処理に移る前にいくつか細かい点を直しておきます。 ■ パスワードの処理が間違えていた users_controller.php中、edit()の処理が間違えていました。どうりでうまくログインできないと思った・・・。 $this->data[‘User’][‘pwd’] = isset($this->data[‘User’][‘pwd’]) ? sha1(PWD_KEY.$this->data[‘User’][‘pwd’]) : $this->User->read(‘pwd’); これだとパスワード空欄でSubmitしたときにissetがTrueになってしまいますね・・・正しくはemptyでした。あと、そのほかの処理も加えてもう一度リファクタリングした結果がこちら。 function edit () {   $this->pageTitle = ‘Edit My Profile’;   $this->checkSession();   $me = $this->User->findById($this->Session->read(‘my_id’));   $this->set(‘me’, $me);   if (empty($this->data))   {     $this->data = $me;   }   else   {     // password change     $this->data[‘User’][‘pwd’] = empty($this->data[‘User’][‘pwd’]) ? $me[‘User’][‘pwd’] : sha1(PWD_KEY.$this->data[‘User’][‘pwd’]);     // file upload     if (!empty($this->data[‘User’][‘pic’][‘name’]))     {       $ext = low(preg_replace("!.*\.!", null, $this->data[‘User’][‘pic’][‘name’]));       $filename = sprintf("%05d.%s",$me[‘User’][‘id’], $ext);       move_uploaded_file($this->data[‘User’][‘pic’][‘tmp_name’], WWW_ROOT."pics".DS.$filename);       $this->data[‘User’][‘pic’] = $filename;     } else {       $this->data[‘User’][‘pic’] = $me[‘User’][‘pic’];     }     // save     if ($this->User->save($this->data[‘User’]))     {       $this->Session->write(‘sys_msg’, ‘Your profile has been updated.’);       $this->redirect(‘/users/home/’);     }   } } 上記ですが、いきなり$meにユーザーデータを突っ込んでしまったのですが、良いですかね?それとも$this->User->idだけ取得しておいてその都度必要なデータを取得したほうが良かったのだろうか。ちょっと富豪的ですが、個人的にわかりやすいからこれでいいかな? あと、前回書いた/webrootへの定数ですが、あらためてマニュアルを読むとWWW_ROOTなるものがありました・・・。 それからstrtolower()はlow()で再定義されているのですね。こちらも便利。 そんなところですかね。では今からプロフィール画像の削除処理に移ります。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

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

さてCakePHP修行。Rapid開発のためのフレームワークなのにもたもたしていてはいけません。すみません、すみません。 今日は画像アップロード処理。考えている仕様は以下のとおり。 画像はJPEG/GIF/PNGなどがアップ可能。 1ユーザーにつき1枚の画像がアップ可能。 アップした画像は適当に縮小される。 アップした画像は/webroot/pics以下にUserIDをファイル名として格納される(0001.jpgとか) 画像の削除ももちろん可能。 さてではいってみましょう。なお、長くなりそうなので今回はパートIということで。 ■ MAX_FILE_SIZEにはヘルパーつかえんのか? さてまずはedit.htmlを変更したいのですが・・・MAX_FILE_SIZEの指定ではまりました。これってhtml helperでできないのかしらん? <?= $html->hidden(‘MAX_FILE_SIZE’, array(‘value’=>’30000’)); ?> 上のようにやってみてもエラーになってしまいます。しょうがないのでそのままhidden属性のinputタグを書いてしまいました。これはこれでいいのかな? あとはFormのenctypeを入れたり、実際アップするinputタグをいれたりで、結局できたedit.thtmlは次のとおり(Form部分だけね)。 <form action="<?= $html->url(‘/users/edit’); ?>" method="post" enctype="multipart/form-data"> <?= $html->hidden(‘User/id’); ?> <input type="hidden" name="MAX_FILE_SIZE" value="30000" /> <table border="0" cellspacing="2" cellpadding="2" width="100%"> <tr valign="top"> <td align="right">Name</td> <td><?= $html->input (‘User/name’, array(‘size’=>’40’)); ?></td> </tr> <tr valign="top"> <td align="right">Email</td> <td><?= $html->input (‘User/email’, array(‘size’=>’40’)); ?><?= $html->tagErrorMsg(‘User/email’, ‘<span class="err">Email is required.</span>’); ?></td> </tr> <tr valign="top"> <td align="right">Password</td> <td><?= $html->password (‘User/pwd’, array(‘size’=>’20’, ‘value’=>”)); ?></td> </tr> <tr valign="top"> <td align="right">Profile</td> <td><?= $html->textarea(‘User/profile’, array(‘cols’=>’38’, ‘rows’=>’10’)); ?></td> </tr> <tr valign="top"> <td align="right">Picture</td> <td><?= $html->file(‘User/pic’); ?></td> </tr> <tr valign="top"> <td>&nbsp;</td> <td><?= $html->submit(‘Update’) ?></td> </tr> </table> </form> ■ ファイルをどこにどうアップするか? アップする画像ファイルの種類は限定したくないので、拡張子も含めてUsersテーブルに格納したいところ。まずはテーブル設計に変更を加えます。 CREATE TABLE users (     id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,     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 ); ↑ picフィールドを足しました。 次にファイルアップ先の/webroot/picsを作り、Permissionを与えます(777にしたけどいいのかな?)。 ここまでやったあとにusers_controller.phpを次のように変更。 function edit () {   $this->pageTitle = ‘Edit My Profile’;   $this->checkSession();   $this->set(‘me’, $this->User->findById($this->Session->read(‘my_id’)));   $this->User->id = $this->Session->read(‘my_id’);   if (empty($this->data))   {     $this->data = $this->User->read();   }   else   {     $this->data[‘User’][‘pwd’] = isset($this->data[‘User’][‘pwd’]) ? sha1(PWD_KEY.$this->data[‘User’][‘pwd’]) : $this->User->read(‘pwd’);     if (isset($this->data[‘User’][‘pic’]))     {       $ext = strtolower(preg_replace("!.*\.!", null, $this->data[‘User’][‘pic’][‘name’]));       $filename = sprintf("%05d.%s",$this->User->id, $ext);       move_uploaded_file($this->data[‘User’][‘pic’][‘tmp_name’], APP."webroot/pics".DS.$filename);       $this->data[‘User’][‘pic’] = $filename;     }     if ($this->User->save($this->data[‘User’]))     {       $this->Session->write(‘sys_msg’, ‘Your profile has been updated.’);       $this->redirect(‘/users/home/’);     }   } } 細かいですが、APPのように/app/webroot/を指す定数はないのだろうか・・・。ちょっと気になる。 さて実際にテストしてみるときちんとファイルがアップされているようです。 ↑ 画像ファイルがアップされました! しかしまだまだ残作業がありますね。 エラー処理(大きすぎるファイルははじきたい) 適当に縮小したい。 アップしてあるファイルの削除機能をつけたい。 セキュリティ? まずはこんなところで。パートIIへ続きます。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

ログイン後にちゃんとクリックしたURLに戻るようにする(CakePHP修行 #25)

さてログイン処理に戻りましょう。 今回やりたいのは「ログイン後にちゃんとクリックしたURLに戻るようにする」です。現在のロジックではログイン後は必ず/users/home/にとぶようになっています。 しかし/users/editをブックマークやメモしておいて、そこに直接飛びたい場合もあるでしょう。そこでログイン前にクリックしたURLを保存し、ログイン後にちゃんとリダイレクトするようにします。 ■ login_backurlをSessionに書き込み ログインしているかどうかのセッションをチェックする際に$login_back_urlをセットしておきます。 class AppController extends Controller {   function checkSession()   {     // If the session info hasn’t been set…     if (!$this->Session->check(‘my_id’))     {       // save login back url       $this->Session->write(‘login_back_url’,((getenv(‘SERVER_PORT’)==443)?’https://’:’http://’).getenv(‘HTTP_HOST’).getenv(‘REQUEST_URI’));       // Force the user to login       $this->redirect(‘/users/login’);       exit();     }   } } ■ リダイレクト処理を変更 次にusers_controller.phpを次のように変更。セッションから読み取ってリダイレクト先を変えています。セッションから読み取れなかったらとりあえずホーム画面を表示。 function login() {   $this->pageTitle = ‘Welcome to CODE*CODE!’;   // sys_msg   $this->set(‘sys_msg’, $this->Session->read(‘sys_msg’));   $this->Session->delete(‘sys_msg’);   // login back url   $login_back_url = $this->Session->read(‘login_back_url’);   // form start   $this->set(‘error’, false);   if (!empty($this->data))   {     $someone = $this->User->findByEmail($this->data[‘User’][’email’]);     if(!empty($someone[‘User’][‘pwd’]) && $someone[‘User’][‘pwd’] == sha1(PWD_KEY.$this->data[‘User’][‘pwd’]))     {       $this->Session->write(‘my_id’, $someone[‘User’][‘id’]);       if (isset($login_back_url))       {         $this->redirect($login_back_url);         $this->Session->delete(‘login_back_url’);       } else {         $this->redirect(‘/users/home/’);       }     }     else     {       $this->set(‘error’, true);     }   } } これでOKですかね。たとえば、ログインしていない状態でhttp://www.codexcode.com/users/editをクリックすると次のようになります。 ↑ ログインしていないのでログイン画面へ。ここでログインすると・・・。 ↑ ちゃんとホーム画面じゃなくて、設定変更画面へ飛びました。 こんなところですかね。ちゃんと動くようになってきましたね。さてあとは面倒そうな「情報を保存する」機能を実装しますかね・・・。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

jQueryを使ってメッセージをふわーんとさせる(CakePHP修行 #25)

さて、何かの処理をしたときに現れるメッセージですが、jQueryを使ってふわーんと消してみたいと思います。prototype.jsでもいいのですが、なんか重いイメージがあるのでjQueryで。 以下、作業手順。 ■ jQueryをインストール jQueryのサイトからダウンロード、/app/webroot/jsに設置します。 ■ jquery.jsをインクルード /app/views/layout/default.htmlにてjQueryをインクルード。 <script type="text/javascript" src="/js/jquery.js"></script> ■ メッセージを出すところを変更 Viewsでメッセージを出すところを次のように変更。ちょー簡単。 <?php if (isset($sys_msg)) { ?> <div id="sys_msg"><?= $sys_msg; ?></div> <script> $(document).ready(function() { $("#sys_msg").fadeOut(2000); }); </script> <?php } ?> これで作業終了。思ったより簡単ですね。 ↑ ふわーんと消えているところ。動画でアップしようと思ったけど面倒なんで略。 それにしてもjQueryは便利ぽい。ちょっとRubyっぽいのかな?どちらにしてもちょっと極めたいところ。ま、おいおい。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

ログイン画面に飽きたから設定変更画面を作った(CakePHP修行 #24)

さて、ログイン画面の改修を続けようと思いましたが、いろいろ煮詰まったので設定変更画面を作りましたw。 以下、作業手順。 ■ /controllers/users_controller.phpにedit()を追加 URL的には/users/editで呼び出したいのでedit()を追加します。 function edit() {   // あとで書く。 } ■ Viewを作る 次に/views/users/edit.thtmlを作ります。 ↑ こんな感じ。 ■ ロジックを書く まー、次のような感じでしょうか。CakePHPのチュートリアルを見ながら適当に作ったら出来た。ビバPHP!w ちょっと気をつけるべきところはパスワードの処理でしょうかね。パスワードに値が入っていたときだけ保存するようにしています。 function edit () {   $this->pageTitle = ‘Edit My Profile’;   $this->checkSession();   $this->set(‘me’, $this->User->findById($this->Session->read(‘my_id’)));   $this->User->id = $this->Session->read(‘my_id’);   if (empty($this->data))   {     $this->data = $this->User->read();   }   else   {     $this->data[‘User’][‘pwd’] = isset($this->data[‘User’][‘pwd’]) ? sha1(PWD_KEY.$this->data[‘User’][‘pwd’]) : $this->User->read(‘pwd’);     if ($this->User->save($this->data[‘User’]))     {       $this->Session->write(‘sys_msg’, ‘Your profile has been updated.’);       $this->redirect(‘/users/home/’);     }   } } ■ テストする テストもすんなりOK。メールが入っていないときはエラーがでるようにもしてみました。ちなみに現段階ではプロフィール画像のアップ機能は省略してあります。それについては大変そうなんでまた後日。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

_がつく関数について(CakePHP修行 #23)

さて、青い人と週末会っていたのでいろいろ聞きました。 細かいところですが、前回の実装でちょっと変なところがありました。 それはUser Controllerからしか使わない関数を、次のように定義してしまった点。 function generatePwd() {   // 中略 } これだと、/users/generatePwdというURLが出来てしまうのでおすすめできないとのこと(Viewを作らないといけませんが)。このようにViewが必要のない内部関数は頭に_をつければURLが生成されないとのことらしいです。 したがって、上記の関数は以下のように書き直しました。 function _generatePwd() {   // 中略 } あわせてこの関数を使っているところも変更。これですっきりかな。 なお、その話の流れでComponentsについても勉強しました。これはのちのち使うことがあるでしょうからそのとき実例を交えて説明しますね。 つうわけで勉強になりました。いつもながらありがとう、青い人! ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

「パスワードを忘れた方は」を実装したい(CakePHP修行 #22)

すっかり間があいてしまいました・・・決して挫折したわけではありません。いろいろあるよね、人生。 さて、気を取り直してログイン画面の実装を進めていきます。今回は「パスワードを忘れた場合」の処理を付け加えます。 作業計画は以下のとおり。 ControllerにresetPwd()を加え、Viewを作成。 resetPwd()ではパスワード生成、DBの更新、メールの送付までを行います。 テストして完了。 ■ コントローラーとViewを設定 ではまずはControllersとViewsを次のように変更。 /controllers/users_controller.phpに以下を追加。 function resetPwd() {   // 中身はあとで書く。 } /views/users/reset_pwd.thtmlを追加。/users/resetPwd/にアクセスします。 ↑ とりあえずきちんと表示されました。もちろんまだ動かないw。 ■ resetPwd()を書く えーと、あれだ。完成系をいきなりご紹介。 function resetPwd () {   $this->pageTitle = ‘Reset Your Password’;   $this->set(‘error’, false);   if (!empty($this->data))   {     $someone = $this->User->findByEmail ($this->data[‘User’][’email’]);     if ($someone)     {       // set new pwd       $new_pwd = $this->generatePwd ();       $this->User->id = $someone[‘User’][‘id’];       $this->User->save (array (‘pwd’ => sha1(PWD_KEY.$new_pwd)) );       // send mail       $mail_file = VIEWS . ‘mail/user_resetPwd.php’;       $msg = implode (file($mail_file));       eval ("\$msg = \"$msg\";");       $toName = $someone[‘User’][’email’];       $subject = "[code*code] Your new password!";       mb_send_mail ($toName, $subject, $msg, "From: ".ADMIN_EMAIL);       // write msg, jump       $this->Session->write (‘sys_msg’, ‘new password has been sent.’);       $this->redirect (‘/users/login/’);     }     else     {       $this->set(‘error’, true);     }   } } なお、この中で新しいパスワードを生成するロジックがありますが、それは以下のようなものを作りました。 function generatePwd () {   $len = 8;   srand ( (double) microtime () * 1000000);   $seed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";   $pass = "";   while ($len–) {     $pos = rand(0,61);     $pass .= $seed[$pos];   }   return $pass; } 英数字から8桁のパスワードを生成するプログラムです。なんかもちょっとエレガントにかけるような気がしないでもない。 あと、メールを送るときの送信元であるADMIN_EMAILを前回と同じように/config/app.phpに定義しました。 苦労した点をいくつか。 データの保存の仕方がわからない パスワードを生成したあとに、新しいパスワードでUsersテーブルをUpdateしなくてはいけません。チュートリアルではFormから渡されたデータをざっくり渡す例がありましたが、個々のフィールドのUpdateの仕方がわかりませんでした。 ここはずるをして青い人に聞き、「$this->User->save (array (‘pwd’ => sha1(PWD_KEY.$new_pwd)) );」で解決することが判明・・・。わかんなかったよ。でもまぁ、解決。 メールのテンプレートファイルをどこに置いてどうアクセスすべきか? メールはハードコードしてはあほっぽいのでテンプレートファイルにしておきたいところ。ファイルを作るのは簡単ですが、どうやってアクセスしたらいいか悩んでいたところ、青い人に聞いたらAPPとかVIEWSとか便利な定数があるらしいのでVIEWSの中にメールテンプレートファイルを作り、そこにアクセスするようにしました。これで変更も楽々です。 ↑ メールがちゃんと届きました! 「メールを送りました」メッセージを出したい パスワードを再設定したあとにログイン画面にリダイレクトするのですが、そのときに「メール送りましたよー」的なメッセージが欲しいところ。これは一回表示させたら消したいので、URLのパラメータで渡すことなく、そのままセッションに書き込み、Viewに渡したら消すように設定しました。 ↑ あとで余力があればjQueryかなんかでふわーんと消したいところ。 以上ですかね。ちょっと時間があきましたが、やっぱりプログラミング楽しい。さてこの勢いで「情報を保存する」機能を実装してみたいと思います。 ※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。