SSブログ
Lancers.jp

CakePHP その15 〜第二回CakePHP勉強会〜 [CakePHP]

完全にいまさらですが、またCakePHPの勉強会に行ってきた。
http://events.php.gr.jp/event.php/event_show/36

言い訳すると、この頃すごい忙しく、懇親会も途中退場して仕事に戻ったりしてて、それが落ち着いたらSo-netが長期メンテナンスに入ってしまってたりと、なかなか書く機会がなかったので、こんな時期になりました。

細かい内容は端折るけど(上部のリンクから見てみてください)、今回は実際の事例が中心で、興味深かった。
システムのこととか、わからないことも多いから、そういうのが勉強になる。
今回からのライトニングトークも、細かく踏み込んだ内容が多かったので、よかった。
特に田口氏のトークは、最初にすべきことの要点をうまくまとめていて非常に参考になったが、ただ、笑いを取っていたネタが、何一つわからなかったのが残念。
(あとで人に聞いたら、2chとニコ動のネタだったらしい。)

ちょっとだけいた懇親会でも第一回目で会った方や新しい人とも出会えたので、忙しい中だったけど参加してよかった。



CakePHP その14 〜MobileHelper〜 [CakePHP]

というわけで、どうにか携帯用サイトのアドレスにうまく飛ばせないか考えた。
無い頭を降る回転させた結果、どうせ、すでに空のMobileHelperは作ってあるんだから、それを使おうという結論に落ちついた。

とりあえず、linkさえあればいいので、それをMobileつくる。
微妙な(気に入ってない)部分も多々あるけど、よくわからんちん。

MobileHelper
<?php
class MobileHelper extends AppHelper{
    var
$helpers = array('Html');
    
    function
link($title, $url = null, $htmlAttributes = array(), $confirmMessage = false, $escapeTitle = true) {

        if(!
is_array($url)){
        
            
$params = split("/",$url);
            if(!empty(
$params[0])){
                
array_unshift($params,"",$this->params['controller']);
            }
            
$url = join("/",$params);
            
$url = "/m".$url;
            
//
            
        
}else{
            
$url['plugin'] = "m";
        }

        return
$this->Html->link($title, $url, $htmlAttributes, $confirmMessage, $escapeTitle);
    }
}
?>

/cake/libs/view/helpers/paginator.php
45行目に'Mobile'を追加
var $helpers = array('Html', 'Ajax', 'Mobile');

257行目あたり
$obj = isset($options['update']) ? 'Ajax' : 'Html';
//追記2008.02.08-->
if($this->params['webservices'] == 'Mobile'){
    $obj = 'Mobile';
}
$obj = isset($options['update']) ? 'Ajax' : 'Html';
//-->追記2008.02.08

view
<html>
<head>
    <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=<?=$html
->charset()?>
    <title>携帯サイト</title>
</head>
<body>

<?php
    $url
= '/files/image.jpg';
    
$img = $html->image($url);
?>
<?=$mobile
->link($img, $url, array('alt'=>'画像','escape'=>false))?>

<ul>
<?=$mobile->link('<li>トップページへ</li>','/',array('escape'=>false))?>
<?=$paginator
->first('<li>先頭へ</li>',array('escape'=>false)) ?>
<?=$paginator
->prev('<li>前へ</li>',array('escape'=>false)) ?>
<?=$paginator
->next('<li>次へ</li>',array('escape'=>false)) ?>
<?=$paginator->last('<li>最後尾へ</li>',array('escape'=>false)) ?>
</li>

</body>
</html>

link()はMobileHelperで、それ以外はHtmlHelperで。住み分け。
個人的には、今のところ良いと思ってるんだけど、どうでしょう?


CakePHP その13 〜webservicesとpaginate〜 [CakePHP]

Cake1.2betaを使って下記を参考に携帯用ページを作成してみた。

「CakePHP 携帯用ビューを表示する」Shin x blog
http://www.1x1.jp/blog/2006/09/cakephp_mobile.html

「cakePHPでPCと携帯で出力文字コードを変更する方法」インストールメモ
http://www.easy-in.net/archives/8/

あっけないほど簡単に設定することができたので、上記サイト様にはほんとに感謝するばかり。

ただ、一つ問題があって、1.2で追加されたpaginateを使うと、リンク先URLに/m/は含まれない。当たり前のような、そうでないような。

とりあえず、場当たり的な解決だけど、/cake/libs/router.phpに手を入れた。
669行目あたりで$baseの設定があるので、そこに追加

$base = $path['base']; // dont need …
//2008.02.07-->
if(!empty($params['webservices']) && $params['webservices']=='Mobile'){
    $base .= "/m";
}
//-->2008.02.07

なんか違うような。コアな部分に手を入れるのって、すごく違和感がある。
もっとスマートなやり方がある気もするけど…。

#やっぱし、良くないらしいです。う〜〜〜。
#でも、やっぱり方法はわからないので、次のバージョンでなんとかして欲しいなー。


CakePHP その12 〜ImageRenderコンポーネント〜 [CakePHP]

欲しいのが無ければ自分で作る!ってことで、自作コンポーネントを作ってみた。

まえから、楽天みたいに欲しい大きさで画像を返してくれる仕組みが欲しかったんだよね。
PHP初級者でコンポーネントとかまともに作ったこと無いので、見よう見まねです。

  • SwfUploadコンポーネントと一緒に使うことを想定してますが依存はしてません
  • 1.2.0.5875で作りました
  • GD2.0以上必須です。
  • てか、Mac用XAMPP0.7.1上でしか動作確認してないです。
  • GIFとJPGとPNGしかサポートしてません
  • 実際の大きさ以上にはなりません
  • 基本的に毎回サムネイルを生成してるので、当然ですが大量の画像を表示するとサーバーが死ぬと思います。
  • これで良いのか、だれか教えてください
  • 先頭の/* $Id$ */って何?


【修正】

  • サイズの指定がなかったときのことを全く考えてなかったので、指定しない場合や0のときはそのまま出力するようにした(07.12.29)


で、使い方はSwfUploadコンポーネントのFILEコントローラーのopen関数を変更する
(自分は前回書いたようにFILEではなくIMAGEクラスとして使ってるので、IMAGEコントローラー)
変更前
    function open($id) {
        $file = $this->get($id);
        if (isset($file)) {
            $this->redirect($file['Image']['path'] . $file['Image']['name']);
            exit();
        }
    }

変更後
    function open($id=null,$maxsize=null){
        $file = $this->get($id);
        if (isset($file)) {
            $filename = WWW_ROOT.$file['Image']['path'] . $file['Image']['name'];
            if(is_file($filename)){
                $this->ImageRender->max_size = $maxsize;
                //$this->ImageRender->resampling = 'normal';
                $this->ImageRender->render($filename);
            }
        }
    }

で、SwfUploadコンポーネントでアップしたファイルには下記でアクセス。
http://localhost/image(クラス名)/open/(画像のid)/(欲しいサイズ)/
すると、欲しいサイズの大きさの画像が表示されます。
ええと、ヘルパーでの書き方はわかりません(なんせFlashで使うために作ったんで)
や、普通に書けば良いと思ういますがね…

ImageRenderコンポーネント
<?php
/* $Id$ */
/**
* ImageRenderComponent
* Copyright (C) 2007-2008
* @version 0.0.2
*/

/**
* @package ImageRenderComponent
* @subpackage controllers.components
*/
class ImageRenderComponent extends Object {
    
    
/* component configuration */
    
var $name = 'ImageRender';
    
/*
        $params[0] width
        $params[1] height
        $params[2] type IMAGETYPE定数値
        $params[3] attributes
        $params['bits'] bits
        $params['channels'] color channel
        $params['mime'] mime type
    */
    
var $params = array();
    var
$img = null;
    var
$max_size = 0;
    
//$resampling リサンプリングの方式 normal or smooth
    
var $resampling = 'smooth';
    
    function
resize($filename=null){
        
//縦型か横型か
        
$maxsize = $this->max_size;
        
$w = $this->params[0];
        
$h = $this->params[1];
         if(
$w == $h){
            
$cwidth = $maxsize;
            
$cheight = $maxsize;
        }else if(
$w > $h){
            
$cpercent = $w/$maxsize;
            
$cwidth = $maxsize;
            
$cheight = ceil($h/$cpercent);
        }else{
            
$cpercent = $h/$maxsize;
            
$cwidth = ceil($w/$cpercent);
            
$cheight = $maxsize;
        }

        switch(
$this->params[2]){
            case
1:    
                
$src = imagecreatefromgif($filename);
                
$img = imagecreate($cwidth,$cheight);
                break;
            case
2:
                
$src = imagecreatefromjpeg($filename);
                
$img = imagecreatetruecolor($cwidth,$cheight);
                break;
            case
3:
                
$src = imagecreatefrompng($filename);
                
$img = imagecreatetruecolor($cwidth,$cheight);
                break;
            default:
                return
false;
        }
        if(
$this->resampling == 'normal'){
            
$r = imagecopyresized($img,$src,0,0,0,0,$cwidth,$cheight,$w,$h);
        }else{
            
$r = imagecopyresampled($img,$src,0,0,0,0,$cwidth,$cheight,$w,$h);
        }
        
$this->img = $img;
        
        return
$r;
    }
    
    function
load($filename=null){
        
$this->params = getimagesize($filename);
        //
        
$r = 0;
        if($this->max_size>0){
            if(($this->params[0]>$this->max_size) || ($this->params[1]>$this->max_size)){
                if(
$this->resize($filename,$this->max_size)){
                    
$r = 1;
                }
            }
        }
        
        if(!
$r){
            
$fo = fopen($filename, "rb");
            
$this->img = fread($fo,filesize($filename));
        }
        return
$r;
    }
    
    function
render($filename=null){
        
//情報の取得
        
$r = $this->load($filename);
        
$mime = $this->params['mime'];
        
header("Content-Type:".$mime);
        
        if(
$r){
            switch(
$this->params[2]){
                case
1:
                    
imagegif($this->img);
                    
imagedestroy($this->img);
                    break;
                case
2:
                    
imagejpeg($this->img);
                    
imagedestroy($this->img);
                    break;
                case
3:
                    
imagepng($this->img);
                    
imagedestroy($this->img);
                    break;
                default:
                    print
$this->img;
                    break;        
            }
        }else{
            print
$this->img;
        }
        
        exit();
    }
    
}
?>










CakePHP その11 〜SwfUploadコンポーネント〜 [CakePHP]

Flashから、効率よくファイルをアップロードするのを考えると、fileHandlerコンポーネントは使いづらい。
という理由で、bakery.cakephp.orgをみてみると、新しいコンポーネントがいくつかあって、そのなかでSwfUploadというまんまの名前のコンポーネントがあった。
http://bakery.cakephp.org/articles/view/swfupload-and-multipurpose-uploader

1.1系のようだけど、1.2でも問題無く動いてるので、これを使うことにしよう。
ただ、サンプルのようにモデル名がFileだと、いろいろと問題あったので、モデル名はImageにした。

それが本当に必要な時には、必要なものが用意されているもんだなー。
それがない時は、必要のない時(自分で作れるハズorもっといい方法がある)と思い知る。


CakePHP その10 〜CakePHP勉強会〜 [CakePHP]

CakePHPの勉強会に行ってきました。
こういう勉強会に参加するのは、うまれて初めてなので、すごい楽しみにしていました。

なのに、遅刻…。
かなり興味のあったShin氏の発表を聞き逃してしまいました。
ですが、資料をアップしてくれてたので、感謝感激しています。
http://www.1x1.jp/blog/2007/12/cakephp_event.html
Shin氏は、すごく人が良さそうな男前な方でした。

akiyan氏の「ドライケーキレシピ」はかなり実践的な内容でした。
SQLへのConditionでの連想配列の使い方は、自分にはなかった発想だったので、関心しきり。

途中で、yando氏が言った「CakePHPを使うのは、配列職人になること」という言葉が印象的で、akiyan氏の職人技はもっと見てみたかったです。

yando氏の1.1から1.2への移行方法はみてて楽しかったです。
リアルにその場で動く様子が見えるのは、説得力がありますね。

準備等、ほんとうに大変だったでしょうが、よい勉強会でした。
懇親会では自分のスキル不足から、何話していいんだかわかんなくて、話が少なくてすみませんでした。

おそらく、皆さんは同士って感じで盛り上がってたのでしょうが、自分的には異種交流会みたいで、周りで話しているのを聞いているだけでも為になったので、また機会があれば参加したいです。

皆様、ありがとうございました。


最近アクセスが多い [CakePHP]

最近、アクセスが多い気がする。
って、1日せいぜい20くらいだけど、こんな場末のブログに?と思って、ググって見たらなんか色々あった。

以前、リンクを勝手に貼らせてもらった『ZoroMemory「cakePHPのfindBy*で複数の条件が設定可能」』にいつの間にか追記があって、

自分はPHP5だったので気がつきませんでした。はまったsandmanさんごめんなさい。

ぎゃあ!なんでか謝られてる!

いやいやいやいや、ってかこちらこそいつも参考にさせてもらってて、というかパクらせてもらって、ごめんなさいって感じです。
すげぇ勉強になります。

あと、おなじsandmanという方のブログもあった。
「どうにかなるBLOG」
http://sandman.s6.xrea.com/nucleus/

とてもわかりやすくい記事で、素晴らしい内容。おなじsandmanでも天と地の差。
超オススメ。CakePHPもちょっとあって、これからの内容に期待大です。
ただ、更新頻度は低めなようなので、更新してくれないと自分のスキルが上がらなくて困ります。
勿論、冗談ですが。

最近は、なんだか、すっげー中途半端なんだけど、CakePHPはちょっと置いておいて、Adobe Flex勉強してます。
うー、全部中途半端に手を出して中途半端に終わるのはどうかと思うんだけど、いまは実務より何が出来るのかっつーのを知る方が大切なんで、しょうがない。
つか、Flex+CakeAMF(swx?)PHPをしたいので、下調べ。
本気で知恵熱出てます。


CakePHP その9 - MailerComponent - [CakePHP]

CakePHP1.1系には、メイル関係のコンポーネントが無い。
Users in Japanで紹介されていたように、1.2のコンポーネントを流用する手もあるのだけど、個人的に使いやすそうで、目的にもあっているのがSimple SMTP Mailerだ。

凄く使いやすく軽いのだけど、日本語に対応していないので、これを日本語化する。
下記がまるごとそうです。

<?php
class MailerComponent extends Object
{

var
$to              = array();
var
$from            = 'xxx@xxx.com';
var
$fromname        = 'オレ様';
var
$Subject         = null;
var
$Message         = null;

    function
socketmail() {
    
        
mb_internal_encoding("UTF-8");

        
//ini_set("smtp_port", "25");  // Optional
        //ini_set("SMTP", "smtp.xxx.com"); // Optional

        
ini_set("sendmail_from", $from);

        
$connect = fsockopen(ini_get("SMTP"), ini_get("smtp_port"), $errno, $errstr, 30) or die("Could not talk to the sendmail server!");

        
$rcv = fgets($connect, 1024);

      
fputs($connect, "HELO {$_SERVER['SERVER_NAME']}\r\n");
      
        
$rcv .= fgets($connect, 1024);

  foreach(
$this->to as $toAddress){
        
        
$toValue = $toAddress[0];
        
$toKey = mb_encode_mimeheader($toAddress[1]);
        
      
fputs($connect, "MAIL FROM:$from\r\n");

        
$rcv = fgets($connect, 1024);

      
fputs($connect, "RCPT TO:".$toValue."\r\n");
        
$rcv .= fgets($connect, 1024);

      
fputs($connect, "DATA\r\n");
        
$rcv .= fgets($connect, 1024);

          
   
fputs($connect, "Subject: ".mb_encode_mimeheader($this->Subject)."\r\n");
   
fputs($connect, "From: ".mb_encode_mimeheader($fromname)." <".$from.">\r\n");
   
fputs($connect, "To: $toKey  <".$toValue.">\r\n");
   
fputs($connect, "X-Sender: <".$from.">\r\n");
   
fputs($connect, "Return-Path: <".$from.">\r\n");
   
fputs($connect, "Errors-To: <".$from.">\r\n");
   
fputs($connect, "X-Mailer: PHP\r\n");
   
fputs($connect, "X-Priority: 3\r\n");
   
fputs($connect, "Content-Type: text/plain; charset=ISO-2022-JP\r\n");
   
fputs($connect, "\r\n");
   
fputs($connect, mb_convert_encoding(stripslashes($this->Message), "JIS", "UTF-8")." \r\n");
   
fputs($connect, ".\r\n");

     
$rcv .= fgets($connect, 1024);

   
fputs($connect, "RSET\r\n");
     
$rcv .= fgets($connect, 1024);


  }

   
fputs ($connect, "QUIT\r\n");
     
$rcv .= fgets ($connect, 1024);

   
fclose($connect);
   
ini_restore("sendmail_from");
    
    }

    function
AddAddress($name = "",$address ) {

        
$cur = count($this->to);
        
$this->to[] = array(trim($address), $name);
        
    }


}
?>

AddAddress関数では、もとは送信先名をそのままto配列のキー名にしていたんだけど、日本語ではそれができないので、[アドレス,名前]という入れるにして、それをto配列に追加することにした。
だから、送信先を取り出す時に、while文ではなくforeach文を使った。

mb関数はいろいろ問題あるけど、とりあえず主要なメーラーではこれで問題なかったよ。

#追記
今までは、コードを載せるときは、「エンコードマニアックス」使ってたんだけど、PHPコードを載せるなら、「PHPコード変換機」の方が色もついてわかりやすいね。


CakePHP その8 - fileHandlerコンポーネント - [CakePHP]

たとえ簡易なものでも、CMSだったらファイルのアップロードくらいはしたいな、ってことでCakePHP用のファイルアップロード用コンポーネントのfileHandlerを使ってみた。
http://www.reversefolds.com/articles/show/filehandler

ちなみにアップロードはできても、削除はできないのでその辺は自前でヨロ。
使い方は、CakePHP Users in Japanの通り。
http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=397&forum=9

これ、アップロード毎に、暗号名のついたディレクトリをつくるのか。
うー、ディレクトリがいっぱいになってしまう。
まあ、便利なのは確かなので、とりあえずこれで行く。

でも、ファイルを沢山アップしたいときもあるので、その時のためにJavaScriptでファイルボタンを増やしたりできるスクリプトをつくった。
とくに、JavaScriptヘルパーとか使ってないです。htmlにベタ書きです。
まずは、初期設定。/app/controller/component/file_handler.php の下記を変更。
var $_required はファイルの一回の最大数なので、これを増やす。ちなみに、0を含むようなので、上記は2ファイルの同時アップっつー事になる。とりあえず、最大10ファイルにしておく。

32行目:var $_required = 9;

つぎに、/app/views/upload/index.thtmlの変更

適当にJavascriptを書く。

<script language="javascript" type="text/javascript">

//ここで、アップロード用ボタンの最初に出てる数を決めておく。とりあえず、2つ。
var file_num = [1,2];

function addFile(){
	if(file_num.length>=10){
		alert('これ以上増やせません');
	}else{
		var n = file_num[file_num.length-1] + 1;
		
		var wrapper = document.getElementById('file_select');
		
		var child = document.createElement('label');
		child.id = 'file'+n;
			
		var child_input = document.createElement('input');
		child_input.type = 'file';
		child_input.setAttribute('name', 'userfile[]');
		
		var child_anchor = document.createElement('a');
		child_anchor.href = 'javascript:deleteFile('+n+')';
		child_anchor.appendChild(document.createTextNode('このファイルを消す'));
		
		
		child.appendChild(document.createTextNode('写真'+n));
		child.appendChild(child_input);
		child.appendChild(child_anchor);
	
		wrapper.appendChild(child);
		//alert(r);
	
		file_num.push(n);
	}
}

function deleteFile(_n){
	//alert(_n);
	for(var i=0;i<file_num.length;i++){
		if(file_num[i] == _n){
			file_num.splice(i,1);
			break;
		}
	}
	//
	var wrapper = document.getElementById('file_select');
	var child = document.getElementById('file'+_n);
	wrapper.removeChild(child);
}

</script>


アップロード周りのボタンを変更。
13〜16行目の

<input type="file" name="userfile[]"/>
<br/>
<input type="file" name="userfile[]"/>


を下記に変更

<div id="file_select">
<label id="file1">写真1
  <input type="file" name="userfile[]"/>
  <a href="#" onclick="deleteFile(1)">このファイルを消す</a>
  </label>
<label id="file2">写真2
  <input type="file" name="userfile[]"/>
  <a href="#" onclick="deleteFile(2)">このファイルを消す</a>
  </label>
</div>
<input type="button" value="画像を追加する"  onclick="addFile()" /><br />

これで、Ajax風味のアップローダー完成ザンス。便利ザーマス。


CakePHP その7 - createdとmodified - [CakePHP]

CakePHPの便利機能として、DBにcreatedというフィールドをつくっておけば、データの挿入の日時を記入してくれるし、modifiedというフィールドがあれば、更新した時に自動的に更新した日時をいれてくれる。

ところが、このmodifiedがある時、一部のコントローラーで機能しなくなってしまっていた。
これに気づいたのが、オープンの前日(苦笑)それまでは、何度も確認して問題ないことを確かめていたのに…。

さあ、困った。
自分が使っているのが、1.1.15。一体、何が原因なんだろう?
ネットで検索したら、下記の情報が。
http://griffinm.wordpress.com/2007/05/28/why-created-and-modified-fields-might-not-save/

とりあえず、応急処置として、全ての保存前に

$this->data['User']['modified'] = null;


としたら、modifiedが機能した。

うーん、原因が分かんないのがやだな。

ついでに、コントローラーごとに、この機能をオン/オフする変数が欲しい。


この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。