5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CakePHP 1.2アプリケーションを1.3へマイグレーションした話

Posted at

CakePHP 1.2でできたアプリケーションをを1.3へマイグレーションした話を書きます。

えー、3じゃないのーという話なのですが、大人には事情(お金)がある(ない)のです。 :sweat:

なにかお役に立てる話が書けたらいいなと思ってますが、たぶんほとんど自分のアプリに特化した内容になりそうです。 コーヒー片手に薄目で見てやってください。 :tea:

参考情報:
1.2から1.3への移行ガイド
http://book.cakephp.org/1.3/ja/The-Manual/Appendices/Migrating-from-CakePHP-1-2-to-1-3.html

:cake: さくっとcakeフォルダを入れ替え

まずは既存アプリからcakeフォルダを消して、CakePHP 1.3のcakeフォルダをコピーしました。

:cake: index.phpファイルをコピー

CakePHP 1.3のapp/webroot/index.phpファイルを既存アプリへコピーしました。

自前で変更している箇所があるので、それも反映しました。

2か所です。

index.php
    if (!defined('ROOT')) {
        define('ROOT', dirname(dirname(__FILE__)));
    }

    if (!defined('APP_DIR')) {
        define('APP_DIR', 'app');
    }

:cake: app/config/core.phpファイルを編集

logという設定とSession.modelという設定が足りてないので付け足しました。

app/config/core.php
    Configure::write('log', true);

    Configure::write('Session.model', 'Session');

:cake: log4phpを使うには……。

CakePHP付属のCakeLogクラスを自前のものに置き換えて、log4phpを呼ぶようにしていました。

自前のCakeLogクラスは、app/cake_log.phpファイルに置き、app/config/core.phpファイルの中からrequire_onceで読み込んでいました。

app/cake_log.phpの中でApp::importでlog4phpを読み込んでいましたが、ここがなぜか動きませんでした。

CakePHP 1.3ではログエンジンを入れ替えられるらしいので、とりあえずコメントアウトして後回しにしました。 :rabbit: :rabbit: :rabbit:

ぼちぼちエラーメッセージが出ますが、とりあえずはこれで画面が見えるようになりました。 :wink:

:cake: Smarty Viewをアップグレード

CakePHP 1.3対応版Smarty Viewを https://github.com/tsak/glitch-auto-sell/blob/master/app/views/smarty.php からダウンロードして置き換えます。

Smarty 2.6の場合(?)、assignByRefが無いのでassign_by_refを使うようにします。書き換え箇所は2つあります。

app/views/smarty.php
$this->Smarty->assign_by_ref($camelBackedHelper, ${$camelBackedHelper});
//$this->Smarty->assignByRef($camelBackedHelper, ${$camelBackedHelper});

/* (snip) */

$this->Smarty->assign_by_ref('view', $this);
//$this->Smarty->assignByRef('view', $this);

viewの下にsmartyディレクトリを作っていましたので、$this->subDir = 'smarty'.DS; としました。

app/views/smarty.php
$this->subDir = 'smarty'.DS;

この辺りはもともとコメントアウトしてあるコードをイキにするだけです。親切ぅ~ :smile:

:cake: Sessionヘルパーを読み込み

SessionコンポーネントとSessionヘルパーは自動的に読み込まれなくなりました。

Sessionヘルパーは自動で読み込まれなくなったそうで、AppController$helpersに追加しろとのことです。

app/app_controller.php
  var $helpers = array('Html', 'Form', 'Javascript', 'Session');

:cake: Htmlヘルパー使用箇所の修正

HTMLエスケープするかどうかを指定する専用の引数があったのですが、なくなってしまいました。

HtmlHelper::link()$escapeTitle引数は削除されました。代わりに$options['escape']を使用してください。

:cake: EmailComponentを継承したクラスを変更

EmailComponentを継承してmb_send_mailを使うクラスを作っていました。

app/controllers/components/mb_email.php
class MbEmailComponent extends EmailComponent {
  var $delivery = 'mb_mail';

  function __wrap($message) { /* ... */ }
  function __mb_mail() { /* ... */ }
  function __wordwrap($str, $width = 75, $break = "\n", $cut = false) { /* ... */ }
}

EmailComponent内にあるメソッドの頭のアンダースコア2つが1つになったので、それぞれメソッド名を変更しました。
__wrap_wrap__mb_mail_mb_mail

たぶんprivate扱いだから、ドキュメントにも書いてない話です。そんなもんオーバーライドしてるのが悪いんです。はい。

:cake: Modelの__existsプロパティがなくなった

これも多分またprivate扱いのやつです。

次のように書き換えました。

$this->exists()

:cake: コミット漏れを修正

CakePHP 1.2のころはトランザクションを開始してコミットしなくても、データベースが更新されていました。

こんな感じにトランザクション開始するコードだけがあり、コミットするコードがないという元バグなのですが、1.2では問題なかったのです。 :sweat_smile:

$db =& ConnectionManager::getDataSource($this->useDbConfig);
$db->begin($this);

1.3ではちゃんとコミット$db->commit($this)しないといけないです。

あるいはsaveメソッドのオプションにatomicを渡すほうがよさそうです。

$myModel->save($myData, array('atomic' => true));

:cake: Model::bindModel()したアソシエーションをModel::saveAll()で保存できない :interrobang:

CakePHPでモデル間のアソシエーションを設定するとき、モデルクラスに直接プロパティを設定すると、毎回DBから読み込んでくるので困ってしまいます。

そんなわけで、ふつうはModel::bindModel()で動的にアソシエーションを設定しますよね?よね?ね?

1.3では、Model::saveAll()の中でModel::resetAssociations()を呼んでいるため、bindModelしたアソシエーションが消えてしまいます。

従って、アソシエーションしたモデルを保存できないという。。。 :astonished:

仕方がないのでbindModelするときにリセットしないようにしました。第2引数にfalseを渡します。

$myModel->bindModel($associatin, false);

:cake: Helper::valueの挙動が変わった

これまたアンドキュメンテッドな話です。

1.2までは次のようなコードが動いていました。

$formHelper->value('Model.field.time'); /* Model.fieldの値が返ります */

1.3では、こういう書き方ではModel.fieldの値が返りません。

幸いなことにSmartyプラグインの中で使っていたので、プラグイン側を書き換えて解決しました。 :rabbit:

:cake: Log4phpを使う

いよいよLog4phpを使えるようにしなくちゃいけなくなりました。

CakeLogの中から使えるようにラッパクラスを作ります。

app/libs/log/Log4phpWrapper.php
<?php
/** 
 * Small log4php wrapper for CakePHP 1.3
 *
 * @package app
 * @subpackage app.libs.log
 * @version $Revision$
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
if (!defined('LOG_FATAL')) {
    define('LOG_FATAL', 1);
}

App::import(array('type' => 'Vendor',
                  'name' => 'log4php',
                  'file' => 'log4php'.DS.'Logger.php',
                  ));

class Log4phpWrapper {
    var $methodMap =
      array(
            LOG_DEBUG =>   'debug',
			LOG_INFO =>    'info',
			LOG_WARNING => 'warn',
			LOG_ERR =>     'error',
			LOG_ERROR =>   'error',
            LOG_FATAL =>   'fatal',
            );

    /** 
     * Constructor
     * 
     */
    public function __construct($options = array()) {
        Logger::configure(CONFIGS.'log4php.php');
    }

    /** 
     * Write log messsage
     * 
     * @param string $type
     * @param string $message
     */
    public function write($type, $message) {
        if (is_int($type) && isset($this->methodMap[$type])) {
            $type = $methodMap[$type];
        }

        if ($type === 'warning') {
            $type = 'warn';
        }

        /* when unknown method provided, fallback to 'info' */
        if (array_search($type, $this->methodMap) === false) {
            $type = 'info';
        }

        if (strpos($message, '|') !== false) {
            list($name, $message) = explode('|', $message, 2);
        }
        else {
            $backtrace = debug_backtrace(false);
            $caller = count($backtrace) > 3
              ? $backtrace[3] : $backtrace[0];
            $name = isset($caller['class'])
              ? $caller['class'] : $caller['function'];
        }
            
        $logger = Logger::getLogger($name);

        $logger->{$type}($message);
    }
}

作成したクラスを使うようにbootstrap.phpのなかで指示します(app/config/core.phpの中でこれをやろうとして、クラスが読み込まれなくて悩みました)。

app/config/bootstrap.php
CakeLog::config('log4php', array('engine' => 'Log4phpWrapper'));

log4php自体の設定はPHP方式にしています。

app/tmp/logsの下にログが落ちるようにしています。

app/config/log4php.php
return array(
    'rootLogger' => array(
        'appenders' => array('default'),
    ),
    'appenders' => array(
        'default' => array(
            'class' => 'LoggerAppenderDailyFile',
            'layout' => array(
                'class' => 'LoggerLayoutSimple'
            ),
            'params' => array(
            	'file' => LOGS.'%s.log'
            )
        )
    )
);

known issue :disappointed:

LoggerLayoutPatternで、呼び出し元を記録するとLog4phpWrapperが記録される。

ほんとは$this->logを呼び出したところが記録されて欲しい。

これを実現するにはlog4phpのLoggerLoggingEventクラスを変更すればよいのだけれど、dirty hackでやだなぁと悩み中。 :unamused:

:cake: おしまい

CakePHP 1.2アプリを1.3へマイグレートしたときに詰まった話を書きました。

また何かあれば追記します。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?