Home > アーカイブ > 2008-04

2008-04

Podcastで聞くPHP

  • 2008-04-28 (月)
  • PHP
この記事の所要時間: 122

先日ipod Touchを買いました*1

買う前は、nanoやclassicとで迷っていた(特に価格面で)のですが、実際買ってみると独特のUIや液晶の大きさなど、とても気に入ってます。

iPodならPodcast、という事でPHPに関するPodcastを探してみました。

そこで見つけたのが、PHP Podcastsです。

PHPに関するPodcastと言えば、php|architect’s C7YやZendのPHP Abstractなどが有名ですが、PHP Podcastsはこれらのaggregatorとなっているので、ここをiTunesに購読しておけば、様々なPodcastをチェックすることができます。

PHP Podcastsでは以下のようなPodcastをアグリゲートしているようです。

全て英語なのが寂しいところですが、本場の雰囲気を感じられて面白いです。(内容はちんぷんかんぷんですが:-P)

日本でも誰かやりませんか? > Podcast

PHP Podcasts

*1 買って、即カバーをつけてます。こんな話がありますが、外す気はありません:-D

PHP 配列を回すならforかforeachか

  • 2008-04-25 (金)
  • PHP
この記事の所要時間: 337

今日、社内で「PHPの配列をループで回すのにforを使うか、foreachを使うか」という話が面白かったので、メモ。

ここでいう配列はキーが数字で、0からの連番であることを想定してます。(キーが数字以外や連番で無い場合は、foreachを使います。)

例えば↓のようにDBテーブルからレコードを読み込んだ内容が入ってる場合、$listをループで回すならforとforeachのどちらを使うべきかという話です。

<?php
$list = array();
$list&#91;&#93; = array('id' => 1, 'name' => 'hoge');
$list[] = array('id' => 2, 'name' => 'foo');
$list[] = array('id' => 3, 'name' => 'bar');
?>

for文派

<?php
for ($i = 0 ; $i < count($list); $i++) {
  echo $list&#91;$i&#93;&#91;'id'&#93; . PHP_EOL;
  echo $list&#91;$i&#93;&#91;'name'&#93; . PHP_EOL;
}
?>

1. ループ内でデータが直感的に参照できる

$listが2次元配列になっているのが分かっているので、直感的にデータへ参照できる。

2. 誰でも知っている構文である

プログラムを知っていれば、誰でも分かる。(foreachに比べると)

3. 参照する変数が変わらない

まあ1.とほぼ同じですが、$listのデータを参照・操作したいので、$list変数へ参照した方が分かりやすい。

=> foreachだとループ内で参照する変数が変わるのが直感的で無い。

foreach派

<?php
foreach ($list as $id => $rec) {
  echo $id . PHP_EOL;
  echo $rec['name'] . PHP_EOL;
}
?>

1. 添え字が数字だろうが、何だろうが要素を順番に参照できる

(PHPでは全てもともと連想配列ですが)リストかハッシュかを意識する必要が無い。

さらに言うとIteratorをimplementしていれば、どのようなデータ構造でもforeachでループを回せる。

2. 参照すべき変数を限定できる

上の例でいうと、ループ内では$listを意識する必要はなく、$idもしくは$recさえ知っておけば要素へ参照できる。

またループ内では$listがどういう構造なのかも知る必要がない。

3. 要素の内容を表す変数名で参照できる

配列は$listだが、添え字を$id・要素を$recと、内容を表す変数が指定できるので、分かりやすい。

おまけ. 若干早い

以前取ったベンチでは、foreach()の方が早かった。ただ配列の要素数が数十程度では大した差にはならないのでそれほど神経質にならなくても良い。

PHPではforeachで

個人的には連想配列しか無いPHPでは、配列は単純にforeachで回すというルールで良いと思います。

私自身はforeach派なのですが、for文派の1や3のようにあくまで配列をイメージして各要素に参照したいというのを利点として挙げていたのが新鮮でした。(私は$list[$i][‘name’]と参照するのが冗長に感じていたので。)

forかforeachか、みなさんはどちらでしょう?

追記(2008/04/26):

皆さんコメントありがとうございます!確認した限りでは、全員foreach派で、for文派はいませんでした;-)。やはりPHPではforeachを使うのが一般的のようです。

以下、foreach派のコメントを。

  • rytich foreach派
  • @shimooka iteratorのように配列の要素数を意識しなくていいのが楽です>foreach
  • @lllnorikolll 私もforeachが多いです。
  • @junya バグの入る余地が少ないからforeachのほうが好み
  • @nazo 全要素に対して何かするならforeach、指定回数何かするならfor
  • @sonkm3 for よりは foreach の方が好きだな。インデックス用の変数ってうっかりしやすいから。。。
  • id:creazynet 今はほぼforeachだけですね。for→foreachを使う様になった時がjava脳→php脳に変わった瞬間だった気がします。
  • id:Kiske 普段はforeach、処理データ大きいときはwhile使ってる / while(list($key, $value) = each($array)){…}
  • id:heavenshell ほぼ foreach かなぁ。

関連エントリ

rsyncの–deleteでファイルが削除されない

  • 2008-04-24 (木)
  • unix
この記事の所要時間: 039

rsyncで以前困ったこと。

–deleteを付けて同期しているのに、同期元で削除したファイルがなぜか同期先で消えない現象が発生。

結局理由は、同期元のパスに*(アスタリスク)を付けていたのが原因だった。

ディレクトリ構成は↓な感じ。

$ find ./ 
./src/
./src/a
./des
./des/a
./des/b

bはdes/にはあるが、src/には無いので–delete付けると、消えるはず。

$ rsync -avz –delete src/* des/
$ find ./
./src/
./src/a
./des
./des/a
./des/b <--- 消えてない。。。 [/code]

アスタリスク無しで実行すればok。

$ rsync -avz –delete src/ des/
$ find ./
./src/
./src/a
./des
./des/a
<--- 消えた! [/code]

ちなみにsrc/以下のサブディレクトリについては、[*]付けてもokでした。[*]を指定したディレクトリでは、ディレクトリエントリは見ないのかな。

rsyncでディレクトリを指定する際は、末尾に[/]を付けるつけないで挙動が変わるから、安全のために[*]を付けていたのが、逆に仇となったという話でした。パラメータはキチンと把握しておこう。

CakePHP Modelに関する6つの誤解

この記事の所要時間: 327

CakePHPのModelはActiveRecordライクなDBアクセス方法を提供しており、さらにアソシエーションを設定することにより複数テーブルの値を同時に操作できるなど、DB操作に対するインターフェイスが数多くあります。

ただ「手軽にDB操作ができる」という印象が先行しているゆえ誤解を招くことがあるようです。

1. クラス名に対応したテーブルしか操作できない

Modelのクラス名とテーブルを自動でマッピングするのはフレームワークのいわば便利機能です。デフォルトでそのような動作をするだけで、容易に変更することができます。

Model#$useTableにテーブル名を指定すれば任意のテーブルを操作できます。

<?php
class Foo extends AppModel {
  public $useTable = 't_user'; // t_userテーブル
}
?>

2. DBを使わないといけない(DB操作が無いと使えない)

DB操作が強調されるので陥りがちですが、DBを使わないModelも作成できます。例えばバリデーションだけを行うModelやDB操作を行わないビジネスロジックを実装したModelなどももちろんokです(というかビジネスロジックを実装するもの)。

DB操作を行わないModelでは、Model#$useTableにfalseを入れておけば良いです。(nullではなく、falseです)

<?php
class Foo extends AppModel {
  public $useTable = false; // テーブルに対応させない
}
?>

3. ステートレスである

find()/findAll()等ではarrayを返すので、何となくステートレスな感じがしたりしますが、実はModelではしっかり情報を保持しています。

メソッド終了時にリセットされる値も一部ありますが、Model#set()などで設定した値はそのまま保持しています。ループの中で複数レコードを登録する際は注意する必要があります。

保持している値についてはModel#create()を呼ぶことにより初期化(クリア)できます。

<?php
    foreach ($list as $data) {
      $this->Foo->create();  // クリアしておく
      $this->Foo->save($data);
    }
?>

4. bakeで生成したModelは変更しない(できない)

bakeでバリデーションやアソシエーションを設定しておくと、生成されるModelではインスタンス変数に様々な値が記述されます。

単純なDB操作のみならばこのままModelのソースを触ることなく開発できますが、複雑なDB操作やビジネスロジックなどを実装する場合は、臆することなくModelにガンガン追加していきましょう。

5. DBのリレーションを全てアソシエーションでも設定する

アソシエーションはとても便利なものですが、上手く利用しないと思わぬSQLが発行され、パフォーマンスに影響が出ます。テーブル同士に外部整合性制約を付けてリレーションしているからと言って、必ずアソシエーションを設定する必要はありません。

はじめからシステムで不要と思われるアソシエーションについては設定しないというのも手です。

いっそ最低限のアソシエーションのみを設定しておき、後は用途に応じてModel#bind()/bindModel()する方が良いかもしれません。

特にModel#$hasManyを設定していると芋づる式に数多くのSQLが発行される場合があるので、注意しましょう。

6. DAOである

DAOのように振る舞うメソッドが多数ありますが、実際にDBへアクセスするのはDataSourceを継承したdboクラスであり、Modelではありません。

ModelをDAOとしか認識していないと、ビジネスロジックはControllerに実装することになり、Controllerが肥大していくことになります。

もちろんModelをDAOのように使うこともできますが、あくまでModelですので、ビジネスロジックはControllerでなくModelで実装して、Controllerへはそのインターフェイスを提供するようにしましょう。

CakeのModelはMVCのModel

しつこく書いてますが、CakeのModelもいわゆるMVCのModelと同じです。DB機能ばかりが強調されていますが、それに捕らわれることなく本来のModelとして活用しましょう。

CakePHP 1.2で携帯用ビューを表示する

この記事の所要時間: 332

CakePHP1.2ではCakePHP 携帯用ビューを表示するで利用していたwebservicesの機能が無くなります。

1.2-betaでRouting.webservicesをonにすると以下のようなメッセージが表示されます。

Deprecated: webservices routes are deprecated and will not be supported in future versions.  Use Router::parseExtensions() instead.

The prefix automagic in CakePHP routingで紹介されているように、1.2からはwebservicesに替わりprefixをURLルーティングで使用するようです。

そこで実際にどのように使用するかを試してみました。

1. URLルーティングでprefixを設定する

Router::connect()の第2引数ではパラメータを連想配列で設定できるのですが、そこに’prefix’というキーで値を設定しておくと、対象URLにアクセスがあればこの機能が有効となります。

下記の例では[/m/foo/bar]へアクセスがあると、prefix=mobileが有効となります。

[app/config/routes.php]

Router::connect('/m/:controller/:action', array('prefix' => 'mobile'))

2. prefix用アクションを作成する

prefixが有効な状態だと、アクションとして[prefix + _ + action]が呼ばれるので、アクションを実装します。

1. の設定で[/m/foo/bar]へアクセスされると、prefix=’mobile’が有効となるので、FooController#mobile_bar()がアクションとして呼ばれます。

[app/controllers/foo_controller.php]

<?php
class FooController extends AppController {
  public function mobile_bar() {
    // 通常のアクションを実装
  }
}
?>

3. prefix用ビューを作成する

2.で実装したアクション同様にビューテンプレートを作成します。

これは通常どおり2.で作成したアクションに対応するビューテンプレートを作成するだけです。

注意点は$html->link()などでURLを指定する際は、アクションにprefixが付いたアクション(mobile_bar)を指定するという事です。下の例なら「/m/foo/bar」がURLとして出力されます。

[app/views/foo/mobile_bar.ctp]

<?php echo $html->link('link', array('controller' => 'foo', 'action' => 'mobile_bar')); ?>

△prefix用Component/Helperが読まれない

携帯電話では入出力文字エンコーディングを変換する事が多いのですが、webservicesを使用すると読み込まれるComponent/Helperで実装していました。

今のところprefixではこの機能が無いようなので、Controller#beforeFilter()/afterFilter()等で自前で実装する必要がありそうです。

そのリクエストで設定されているprefixはController#$params[‘prefix’]で確認できます。(@see 1.2 の WEBSERVICES パラメータ)

○prefix用アクションは直接呼ばれない

prefixに設定したアクションは直接呼ぶことができず、prefixで設定したURLでないとアクセスできません。

つまり1.のような設定の場合、[/foo/mobile_bar]へアクセスしてもprivate methodとして扱われてエラーとなります。

これによりprefixが設定されず、prefixアクション(mobile_bar)が呼ばれるのを防ぎます。

このあたりの挙動はRouting.adminと同じですね。:-D

webservicesは中々便利な機能だったので無くなるのは残念ですが、代替機能があるのは嬉しいことです。

CakePHP DboSourceをPHP5らしく使う

この記事の所要時間: 044

CakePHPではDBアクセスは通常Modelを介して行うので、直接DboSourceを利用する事は無さそうですが、SQL文を自分で構築する際など、意外と使う機会があります。

DboSourceは、通常以下のよう使用します。

<?php
// $hogeをエスケープ
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$db->value($hoge);
?>

もちろんこれでも問題無いのですが、やや冗長な感じもあります。そこでPHP5で以下のように書いてみました。

<?php
ConnectionManager::getDataSource($this->useDbConfig)->value($hoge);
?>

1文にまとまりました。ただ、まだ冗長なので、これをAppModelでまとめてみます。

<?php
class AppModel extends Model {
  public function getDbo() {
    return ConnectionManager::getDataSource($this->useDbConfig);
  }
}
?>

使う際はこうなります。

<?php
// Modelなら
$this->getDbo()->value($hoge);

// Controllerなら
$this->Foo->getDbo()->value($hoge);
?>

スッキリした書き方になりました;-)

そろそろPHP5も普及しつつあるので、CakePHPももっとPHP5らしい書き方に移行したいな、という話でした。

CakePHP FormHelper#datetime()で年月日表示を変える

この記事の所要時間: 116

CakePHP1.2で追加されたFormHelperには日時をプルダウンメニューで選択する機能があります。

<?php echo $form->datetime('hoge', 'YMD', 'NONE'); ?>

これで年月日を選択できるのですが、デフォルトだと↓のように月が英語表現で表示されます。

そこで月を数字表現に変更する方法です。

datetimeメソッドはFormHelper#$optionsの値をプルダウンで表示するリストに使用します。(値が無ければデフォルト値を使用)よってこれを書き換えれば任意の値をリストに表示できます。

<?php
// 月を数字へ
$form->options['month'] = array();
for ($i = 1 ; $i <= 12 ; $i++) {
  $form->options['month'][$i] = sprintf("%02d", $i);  
}
?>
<?php echo $form->datetime('hoge', 'YMD', 'NONE'); ?>

これで以下のようになります。

FormHelper#$optionsでは月の他に様々なリストを指定できるので、年を和暦表現する、任意の日のみ選択対象にするなど変更できます。

■指定できるリスト

$options[‘year’]
$options[‘month’]
$options[‘day’]
$options[‘hour’] 時(12時間表現)
$options[‘hour24’] 時(24時間表現)
$options[‘minute’]
$options[‘second’]
$options[‘meridian’] 午前・午後

日時表現としては、FormHelper#time()、FormHelper#input()もありますが、これらも内部的には同じロジックを共有しているので、同じように指定ができます。

Home > アーカイブ > 2008-04

検索
フィード
メタ情報

Return to page top