Memo/Akelos

http://dexlab.net/pukiwiki/index.php?Memo%2FAkelos
 

  • RubyもRailsも知らないPHPerな私用メモ
  • 間違いや、Railsではこうやる、もっと別の方法があればご指摘下さい。

Akelos PHP Framework

Ruby on Railsの完全コピーを目指しているPHPフレームワーク。


サンプル


初心者メモ

ファイルレイアウト

root
|-- app                                アプリケーション固有処理
|   |-- apis
|   |-- controllers                    コントローラ
|   |   |-- page                       デフォルトコントローラ
|   |-- filters
|   |-- helpers                        viewで使うヘルパ
|   |-- installers                     マイグレーションファイル(環境構築)
|   |   `-- versions
|   |-- locales                        言語ファイル(画面の出力メッセージ等)
|   |   |-- helpers
|   |   |   `-- active_record          ActiveRecordを使ったエラーメッセージ等
|   |-- models                         モデル
|   |-- vendor
|   |   |-- behaviours
|   |   `-- plugins
|   `-- views                          ビュー
|       |-- layouts                    レイアウト(共通して使う画面デザイン)
|       |-- page                       デフォルトビュー
|-- config                             設定ファイル
|   |-- environments                   開発、本番、テスト環境に分けた設定ファイル
|   `-- locales
|-- docs
|   `-- images
|-- log                                ログ
|-- public                             ドキュメントルート
|   |-- images                         デフォルト画像
|   |-- javascripts                    デフォルトJavaScript
|   `-- stylesheets                    デフォルトCSS
|-- script                             Akelosフレームワークのコマンド群
|-- test                               ユニットテストファイル
`-- tmp                                テンポラリ

型変換を忘れずに

integer型に代入する際に、ゼロが入らずにnullになる時がある。フォーム等で0を入力した場合、文字列型になるので明示的に数値型に直す必要がある。

OK : 0
NG : '0'

よって、モデルのvalidate()、beforeCreate()、beforeUpdate()等で、intval()等を使って型変換してやる。

ActiveRecord?のトランザクション

DBを直接扱う場合とメソッドが違う。

$model = new ModelName(); // ActiveRecordを継承したモデル
$model->transactionStart(); // begin
$model->update(...); // 処理
$model->transactionFail(); // rollback
echo $model->transactionHasFailed(); // rollbackしたら1
$model->transactionComplete(); // commit

DBを直接扱う

  • インスタンスの取得
    $db = &Ak::db();
  • adodbオブジェクト
    $db->connection
  • 一行づつ取得 ($resはadodbなので、adodbの関数が使える)
    $db = &Ak::db();
    $res = $db->execute('select * from emp');
    while ( $row = $res->FetchRow() ) {
        print_r($row);
    }
  • 結果を一気に取得
    $result = $db->selectAll("select * from books");
  • 値だけ取得
    $result = $db->selectValues("select id from books"); // $result = array('1','2',...)
  • 現在の設定
    var_export($db->settings);
    array (
      'type' => 'pgsql',
      'database_file' => '',
      'host' => 'localhost',
      'port' => '',
      'database_name' => 'booklink_dev',
      'user' => 'postgres',
      'password' => '',
      'options' => '',
    )
  • トランザクション関係
    $db->startTransaction(); // begin
    if( @$db->execute('delete from books') ){
      $db->stopTransaction(); // commit
    }else{
      $db->failTransaction(); // rollback
    }
    echo $db->hasTransactionFailed(); // エラーなら1
  • エラー番号とメッセージ
    echo $db->connection->ErrorNo();
    echo $db->connection->ErrorMsg();
  • AkDbAdapter?の使用可能なメソッド。
    $db = &Ak::db();
    var_export(get_class_methods($db));
  • 結果
    array (
      0 => 'type',
      1 => 'renameColumn',
      2 => 'availableTables',
      3 => 'quote_string',
      4 => '__construct',
      5 => '__destruct',
      6 => 'connect',
      7 => 'connected',
      8 => 'getInstance',
      9 => '_hash',
      10 => 'getDictionary',
      11 => '_constructDsn',
      12 => '_getDbSettingsFromDsn',
      13 => 'debug',
      14 => '_log',
      15 => 'addLimitAndOffset',
      16 => 'execute',
      17 => 'incrementsPrimaryKeyAutomatically',
      18 => 'getLastInsertedId',
      19 => 'getAffectedRows',
      20 => 'insert',
      21 => 'update',
      22 => 'delete',
      23 => 'selectValue',
      24 => 'selectValues',
      25 => 'selectOne',
      26 => 'selectAll',
      27 => 'select',
      28 => 'startTransaction',
      29 => 'stopTransaction',
      30 => 'failTransaction',
      31 => 'hasTransactionFailed',
      32 => 'getColumnDetails',
      33 => 'getIndexes',
      34 => 'quote_datetime',
      35 => 'quote_date',
      36 => 'escape_blob',
      37 => 'unescape_blob',
      38 => 'AkObject',
      39 => 'toString',
      40 => '__toString',
      41 => '__clone',
      42 => 'log',
      43 => 'freeMemory',
    )
    

コントローラから使える変数

  • セッション
    $this->session['hoge']
  • パラメータ
    $this->params['hoge']
  • モデル(大文字、小文字両オブジェクトあり)
    $this->モデル名->hoge()

デフォルトテンプレートの変更

通常はアクション名を同じテンプレート(アクション名.tpl)が呼ばれるが、任意に指定したい時。

  • controller
    $this->setDefaultTemplateName('hoge.tpl');

renderPartial

  • views/現在のコントローラ/_hoge.tpl を呼び出す
    <?php  echo   $controller->renderPartial('hoge') ?>
  • views/layouts/_hoge.tpl を呼び出す
    <?php  echo   $controller->renderPartial('layouts/hoge') ?>

検索とページャ

  • view
    <input id="search_text" name="search_text" type="text" size="64" value="{session-search_text}" />
    <input type="submit" value="検索">
  • controller
    function listing()
    {
    	if($this->Request->isPost()){
    		$this->session['search_text'] = @$this->params['search_text'];
    	}
    	$conditions = "";
        if($this->session['search_text'] != ''){
        	$conditions = sprintf("name like %s", Ak::db()->quote_string('%'.$this->session['search_text'].'%'));
        }
        $this->book_pages = $this->pagination_helper->getPaginator($this->Book, array('items_per_page' => 30, 'count_conditions' => $conditions));
        $options = $this->pagination_helper->getFindOptions($this->Book);
        $options['conditions'] = $conditions;
        $this->books =& $this->Book->find('all', $options);
    }
    

json(日本語可)

  • $json = Ak::toJson($data);
    $data = array("ああ"=>"いい","うう"=>"ええ");
    $json = Ak::toJson($data); // {"\u3042\u3042":"\u3044\u3044","\u3046\u3046":"\u3048\u3048"}
  • Ak::fromJson($json)
    class stdClass {
     var $ああ = 'いい';
     var $うう = 'ええ';
    }

xml (キーが日本語だとAk::xml_to_array()で空配列になる)

$data = array('key1' => 'var1', 'key2'=>'var2');
$xml = Ak::array_to_xml($data)
Ak::xml_to_array($xml)

公開ディレクトリのファイル指定

public/images
public/javascripts
public/stylesheets
  • スタイルシート(viewで指定)
    <?php  echo  $asset_tag_helper->stylesheet_link_tag('scaffold') ?>
  • JavaScript?(viewで指定)
    <?php  echo  $asset_tag_helper->javascript_include_tag('prototype') ?>
    • 'defaults' で指定すると以下のファイルがviewで使える
      prototype.js
      event_selectors.js
      scriptaculous.js

view でパラメータの取得

  • $_GET, $_POST
    <?=$params['hoge'];?>
  • $_SESSION
    <?=$session['hoge'];?>

viewからはコントローラのメンバ変数は直接アクセスできる

  • Controller
    class TestController extends ApplicationController{
      var $hoge = "hoge";
    }
  • test.tpl
    {hoge}

index() -> foo()へのルーティング

class HogeController
{
	function index()
	{
		// foo()メソッドが実行され、foo.tplが呼び出される。
               // 'controller'=>'hoge' も追加できる
		$this->redirectTo(array('action' => 'foo'));
	}
	
	function foo()
	{
	}
}

flash, flash_now

  • $this->flash : 次のリクエストにならないとView側で取れません。
  • $this->flash_now : リクエストの遷移が無いがView側で使いたい場合

TOPからのURL (viewで扱う場合は、helper経由)

echo AK_ASSET_URL_PREFIX;
  => /hoge 

svnで無視すべきファイル

$ svn propedit svn:ignore project_name/log
----
*.log
----

$ svn propedit svn:ignore project_name/tmp
----
*
----

外部から呼び出されたくないコントローラのメソッドは、頭に「_(アンダースコア)」を付ける。

_hoge(){}

設定ファイル

config
|-- boot.php              初回処理
|-- config.php            共通設定
|-- environments
|   |-- development.php   開発時設定
|   |-- production.php    本番時設定
|   `-- testing.php       テスト時設定
|-- locales               多言語
|   `-- ja.php
`-- routes.php            URLルーティング

console, generate, migrate, pluginコマンドで、config.phpで設定した環境が反映されない。

常に'development'で動作する模様。フレームワーク側の不具合。config.phpを読んだ後に、定義するように変更してやる。
「console, generate, migrate, plugin」に対して編集する。

Index: script/console
===================================================================
--- script/console      (リビジョン 79)
+++ script/console      (リビジョン 211)
@@ -5,8 +5,8 @@
 
 define('AK_CONSOLE_MODE', true);
 define('AK_ENABLE_AKELOS_ARGS', true); 
+include(dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
 defined('AK_ENVIRONMENT') ? null : define('AK_ENVIRONMENT', 'development');
-include(dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
 require_once(AK_LIB_DIR.DS.'utils'.DS.'scripts'.DS.'console.php');
 

date_selectでデフォルト値を設定できるようにする。

デフォルト値を設定するには、コントローラのメンバ変数に同じ名称のオブジェクトが必要なようだ。オプションでの設定ができない。

  • view
    $date_helper->date_select("post", "written_on");
  • controller
    var $post = array('written_on'=>'2008-1-1');
  • オプションからデフォルト値を設定する場合、フレームワーク側を修正
    $date_helper->date_select('hoge', 'start_on', array('selected'=>'2008-01-02'))?>
    • akelos/lib/AkActionView?/helpers/date_helper.php
              }elseif(!empty($this->_object[$object_name])){
                  $date = $this->_object[$object_name]->get($column_name);
      +        }else if( isset($options['selected']) ){
      +        	$date = $options['selected'];
              }

フォームの値にゼロを入れるを空欄になる

  • ゼロを入力して更新は可能
  • 再度編集しようとすると空欄になっている
  • フレームワーク側の不具合
    --- tag_helper.php	(リビジョン 117)
    +++ tag_helper.php	(リビジョン 118)
    @@ -102,7 +102,7 @@
         {
             $formated_options = array();
             foreach ($options as $key=>$value){
    -            if(empty($value) && !is_string($value)){
    +            if($value === '' && !is_string($value)){
                     continue;
                 }
                 if(!is_numeric($key) && !is_array($value) && !is_object($value)){
--- lib/AkActionView/helpers/form_helper.php    (リビジョン 86)
+++ lib/AkActionView/helpers/form_helper.php    (作業コピー)
@@ -335,7 +335,7 @@
         }
         $options['type'] = $field_type;
         if($field_type != 'file'){
-            $options['value'] = !empty($options['value']) ? $options['value'] : $this->value_before_type_cast();
+            $options['value'] = isset($options['value']) ? $options['value'] : $this->value_before_type_cast();
         }
         $this->add_default_name_and_id($options);
         return TagHelper::tag('input', $options);


migrateで任意の関数を実行

editamのソースより

  • 用意するクラス ./app/installers/database_installer.php
    class DatabaseInstaller extends AkInstaller
    {
        function installDataFiles()
        {
        	echo "hoge";
        }
    }
  • 実行
    $ ./script/migrate database installDataFiles
    hoge

NOTICE, WARNING, ERRORログが出力されない

ソースを読むと、AK_LOG_{エラーモード}で、AK_MODE_FILE(0x04)ビットが立っていないと有効にならない。
しかし、Ak.phpで「define('AK_LOG_NOTICE', 0);」になっているため、3つのエラーモードが出力されないようだ。
よって、コメントアウトすれば出力されるようになる。
他の箇所でも特に使っていないようだし、不具合なのか意図があるのか不明だ。

$logger = Ak::getLogger();
$logger->debug('...');		// OK
$logger->info('...');		// OK
$logger->message('...');	// OK
$logger->notice('...');	// NG
$logger->warning('...');	// NG
$logger->error('...');		// NG
$logger->critical('...');	// OK
  • lib/AkLogger?.php
    $this->mode = defined('AK_LOG_'.$error_mode) ? constant('AK_LOG_'.$error_mode) : $this->default_log_settings;
    
  • lib/Ak.php
    //define('AK_LOG_NOTICE', 0);
    //define('AK_LOG_WARNING', 1);
    //define('AK_LOG_ERROR', 2);

PHP5.2.0未満+PostgreSQLでデータが更新されない

PHP5.2.0でpg_escape_string()の引数が一つ追加されたが、Akelosはマイナーバージョンは見てないので、5.2.0未満でも新しい関数を使おうとして失敗している模様。

  • 問題発生環境
    • CentOS 4.5
    • PHP 5.1.6
    • PostgreSQL 8.1.11
    • Akelos r660
  • エラー内容
    # tail -f /var/log/httpd/error_log
    [Mon Jul 07 13:45:21 2008] [error] [client 192.168.0.154] PHP Warning:  pg_escape_string() expects exactly 1 parameter, 2 given in /var/www/html/framework/akelos/lib/AkActiveRecord/AkDbAdapters/AkPgsqlDbAdapter.php on line 51, referer: http://192.168.0.185/framework/booklink/book/add/
  • AK_PHP5は lib/constants.php で定義されている。
  • 詳細なバージョンを比較するように変更
    Index: lib/AkActiveRecord/AkDbAdapters/AkPgsqlDbAdapter.php
    ===================================================================
    --- lib/AkActiveRecord/AkDbAdapters/AkPgsqlDbAdapter.php        (リビジョン 764)
    +++ lib/AkActiveRecord/AkDbAdapters/AkPgsqlDbAdapter.php        (作業コピー)
    @@ -47,7 +47,7 @@
         
         function quote_string($value)
         {
    -        if (AK_PHP5) {
    +        if ( version_compare(PHP_VERSION, '5.2.0', '>=') ) {
                 return "'".pg_escape_string($this->connection->_connectionID,$value)."'";
             } else
                 return "'".pg_escape_string($value)."'";

その他不具合


資料/リンク

Akelos製

  • editam Akelos 開発者が開発(途中)しているCMS

その他

  • PHP Framework Fight!
    • PHP Framework Fight! は、数多く存在するPHPのフレームワークに、 同じアプリケーションを実装させ、その性能や実装効率を比較しようという企画です。
    • 実装仕様はphwittr。Twitterのようなミニブログ
    • 既にいくつかのフレームワークでのソースがリポジトリに上がっています。

コメント

コメントはありません。 コメント/Memo/Akelos?

お名前: 「かくにん」を漢字で入力して下さい。1文字目が「たしかめる」で2文字目が「みとめる」です。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-09-03 (水) 09:01:29 (3581d)