続Zend_Mailで日本語本文が文字化け

以前https://life.co-hey.com/2008/09/zend-mail-6.htmlという記事を書きましたが、的外れだった可能性がでてきました。

メールヘッダーがbase64になっていたのに、printableで送信していたため、
この対処を入れて様子を見ていたのですが、まだ文字化けする場合がありました。

Mac OS9で、Outlook Expressをの場合に文字化けする現象がでました。
いろいろ試したところ、以下の対処で文字化けしなくなりました。

$mail = new Zend_Mail(‘ISO-2022-JP’);
$mail->setBodyText($text):

上記のように、base64の指定をしない形に戻す。

次に、メール本文の最後に改行を入れる。

これでMac OS9 Outlook Expressでも文字化けしなくなりました。
誤った情報を書いてしまったことをお詫び致します。

Zend_Mailで日本語本文が文字化け

今回、メーラーによっては、日本語の本文が文字化けするという現象を確認。

調査したところ、通常、本文はPrintableでエンコードされて送信されていました。
なので、ここをbase64にてエンコードするように修正して解決。

$mail = new Zend_Mail(‘ISO-2022-JP’);

$mail->setBodyText($text):

と書いていた部分を以下のようにエンコード形式を指定して設定します。

$mail = new Zend_Mail(‘ISO-2022-JP’);

$mail->setBodyText($text, null, Zend_Mime::ENCODING_BASE64);

文字化けってほんとやっかいです。

findManyToManyRowsetの取得データ

findManyToManyRowsetの説明は、こちらを読んでください。

つまり、例をあげると、

アカウント【Accounts】、ブログ記事【Articles】、コメント【Comments】
というテーブルクラスがあり、$account(Zend_Db_Table_Row)があった場合、
$account->findCommentsViaArticles();
とやると、コメント情報(Zend_Db_Table_Rowset)が取得できます。
リンク先のマニュアルに書いてあるのはここまでです。

今日、このメソッドを使っていて発見したこと。
このメソッドで取得される情報には、中間テーブル(今回はArticles)の
データも含まれていることです。Commentsの情報しかないと思っていたので意外。

データの更新は、Commentsのカラムしかできませんでした。
中間テーブルのデータは参照だけできます。更新しようとするとエラーになります。

どういうときに便利かはわかりませんが、忘れないようにメモ。

Zendのディレクトリ構成

前回の記事で以下のようなディレクトリ構成でやってますって書きました。

web/index.php
lib/models/xxxxModel.php
lib/validates/xxxxValidate.php
lib/dbs/xxxxxDb.php
lib/controllers/xxxxxController.php

そこにコメントをいただき、調べてみたところ、
以下の構成にしたら、Zend_Loader::registerAutoload()を生かせるのかなと思った。

web/index.php
lib/models/xxxx.php
lib/models/Dao/xxxxx.php
lib/models/Validate/xxxx.php
lib/controllers/xxxxxController.php

もともとmodelってのは処理クラスの集合体があることがイメージされているのかも。
そうだとしたら、私は勘違いしてた。
もうちょっと考えてみる。

_autoloadが便利

偶然見つけたメソッド __autoload。
最近までphp4ばかり使ってたので、マジックメソッドには疎いのです。

たまたま見つけたサイトで、マジックメソッドについて読んでみて、
早速__autoloadから試してみました。結果は便利です。

私は、phpでプログラムを新規で作る場合は、Zend Frameworkを利用していますが、
だいたいこんなディレクトリ構造です

web/index.php
lib/models/xxxxModel.php
lib/validates/xxxxValidate.php
lib/dbs/xxxxxDb.php
lib/controller/xxxxxController.php

小規模なものしか作っていないので、この階層分けでなんとかなってます。

そこでindex.phpに以下のように __autoloadを追加したら、
各ファイルで、Zend_Loader::loadClassや、require_onceを
記述する必要がなくなってとても楽です。

function __autoload($className)
{
  if (preg_match("/^Zend_/", $className)){
    Zend_Loader::loadClass($className);
    return;
  }

  if (preg_match("/Model$/", $className)){
    require_once('models/' . $className . '.php');
    return;
  }

  if (preg_match("/Db$/", $className)){
    require_once('dbs/' . $className . '.php');
    return;
  }

  if (preg_match("/Validate$/", $className)){
    require_once('validates/' . $className . '.php');
    return;
  }
}

Zend_Mailで日本語メールヘッダを扱う

以前、https://life.co-hey.com/2008/03/zend-mailfrom.htmlという記事を書きました。

ヘッダーをエンコーディングする際の処理を変更しようという内容でしたが、
今回追記しておいた方がいいなということあったので追記です。

mb_encode_mimeheaderでは、mb_internal_encodingの文字コードと、
mb_encode_mimeheaderの第2パラメタで指定した文字コードが一致しないと
正常に変換されない場合があるようです。

なので、以下ように書いておいた方が無難ですね

mb_encode_mimeheader('"'.$name.'"', 'ISO-2022-JP')

  ↓

$encode = mb_internal_encoding();
mb_internal_encoding('ISO-2022-JP');
mb_encode_mimeheader('"'.$name.'"', 'ISO-2022-JP');
mb_internal_encoding($encode);

今更findParent~

Zend FrameworkのDbテーブルリレーションを使って、初めて親テーブルの
データを取得したんですが、返り値ってZend_Db_Table_Rowなんですね。

子テーブルのデータをとってくるときと一緒で、Zend_Db_Table_Rowsetの
つもりで current() を呼び出してエラーになってしまっていました。

子は複数だけど、親は1つっていう考えになってるんですかね。

ほーって思ったので、メモしておきます。

Exceptionとか

出張で、家に2週間いなかったのも手伝って、全然更新してませんでした。
その間、初めて業務関係でZend Frameworkをいじれていました。

ある程度の規模のものを作っても全然楽。開発スピードが違う。
特に、ErrorHandler、ErrorControllerを使ったエラー処理は便利。
Exceptionをthrowしておけば、エラーは拾ってくれるし、
Exception内のデータから、エラーの原因も特定できる。
何より、エラーのための処理をソースにあまり書かなくていいから、
可読性がものすごくあがる。これはかなりメンテナンスしやすい!!
Exceptionは、エラー発生ファイル、ライン数、そこまでのトレース情報までわかる。

あと、Smartyの便利さに改めて気づきました。
Smartyから、post、sessionの情報を参照できるのは使うと、手放せません。
まだやったことない方はぜひお試しください。

Zend Frameworkバージョンアップ

今まで使っていた、Zend Framework 1.0.3を、1.5.1にバージョンアップしました。

まだ細かいところは検証してませんが、最初に引っかかった問題を書いておきます。

今まで、下記の【1】のように書いていた index.php を、
【2】のように書き換えないと動かなかったです。
不便だなと感じましたが、1行の修正なので大した修正ではないです。
※ /xxx/yyy/xxx/ 配下に contrllersディレクトリが存在します

【1】

$add_path = . '/xxx/yyy/ZendFramework/library' . PATH_SEPARATOR
      . '/xxx/yyy/zzz';

ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $add_path);

require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Controller_Front');

$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('controllers')
         ->dispatch();

【2】

$add_path = . '/xxx/yyy/ZendFramework/library' . PATH_SEPARATOR
       . '/xxx/yyy/zzz';

ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $add_path);

require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Controller_Front');

$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('/xxx/yyy/zzz/controllers')
    ->dispatch();

相対パス、絶対パスでの指定では動作したのですが、
パス設定をした後に、パスを利用したディレクトリ指定ではNGでした。

fetchRowとfetchAllの違い

fetchRow、fetchAllどちらもDBから行を取得するメソッドです。

相違点は以下

fetchRow
 1. 単一の行を取得
 2. 返り値の型は、Zend_Db_Table_Row
 3. 0件取得の場合は、nullを返却

fetchAll
 1. 複数の行を取得
 2. 返り値の型は、Zend_Db_Table_Rowset
 3. 0件取得の場合は、Zend_Db_Table_Rowsetを返却

注目するところは、3です。
fetchAllは、0件でもZend_Db_Table_Rowsetを返却します。
なので、取得の有無で、nullかどうかでは判定できません。

私はどちらもnullが返ってくると勘違いしていたので、ミスりました。