顯示具有 Zend Framework 標籤的文章。 顯示所有文章
顯示具有 Zend Framework 標籤的文章。 顯示所有文章

2011年5月27日 星期五

How to return php script error message in Zend Framework with uploadify jQuery tool?

Uploadify jQuery script:

$(document).ready(
    function(){
        $("#Filedata").uploadify({
            'uploader'    : '/js/jquery/uploadify/uploadify.swf',       
            'cancelImg'   : '/js/jquery/uploadify/cancel.png',
            'queueID'     : 'queue',       
            'multi'       : true,
            'script'      : '/file.upload.do',   
            'auto'        : true,
            'fileExt'     : '*.jpg;*.gif;*.png;*.bmp',
            'fileDesc'    : 'Image Files (.JPG, .GIF, .PNG, .BMP)',
            'onError'     : function(event, ID, fileObj, errorObj){
                              alert(errorObj.type + ' Error: ' + errorObj.info);
                          },
            'onComplete'  : function(event, queueID, fileObj, response, data){
                                if(response != 'OK') alert(response);
                          }

            #response: message returns from server side script
        });
      }
);

album-photos/uploadError.phtml:
<?=message?>

uploadify's php script:
public function fileUploadDoAction(){
    $this->_helper->layout()->disableLayout();
       
    try{
        #move tmp file codes
           
        $this->view->message = 'OK';
    }catch(Exception $e){
        $this->view->message = $e->getMessage();
    }
    $this->renderScript('album-photos/uploadError.phtml');
}

2011年3月30日 星期三

Zend_Cache, Reducing Your System's Loading



#設定expired time, 及是否要用PHP的serialize函式將data serialize
$frontendOptions = array(
                         'lifetime' => 60 * 60 * 24 * 30,
                         'automatic_serialization' => true
                   );
#設定儲存路徑&資料階層數,i.e. 1 => ~/zend_cache__5/zend_cache_{$cacheName} 
0 => ~/zend_cache_{$cacheName}

$backendOptions = array(
                        'hashed_directory_level' => 1
                        'cache_dir' => realpath(APPLICATION_PATH . '/../library'));

$zendCache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);



$zendCache->save($data, $cacheName);


$zendCache->remove($cacheName);


#判斷Cache是否有值

if(($data = $zendCache->load($cacheName)) === false){
    ....
}

2011年2月25日 星期五

PHP 'usort' function with user-defined comparison function in zend framework‏

public function xxxAction(){
      ...
      usort($result, array($this, 'cmp'));
      ...
}

protected function cmp($a, $b){
     return -strcmp ($a['visit_times'],$b['visit_times']);
}

2011年2月18日 星期五

在controller利用Zend_Config_Writer_Ini‏修改application.ini

application.ini內容

[production]
languages.locales.zh_TW = "繁體中文"
languages.locales.en = "English"

每當有修改語系時,從DB Call出所有語系,與application.ini比對,若沒有則加入至ini檔。

$row = $db->select()->from('languages')->query()->fetchAll();

$application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
$options = $application->getOptions();

$config = new Zend_Config_Ini('application/configs/application.ini', null, array('skipExtends' => true, 'allowModifications' => true));

foreach($row as $key => $val){
       if(!array_key_exists($val['code'], $options['languages']['locales'])){
                $config->production->languages->locales->$val['code'] = $val['name'];
                $writer = new Zend_Config_Writer_Ini(array('config'   => $config, 'filename' => APPLICATION_PATH . '/configs/application.ini'));
       }
}
$writer->write();

2011年1月8日 星期六

Zend_Controller_Router_Route render page with url parameter by queried from db

Zend Framework裡的Zend_Controller_Router_Route裡有相當多的功能可以根據url參數render某個module, controller及action. 倘若今日希望url後的第一個參數到DB取得資料再決定導入哪個
module, controller及action,作法如下:

application.ini:

resources.db.adapter = "PDO_MySQL"
resources.db.params.host = your db server ip
resources.db.params.username = your db server account
resources.db.params.password = your db server password
resources.db.params.dbname = db name

Bootstrap.php:

$front = Zend_Controller_Front::getInstance();

$router = $front->getRouter();
$name = substr($_SERVER['REQUEST_URI'], 1);    #note 1
$number = strpos($name, '/');
if($number !== false){
  $name = substr($name, 0, $number)
}

$application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
$app = $application->getOptions();
$db = new Zend_Db_Adapter_Mysqli(
      array(
            'host'     =>    $app['resources']['db']['params']['host'],
            'username' =>    $app['resources']['db']['params']['username'],
            'password' =>    $app['resources']['db']['params']['password'],
            'dbname'   =>    $app['resources']['db']['params']['dbname']
      )

);


$sql = 'select {fields} from {table} where {field} = ?';
$stmt = new Zend_Db_Statement_Mysqli($db, $sql);
$stmt->;execute(array($name));
$row = $stmt->fetch();


$route = new Zend_Controller_Router_Route(
         ':param/*',     #note 2
         array(
              'module'     => {module you want to redirect},
              'controller' => {controller you want to redirect},
              'action'     => {action you want to redirect},
              {variable}   => {variable value}
              )
         );

$router->addRoute('number', $route);

note 1:
 $_SERVER['REQUEST_URI']會取得hostname以後之參數, e.g. http://www.blogger.com/post-edit.g?blogID=!@#?% 會回傳 '/post-edit.g?blogID=!@#?%'
若有在該參數後再加上其它參數的需求,所以才會用strpos檢查url是否有多個參數, e.g. http://www.blogger.com/pattern/type/1,所以在note 2處需要該設定方式


note 2:
':param/*'  :param即為所需決定render哪一個頁面的參數,而之後的*字號則可輸入可不輸入, e.g. /pattern, /pattern/, /pattern/type/1都會render到同一個module, controller, action內。若需要在該action內取得此param值, 使用$this->getRequest()->getParam('param');即可。

2010年11月17日 星期三

Zend_Db_Statement 直接下 SQL

過去用Zend_Db_Table物件寫SQL時,常常遇到不知該如何把一整行SQL指令拆成Zend_Db_Table的方式。現在有了Zend_Db_Statement之後,不管SQL指令再怎麼長,再怎麼join來join去都不是問題了...


$sql = 'select count(a.id) from tableA as a inner join tableB as b on a.Bid = b.id where b.member_sn = ? and a.status = ?';

$stmt = new Zend_Db_Statement_Mysqli($dbAdapter, $sql);
$stmt->execute(array($var1, $var2));
$row = $stmt->fetch();   //單筆資料用fetch(), 多筆用fetchAll()

2010年10月19日 星期二

Zend_Mail 解決E-mail中文內容亂碼問題

config.php:

return array(
  /**
   * SMTP Config
   */
   'smtp' => array(
   'host' => 'smtp.gmail.com',
   'config' => array(
   'ssl' => 'ssl',
   'port' => 465,
   'auth' => 'login',

   'username'    => 'XXX',
   'password'    => '1234'
    )
  )
);

action:

require_once 'Zend/Mail/Transport/Smtp.php';
$arr = include 'application/configs/config.php';
require_once 'Zend/Mail.php';

$authDetails = $arr['smtp']['config'];
$transport = new Zend_Mail_Transport_Smtp($arr['smtp']['host'], $authDetails);
Zend_Mail::setDefaultTransport($transport);

$mail = new Zend_Mail('utf-8');
$mail->addTo('eigrp@gmail.com', 'cisco');
$mail->setSubject("=?UTF-8?B?".base64_encode($postData['subject'])."?=");
$mail->setBodyHTML($systemMessage.$postData['content'], 'utf-8',Zend_Mime::ENCODING_BASE64);
$mail->send();

2010年10月5日 星期二

phpMyAdmin, MySQL中文亂碼, 於Zend Framework application.ini設定方式

中文亂碼問題
到application.ini中加入resources.db.params.driver_options.1002 = "SET NAMES utf8",如下:

resources.db.adapter = "PDO_MYSQL"
resources.db.params.host = "localhost"
resources.db.params.username = "root"
resources.db.params.password = "ok1234"
resources.db.params.dbname = "db_production"
resources.db.params.driver_options.1002 = "SET NAMES utf8"

or
resources.db.params.charset = "utf8"

2010年9月8日 星期三

Zend_Db_Table->Join() 指定欄位別名

如果table A和table B要join一塊,但兩者有共同名稱的欄位時你會下以下SQL:
select a.name b.name from member as a join forum as b on a.id = b.memberId where ...

那如果是用Zend_DB_Table要如何寫呢?
$sql = $adapter->select()
               ->from(array('a' => 'member'), 'a.name')
               ->join(array('b' => 'forum'), 'b.memberId = a.id', array('b.name as bName'))
               ->where('xxx = ?', $var)
               ->order('col desc')
               ->query()
               ->fetchAll()

2010年8月30日 星期一

Zend_DB_Table -> limit($count, $offset)? or -> limit($offset, $count)? It's not the same as you think.

call 某個table的第10筆至第15筆資料,你會怎麼下SQL???
--for MySQL

select a, b, c from tb limit 10, 5


--for PostgreSQL


select a, b, c from tb limit 5, 10

讓如果是用Zend Framework裡的Zend_DB_Table使用PDO又要如何下SQL語法呢?

$sql = $adapter->select()
               ->from(array('t' => 'tb'), array('a', 'b', 'c'))
               ->limit(5, 10#沒錯,PDO_MySQL使用PostgreSQL limit的用法
               ->query()
               ->fetchAll();

       今天光是找這個limit的Bug就花了我快3個小時。或許你會問,那如果我偏偏要使用MySQL limit的用法呢?ZF會讓你用嗎?
       答案是會的!不過,當你call到最後的count數時,會出現嚴重錯誤。舉例,tb裡有21筆records,若用分頁方式寫PHP,每次call 10筆,則你會需要分3次(意即3頁),當你call了第一頁、第二頁時,均不會有事(因為limit(0, 10), limit(10, 10));但當你table只剩最後不到10筆,而你確指定硬call 10筆時,你所取得的array不再是一個n*10的矩陣,而會是一個n*(10 + 最後不到10筆records)的矩陣。如果你在output array時又將count值(即10筆)寫死在PHP裡,很抱歉,你的第2頁的內容會和第3頁一模一樣。
       唯一的辦法就是使用PostgreSQL的用法。

tested by Zend Framework 1.7.5 edition

2010年8月1日 星期日

Zend Framework - Zend_Layout

過去寫網頁時,假設每個網頁的開頭及結尾部份圴是相同的,不論是CSS或HTML,常常將一個網頁分成header, wrapper及footer。這種方法固然好用,但每次新增一個網頁時,又得include header及footer。而現在使用Zend Framework的Zend_Layout之後,每當有需要修改時,僅需修改menun及所對應的content即可,如下圖:


如左圖,整個網頁就只有layout這個版面。當使用者在URL裡KEY上D.N. 後,系統即立刻執行menuAction, 將menu寫至menu區塊內;而一開始則將index.phtml(首頁)顯示在content的區域中。

當使用者點選menu裡的各項連結後,系統再依據controllerName及actionName,將指定的phtml顯示至content區域。

底下以一個簡單的範例來說明。而接著下圖則為整個專案的目錄結構:


index.php程式碼:

<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
date_default_timezone_set("Asia/Taipei");

$documentRoot = dirname(dirname(__FILE__));
define('DOCUMENTROOT', $documentRoot);
set_include_path(get_include_path()
    .PATH_SEPARATOR.DOCUMENTROOT.'/library/'
    .PATH_SEPARATOR.DOCUMENTROOT.'/application/models/'
);

//Load classes automatically.
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);

//Loads the configuration and designates the 'general' section to be loaded.
$config = new Zend_Config_Ini($documentRoot.'/application/configs/config.ini', 'general');
Zend_Registry::set('config', $config);

//Sets the MySQL PDO connection adapter.
$db = Zend_Db::factory($config->db);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
Zend_Registry::set('db', $db);
$db->query("SET NAMES 'utf8'");

$frontController = Zend_Controller_Front::getInstance();
$frontController->setControllerDirectory(DOCUMENTROOT . '/application/controllers');

$frontController->registerPlugin(new TS_Controller_Plugin_ActionSetup());
$frontController->registerPlugin(new Ts_Controller_Plugin_ViewSetup(), 98);

//Sets the layout directory.
Zend_Layout::startMvc(
    array('layoutPath' => DOCUMENTROOT.'/application/views/layouts')
);

//Runs the application.
$frontController->dispatch();

config.ini設定:

[general]
db.adapter = PDO_MYSQL
db.params.host = localhost
db.params.username = root
db.params.password = 1234
db.params.dbname = ts
date_default_timezone = Asia/Taipei

layout.phtml程式碼:

<?php echo layout()->content;?>
<?php echo layout()->menu;?>

其中layout()->content即每次依據Controller及Action決定phtml要顯示的區域,而layout->menu則為稍後IndexController的menuAction指定$mainMenu要顯示的區域。

ActionSetup.php程式碼:

<?php
class TS_Controller_Plugin_ActionSetup extends Zend_Controller_Plugin_Abstract{
    public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request){
        $front = Zend_Controller_Front::getInstance();
        if(!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')){
            $actionStack = new Zend_Controller_Plugin_ActionStack();
            $front->registerPlugin($actionStack, 97);
        }else{
            $actionStack = $front->getPlugin('Zend_Controller_Plugin_ActionStack');
        }
       
        $menuAction = clone($request);
        $menuAction->setActionName('menu')
                ->setControllerName('index');
        $actionStack->pushStack($menuAction);
    }
}

此為index.php指定執行的plugin,主要功能為使用者僅需在URL內key入DB,系統不只會執行index.php,亦會執行IndexController內的menuAction。而menuAction內則指定選單的名稱、對應的URL,並將Renderer指向layout.phtml的layout()->menu。

IndexController程式碼:

<?php
class IndexController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
       
    }
   
    public function indexAction(){
        // action body
    }

    public function menuAction(){
        // action body
        $mainMenu = array(
            array('title' => 'Home',
                'url' => $this->view->url(array(), null, true)
            ),
            array('title' => 'db',
                'url' => $this->view->url(array('controller' => 'db', 'action' => 'call'), null, true)
            )
        );
       
        $this->view->menu = $mainMenu;
        $this->_helper->viewRenderer->setResponseSegment('menu');
    }
}

接下來在IndexController的MenuAction裡的超連結$mainMenu,則會在layout樣版上的menu區塊裡顯示。(若有其它需求,則自行套CSS即可)而每個menu的Controller, Action的執行結果,均會顯示在layout->content()區域。而首頁則是會將/view/script/index/index.phtml顯示於layout->content()區。