[分享] 小弟寫的 cakephp 換頁 排序 功能 (第一版)

edited 十月 2013 in CakePHP
###############################################
# 資料來源 : bbs.ecstart.com
# 作者 : FIEND
# 轉貼請註明出處
###############################################

最近太無聊了 花了一星期玩了一下 cakephp ^^bb

果然是一套有趣的大玩具 .

雖然功能很弱看起來還沒有很成熟 .

不過蠻適合做小專案的 , create 程式的速度超快 ^^

官方並沒有出 cake php 的換頁和排序 method .

而 1.2 版有出 但好像也不怎麼好用.

放棄~~~

所以後來想想還是自己寫 比較好用..

1.2 以下的使用者 苦腦怎麼實現 換頁 排序 功能的 或是覺得官方預設的不夠強 .

可以抄小弟寫好的 回家用 下星期有空我再加上 搜尋功能

^^

cakephp 換頁 排序 (第一版) :

1. create tests table :
CREATE TABLE IF NOT EXISTS `ecstart_tests` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(50) default NULL,
  `body` text,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=35 ;
2. vim cake/app_controller.php << 你也可以將其放在 app/controllers/components :

首先將 這些寫好的 method 放進 app_controler class 內 .
        function ecstart_record_page($p , $max_page ,$max_range , $rows){

                if(!isset($p)){$p = "" ;}
                if(trim($p)=="" or $p < 1){$p = 1  ; }
                $lastpage = ceil($rows/$max_page);
                if($p > $lastpage){ $p=1 ;}
                $rec_start= ($p-1)*$max_page +1;
                $rec_end  = $rec_start + $max_page -1;
                $ploops   = floor(($p-1)/$max_range)*$max_range + 1 ;
                $ploope   = $ploops + $max_range -1;
                if($ploope >= $lastpage){ $ploope=$lastpage;}
                $ppg      = $p - 1 ;
                $npg      = $p + 1 ;
                if($ppg<= 0) $ppg=$lastpage;
                if($npg > $lastpage) $npg=1;
                if($rec_end > $rows) $rec_end=$rows;

                $ecstart_page["rec_start"]       = $rec_start ;
                $ecstart_page["rec_end"]         = $rec_end ;
                $ecstart_page["firstpage"]       = 1 ;
                $ecstart_page["lastpage"]        = $lastpage ;
                $ecstart_page["previousrange"]   = $ploops - $max_range ;
                $ecstart_page["nextrange"]       = $ploops + $max_range ;
                $ecstart_page["previouspage"]    = $ppg ;
                $ecstart_page["nextpage"]        = $npg ;
                $ecstart_page["thispage"]        = $p ;
                $ecstart_page["total"]           = $rows ;
                $ecstart_page["loop"]            = $lastpage+1 ;
                for($i=$ploops;$i <= $ploope;$i++){
                        $ecstart_page["item"][]["p"] = $i ;
                }
                return $ecstart_page;
        }


        function ecstart_show_me_the_var($ecstart){
                echo "<pre>";
                print_r($ecstart);
                exit ;
        }




        function ecstart_show_me_the_obj(){
                echo "<pre>";
                print_r($this);
                exit ;
        }



        function ecstart_record_sort($field_arr){
                //echo "<pre>"; echo $field_list; print_r($field_arr); exit;
                foreach($field_arr as $fv){

                        $fvs = explode('.',$fv);
                        if(isset($this->params["url"]["sort_".str_replace('`','',$fvs[1])])){
                                //print_r($this->params["url"]["sort_id"]);
                                $sort["subquery"]  = $fv." ".$this->params["url"]["sort_".str_replace('`','',$fvs[1])];
                                $sort["status"]["path"]["sort"] = '&sort_'.str_replace('`','',$fvs[1]).'='.$this->params["url"]["sort_".str_replace('`','',$fvs[1])]  ;
                                $sort["status"]["sort"][str_replace('`','',$fvs[1])] = $this->params["url"]["sort_".str_replace('`','',$fvs[1])];
                        }

                }
                if(isset($sort)){
                        return  $sort ;
                }
        }


        function ecstart_get_page_record($CALL,$funname){



                if(!isset($this->params["url"]["p"])){
                        $this->params["url"]["p"] = "1";
                }
                $ecstart["params"] = $this->params ;

                $fields_arr = $CALL->getColumnTypes() ;

                if(is_array($fields_arr)){
                        foreach($fields_arr as $fk => $fv){
                                $fields[$fk] = "`".$funname."`.`".$fk."`" ;
                        }
                }



                $ecstart["sort"] = $this->ecstart_record_sort($fields);
                $ecstart["record"] =
                                   $CALL->findAll(
                                                        $conditions = null,
                                                        $fields = null,
                                                        $order = $ecstart["sort"]["subquery"],
                                                        $limit = MAX_RECORD,
                                                        $page = $this->params["url"]["p"],
                                                        $recursive = null
                                   );
                $num = $CALL->findCount() ;

                $ecstart["page"] = $this->ecstart_record_page($this->params["url"]["p"],MAX_RECORD,MAX_RANGE,$num) ;

                $ecstart["status"]["funname"] = $funname ;

                if(isset($this->params["url"]["show_me_the_var"])){
                        $this->ecstart_show_me_the_var($ecstart) ;
                }

                if(isset($this->params["url"]["show_me_the_obj"])){
                        $this->ecstart_show_me_the_obj() ;
                }


                $this->set('ecstart', $ecstart);
        }
3. 呼叫 ecstart_get_page_record method :

app/controllers/test_controller.php
class TestController extends AppController {

   var $name = 'Test';

   var $helpers = array('Html','Ecstartsort');  // << 這是 排序的 helper 版型邏輯 未來要給 標題使用的 稍後再跟大家介紹 helper 相信己玩過 cakephp的人不漠生 .

   function index() {
                $this->ecstart_get_page_record($this->Test,$this->name);  // << 呼叫先前寫好的 換頁和 排序 邏輯 .
   }

   function add()
   {

        if (!empty($this->data))
        {
            if ($this->Test->save($this->data))
            {
                $this->flash('Your post has been saved.','/test');
                //$this->redirect('/test');
            }
        }

   }

   function edit($id = null)
   {
       if (empty($this->data))
       {
           $this->Test->id = $id;
           $this->data = $this->Test->read();
       }
       else
       {
           //print_r($this->data);        exit ;
           if ($this->Test->save($this->data['Test']))
           {
               $this->flash('Your post has been updated.','/test');
           }
       }
   }


   function delete($id)
   {
        $this->Test->del($id);
        $this->flash('The post with id: '.$id.' has been deleted.', '/test');
   }



}
4. SORTHELPER :

這是 要給所有版型 可以呼叫用的 helper method , 您可以參考 3. 小弟 的註解

主要的功能是使標題的排序 .

vim app/views/helpers/ecstartsort.php
<?php
class EcstartsortHelper extends Helper {

    var $helpers = array('Html');

    function fieldsort($name,$field,$url,$ecstart)
    {
        // Use the HTML helper to output
        // formatted data:
        if(@$ecstart[";sort"]["status"]["sort"][$field] == "desc"){
                $url .= "&sort_".$field."=asc" ;
        }
        else{
                $url .= "&sort_".$field."=desc" ;
        }


        if(@$ecstart[";sort"]["status"]["sort"][$field] == "desc"){
                $image_path = "<img src='/images/s_desc.png'>" ;
        }
        elseif(@$ecstart[";sort"]["status"]["sort"][$field] == "asc"){
                $image_path = "<img src='/images/s_asc.png'>" ;

        }
        else{
                $image_path = "" ;
        }
        $url .= "&p=".$ecstart["page"]["thispage"] ;
        //echo "<pre>";         print_r($ecstart["page"]["thispage"]);  exit ;
        unset($ecstart);


        return $this->output("<a href=".$url." class=\"editOuter\">".$name."</a> ".$image_path);
    }
}
?>
5. 換頁區塊 :

因為換頁的區塊一般都長一樣 , 所以小弟將換頁 做成 區塊讓所有的版型可以去呼叫它

vim app/views/elements/ecstart_record_page.thtml
<table>
<tr>
 <td>

<?php
echo $html->link('最前頁', '/test&p='.$ecstart["page"]["firstpage"].$ecstart["sort"]["status"]["path"]["sort"]);echo " | ";
echo $html->link('上一頁', '/test&p='.$ecstart["page"]["previouspage"].$ecstart["sort"]["status"]["path"]["sort"]);echo " | ";
if(is_array($ecstart["page"]["item"])){
        foreach($ecstart["page"]["item"] as $pv){
                if($ecstart["params"]["url"]["p"] == $pv['p']){
                        echo "<b>[ ".$pv['p']." ]</b>";
                        echo " " ;
                        echo " " ;
                }
                else{
                        echo "".$html->link($pv['p'], '/test&p='.$pv['p'].$ecstart["sort"]["status"]["path"]["sort"])."";
                        echo " " ;
                        echo " " ;
                }
        }
}
echo " | ";
echo $html->link('下一頁', '/test&p='.$ecstart["page"]["nextpage"].$ecstart["sort"]["status"]["path"]["sort"]);echo " | ";
echo $html->link('最終頁', '/test&p='.$ecstart["page"]["lastpage"].$ecstart["sort"]["status"]["path"]["sort"]);
?>

 </td>
</tr>
</table>
6. record 頁的 換頁 排序 版型 :
<table>
    <tr>
        <th>No</th>
        <th>
        <?= $ecstartsort->fieldsort("ID","id","/test",$ecstart);?>  <!-- 利用 helper 寫好的 展示層排序邏輯 直接呼叫 method 即可組合 連結 -->
        </th>
        <th>
        <?= $ecstartsort->fieldsort("標題","title","/test",$ecstart);?>
        </th>
        <th>
        <?= $ecstartsort->fieldsort("新增時間","created","/test",$ecstart);?>
        </th>
        <th>Update</th>
        <th>Delete</th>
    </tr>

   <!-- Here's where we loop through our $posts array, printing out post info -->

    <?PHP //echo "<pre>";       print_r($test);         exit ; ?>
    <?php foreach ($ecstart["record"] as $rid => $record): ?> <!-- // 由 test_controler 去呼叫 ecstart_get_page_record 的 record 陣列再由這裡去 做 foreach -->
    <tr>
        <td><?php echo $rid+1; ?></td>
        <td><?php echo $record[$ecstart["status"]['funname']]["id"]; ?></td>
        <td>
            <?php echo $html->link($record[$ecstart["status"]['funname']]['title'], '/test/view/'.$record[$ecstart["status"]['funname']]['id']);?>
        </td>
        <td><?php echo $record[$ecstart["status"]['funname']]['created']; ?></td>
        <td>
                <?php echo $html->link('Edit', '/test/edit/'.$record[$ecstart["status"]['funname']]['id']);?>
        </td>
        <td>
            <?php echo $html->link(
                'Delete',
                "/test/delete/{$record[$ecstart["status"]['funname']]['id']}",
                null,
                'Are you sure?'
            )?>
        </td>

    </tr>
    <?php endforeach; ?>

</table>
<?php echo $this->renderElement('ecstart_record_page'); ?>   <!-- // 這裡就是將 5. 做好的 區塊版型抓進來用   -->

<p><?php echo $html->link('Add Post', '/test/add'); ?></p>
對了這裡是完成的 程式 範例 用我家的 vmware 架的 linux 有時會連不上請多包含:

http://fiend.no-ip.biz/test/


^^ po 完了 大家慢慢消化吧 .

fiend 上

原始討論: http://twpug.net/x/modules/newbb/viewtopic.php?topic_id=3094
«1

評論

  • edited 十二月 2007
    感謝提供,我本身都是使用 1.2 版預設的元件進行,1.1 版就是以你不喜歡的那個元件處理( http://phorum.study-area.org/index.php/topic,49746.0.html:)

    我想 CakePHP 並不非常卓越的程式架構,但是在 CakePHP 社群中樂於分享的人們讓它比起一般程式架構來的平易近人。
  • edited 一月 2008
    個人見解

    也許主觀了點 , 但是 cakephp 我覺得不過是好玩的玩具.

    在 嚴厲的環境下使用 它將會是一個讓人後悔使用的垃圾 .


    而 CAKEPHP 的架構 是很漂亮的 畢竟也是抄 ruby 的架構 .

    雖然只做到三層式架構 將 :

    資料邏輯層 和 頁面邏輯層 清礎的切開運用 .

    最好玩的是 它在繼承 物件的 分配上真的很方便 .

    做到 完美的一致性和延展性的運用 .

    架構怎麼會不漂亮呢 ?

    如果要進行小專案或是後台及快速開發的專案.

    它真的是一個不錯的選擇 .

    #############################

    但是 運用在 大型 website 上 .

    final 元件的人 觀念不足 寫出來東西 看不清 問題所在 .

    導致 維護上的彈性不易.

    再則 架構面雖然很漂亮 但是 許多元件在效能上並不理想 .

    是效能和有效率的開發的取捨.


    就 1.2 而言 , 老實說 它有不少原件 放在 大型 website 上.

    會造成蠻大的 loding

    例如 :

    1.2 換頁那個 元件 , query 做了二次才能做到 換頁效果 .

    在過萬人的 site 上一定會被打槍的 .

    原則上 這篇文章的換頁功能

    我寫的也這樣設計 ^^!!

    因為我繼承了 model 的物件 來用 , 而且我只想運用在後台的開發 .

    所以小弟 覺得 cakephp 如果要浮上枱面使用 ,

    在破千人上線環境下 我是絕對不會選擇使用它的.
  • edited 一月 2008
    CakePHP 的效能確實沒有很好的表現,不過相較於 RoR 或是其他 PHP 程式架構,它應該還有中上的水平;效能表現更差的 symfony ,開發人員有提過,程式架構一般都是用來發展雛型,沒有經過調校就貿然上線,問題是出在程式設計人員,而非採用程式架構的決定。
  • edited 一月 2008
    我們拿開 共同開發 造成 CAKEPHP 有許多預設原件的設計很粗糙不談.


    如果說要拿來發展大型專案的 雛型 .

    要講架構 小弟覺得 CAKE PHP 不夠活 .

    MVC 架構 小弟覺得 只能算是因為"簡單"所以 "平易近人" .

    所以 ROR 架構才會紅 ,

    若是要做小專案或是後台 CAKEPHP 確實是不錯的選擇 .

    因為它就像初學者入門的玩具一樣 .

    但是要給 資深一點的設計師使用 .

    老實說 架構的切割上 還稍嫌 不夠細膩精緻 .

    說它是 平易近人 的 玩具 相信許多高手大大們都覺得很貼切.

    純討論 , 請多包含 .
  • edited 一月 2008
    恩~
    FIEND是指MVC架構不夠細膩還是說CakePHP這個框架不夠細膩?
  • edited 一月 2008
    SORRY 表達能力太差 .

    MVC .
  • edited 一月 2008
    對我而言,採用 MVC 架構可以讓專案的成員間更容易溝通,相較於過去單打獨鬥的開發方式,我想這已經很值得一試了。執行效能不佳、元件設計粗糙,要從負面思考總是有抱怨不完的細節,也許可以將焦點放在積極些的地方,帶著大家成長,而不是文人相輕。 :)
  • edited 一月 2008
    我並沒有全盤去否定 CAKEPHP的價值 , 只是把它的問題挑出來.

    了解 工具的好壞 因應環境的需求去變動 每個設計師都應該去了解.

    而不是一味的去崇拜 名牌而不去了解或避免觸及其缺點 .

    不採用 MVC 不代表 沒有能力共同開發 , 也不表示單打獨鬥 .


    MVC 其實只做到三層式的模組化和切割 .

    WEB 的架構 MVC 算是比較初階 好入門 的架構.

    若是採用在 一般的小型專案 確實是一個很好的選擇 .

    對剛入門的程設師來說 確實是一個可以快速一起 TEAM WORK 的工具.

    這點我並沒有否認 .

    而且現在我也為了一些未來要開發的小專案而使用CAKEPHP在編寫 開發工具.

    目地就是為了讓 CODING 人員可以快速上手 ,

    抄的盡興 早點回家陪老婆小孩吃飯.

    客觀及不避諱 工具的本質 小弟覺得才是客觀的討論.
  • edited 一月 2008
    我知道下面的回應可能會變成打嘴砲,不過既然上面有問,FIEND有回,不回應一下不好意思。

    話說我開始寫網頁程式的時候,當時還是個大學生,第一個程式是用ASP來寫的,而不久之後ASP.NET的beta版出來了,我還玩過一陣子。
    之後一直到了大三大四,要做畢業專題,才開始用PHP。
    不過在當時,我就已經知道,網頁的功能其實跟資料庫做的事情一樣:新、刪、修、查,這老四項的變化。

    研究所畢業之後,到社會上工作,有曾想過類似CakePHP的框架,不過當時的我覺得,如果有這個框架的話,寫程式會很快,但是客戶會認為寫程式是個沒有附加價值的工作,所以當時我並沒有去實做它。
    當時我只有提出這個概念,跟某個朋友提過,後來也運用這個概念做過一些東西。

    上一次FIEND舉辦的網聚,我跟我那位朋友也有去,當時FIEND拿出來的東西,其概念跟我當初提得很類似,會後我們兩個都覺得蠻巧合的。

    廢話一堆只是為了要鋪陳後面要講的幾件事情:
    1. FIEND認為MVC是三層,我的看法則是在同一層的三個「獨立」的區塊,實做上有些作法會相互混用或是承先啟後,不過那種作法只是「存在事實」並不能作為MVC視為「層」的概念
    2. 在多層架構下,每一層都有各自的MVC,FIEND認為不夠細膩可能是因為他覺得多層架構下仍維持一組MVC的緣故
    3. 整個網際網路是「網」而非「層」的概念,因此MVC常常在實做上被打破,這是寫程式的權變,不能依此對MVC做適不適合的評論
  • edited 一月 2008
    也許真正客觀的論點是找到比較對象,花些時間產生測試數據;只是許多朋友都騰不出時間進行,或是沒有如此深入的興趣,導致許多深入的議題大都停留在個人意見的陳述。

    效能的部份,我的想法是,在不影響專案執行進度的前提下,或許有些可以不更動程式架構的解決方案可以嘗試,如果專案帶來的效益可以在效能逼近無法可解的瓶頸前出現,屆時可以挹注更多的資源,即使重新來過也要比專案執行初期為了效能綁手綁腳來的舒坦些。有時候,這是個不得不為的賭注,你無法期待公司找來的每個人都能夠跟你有一致的水平,或是強過於你;這時候,一個能夠讓所有人各司所長的架構,比起拿嚴厲標準鞭策那些趕不上的成員來的好一些。

    當然,也許 FIEND 幸運地待在能夠僱用許多一流人才的公司,看到的角度自然不同;我並沒有避談 CakePHP 的缺點,只是它的優點似乎比起缺點更值得深入探討。
  • edited 一月 2008
    FIEND 寫道:

    而 CAKEPHP 的架構 是很漂亮的 畢竟也是抄 ruby 的架構 .
    雖然只做到三層式架構 將 :
    資料邏輯層 和 頁面邏輯層 清礎的切開運用 .
    這說法挺奇怪的,基本上 "三層式" 只是概念性的說法,以有別於主從式架構明顯的二層結構,並不是 "只有三層"。所以後來更常見的說法是 n-Tier 或 "分散式架構"。"層數"並不是愈多愈好,因此它"雖然"只有三層,並不表示它做的比較差。

    還有一點,三層式並不等於 MVC ,雖然同樣是分三塊。

    n-Tier的意義是將運算工作,分散交給不同的伺服器處理,以有效配置伺服器的處理效能。而最常見的分配策略,就是 Database, Application, Client 。

    再說到 MVC ,則是基於"程式碼功能"的隔離策略。根據程式碼的功能分成 Model, View, Control 三個功能性區塊,以降低程式碼之間的耦合度,便於程式開發之分工與維護。

    至於 n-Tier 和 MVC 之間出現扯不清的關聯,則是因為 n-Tier 在系統架構上就限制了程序員必須將不同功能的程式碼分開撰寫。在此結構性限制下,成熟的 MVC 策略顯然是程序員的上上之選。

    事實上,就 web 應用開發工作內容來看,我們其實只在 n-Tier 架構中的 Application 和一部份的 Client 內容中打轉。MVC只被我們應用在其中的2層而已。如何在這2者之間劃上等號?
    FIEND 寫道:

    final 元件的人 觀念不足 寫出來東西 看不清 問題所在 .
    導致 維護上的彈性不易.

    再則 架構面雖然很漂亮 但是 許多元件在效能上並不理想 .
    是效能和有效率的開發的取捨.

    就 1.2 而言 , 老實說 它有不少原件 放在 大型 website 上.
    會造成蠻大的 loding

    例如 :
    1.2 換頁那個 元件 , query 做了二次才能做到 換頁效果 .
    在過萬人的 site 上一定會被打槍的 .
    原則上 這篇文章的換頁功能

    我寫的也這樣設計 ^^!!
    我不是 CakePHP 的重度使用者,當你說它 query 做了二次才達到換頁效果後,我才仔細地看了一下你寫的程式碼和 CakePHP 的 API manual 。我猜想你所謂的 query 了二次是指 findAll() 和 findCount() 吧?

    基本上,你誤用 findCount() 的使用場合了 (也許CakePHP官方的那個也是誤用了)。既然 findAll() 已經以 array 型態傳回了資料結果,何不直接用 PHP 的陣列函數 count() 取得資料結果陣列的筆數?我自己的系統是這樣做的(不是用CakePHP)。
    //$num = $CALL->findCount() ; 
    $num = count($ecstart["record"]);
    

    而 findCount() 的使用時機,則應該是用在我們"僅僅要"資料筆數,而不要資料內容的場合。這時我們使用 findCount() ,將資料查詢和筆數計算的動作在 Database 端處理後,僅僅只將資料筆數傳回 Application 端,以減少資料內容在 Application 端的暫時性地儲存動作。

    CakePHP 基底元件的 method 設計是正確的。但是衍生元件的開發者在使用這些 method 時,還需要多下點功夫了解這些 method 的使用時機與場合。
  • edited 一月 2008
    個人單就工作上使用到 CakePHP 進行專案及其運作狀況做個分享。

    案例是一個每日點擊數大約 2m ~ 3m 的網站,全站架構於 CakePHP,由數台伺服器做 Load Balance(2008/1/1 access_log 加總行數大約在 8m 行左右),尖峰時段單台流出量大約是 4mbits/s,每台伺服器 Loading 在 0.5 ~ 3 之間,平均 CPU 使用率在 10% ~ 30% 之間不等。

    當然以上的數據完全受到硬體和系統整體規劃影響,也沒有對照組來比較,無法反應實際程式運作的效率,只是簡單描述系統運作的現況。

    事實上,大流量與複雜 query 的環境之下,不同 Layer 間的資料緩衝反而對效能影響更大。CakePHP 的 ORM(liked?) Model 使用起來很方便,但是在系統規劃的時候就要把 Model 的 association 和 recursion 之類的行為考慮進去,否則將對 DB 而言會造成很大的負擔。個人在專案上也因為專案特殊的需要,又多疊了一些 magic method 在現有的程式之上(override AppController、AppModel ...etc)。

    As a framework,CakePHP 提供了現成的開發模式,利用統一定義不同元件的行為層層繼承,讓專案成員不用再多費心思在不同模組之間的操作、溝通,能夠專心 focus 在模組本身要完成的功能上(好吧,還有處理客戶[d]無理[/d]的需求)。

    Happy baking.
  • edited 一月 2008
    感謝 dingjie 提供的資訊 :)
  • edited 一月 2008
    感謝分享~~

    謝謝你們給小弟很多保貴的建議

    ccc
  • edited 一月 2008
    dear tokimeki :

    謝謝你的建議 .

    其實我也是把它當你心目中的區 來分 .

    只是我己經習慣用 層來 說明 .

    一般 我把 web 看成 五個區 <<< 我都講五個層 .

    其實在我的設計下 我喜歡 切的愈細愈好.

    可以做到

    一致性的獨立.

    又可以做到 很強的延展性.

    謝謝您的指教 ^^
  • edited 一月 2008
    謝謝上述幾個大大的指教

    1. 層的說法 :

    這裡跟習慣 layer7 說法的大大說聲報歉 .

    其實在 web 的設計上 .

    小弟所謂的層是指 應用層上的再切細的說法.

    可能是講法 您不習慣 , 但是在 php4 紅皮書上 也是用層的說法 .

    它有提到 三層和五層的好處 很利害吧 十幾年前出的書就提出此方法的應用了.

    可能台灣人太習慣 layer7 .

    大家可能在層的認知上不同 , 我會少用這個名詞避免誤會

    謝謝指教.

    2. 效能面 :


    其實 select 一句話就可以把所有 多筆 record 包含 數量表示.

    就 databases 的 連結邏輯.

    可以 直接翻開程式去閱讀.

    你會發現 db 其實是

    1. 先連結

    2. 提出 命令

    3. 做完暫存在 db server

    4. 再去要資料.

    由這四個流程可以了解.

    我己經要出暫存 資料在 db server 了

    我為什麼要重覆 再做一次 上述四個流程

    假設我有一個 table

    它的 資料有 一萬筆 .

    我再做一次 count 只是在做一次.

    當然有人會說~~ 我有去計算時間 效能不錯啊?

    這個觀念錯的很誇張.

    因為 你要的只是 第三個流程你和 db 要資料的速度

    一般維護過 幾百萬人網站的

    都可以很深刻的理解小弟的說法

    ^^ 謝謝大家指教
  • edited 一月 2008
    上述講錯 :

    因為 你要的只是 第三個流程你和 db 要資料的速度

    是 第四個流程你和 db 要資料的速度.

    這個不能證明 效能有變好

    如果您的判斷方式是如此

    可以實驗一下小弟的說法.

    也由此可說明您對 db 和 程式端的觀念還沒有很成熟.
  • edited 一月 2008
    re : shirock

    ^^!! 如果 我的 db 有 一千萬筆資料

    我全部都要入 變數 再 count ...

    應該會 error ....

    提供參考

    謝謝你的指教
  • edited 一月 2008
    re dingjie :

    可能是我太追求完美了吧.

    又或者是 我老是面對機車的 report 和 資料庫問題.

    所以小弟很直覺得覺得它 在使用上.

    會吃掉 不少資源 , 不過 用機器的數量來解決也不是一件壞事.

    機器一台才 十來萬 .

    一個人力成本可能就不只這些錢了 .

    多幾台併行運作 也是很 ok 的做法 .

    只是我覺得它還可以有更好的表現 , 為什麼要做浪費資源的東西.

    謝謝您的數據.

    但是我對 cakephp 處理 db 的 model 用法上.

    還是有蠻大的意見.
  • edited 一月 2008
    re : 我跟我那位朋友也有去,當時FIEND拿出來的東西,其概念跟我當初提得很類似,會後我們兩個都覺得蠻巧合的。

    呵呵~~

    沒有所謂的巧不巧..

    其實 w3c 有提供 更細的架構表.

    如您所說 區的切割 更細.

    這是己知的做法 .

    條條道路通羅馬 以您的能力會想到這樣的東西 我覺得不是巧合 .

    您說是吧 ^^
  • edited 一月 2008
    dear shirock :

    re :
    ##################################
    基本上,你誤用 findCount() 的使用場合了 (也許CakePHP官方的那個也是誤用了)。既然 findAll() 已經以 array 型態傳回了資料結果,何不直接用 PHP 的陣列函數 count() 取得資料結果陣列的筆數?我自己的系統是這樣做的(不是用CakePHP)。


    //$num = $CALL->findCount() ;
    $num = count($ecstart["record"]);


    而 findCount() 的使用時機,則應該是用在我們"僅僅要"資料筆數,而不要資料內容的場合。這時我們使用 findCount() ,將資料查詢和筆數計算的動作在 Database 端處理後,僅僅只將資料筆數傳回 Application 端,以減少資料內容在 Application 端的暫時性地儲存動作。

    CakePHP 基底元件的 method 設計是正確的。但是衍生元件的開發者在使用這些 method 時,還需要多下點功夫了解這些 method 的使用時機與場合。

    ##################################



    小弟再補充說明一下 :

    老實說 看完您的論點... @$%@#%@#%@#%@#

    ^^!! 算了 我覺得我真的很欠 k . 我真的蠻需要 知音的 鳴鳴~~

    忍不住還是想跟您說 model 的設計 正確什麼? ^^!!

    我在 model 和 db_sources 內並沒有看到 可以避免 二次 select 的method

    分享一下 我的想法 片面之詞可能大家覺得小弟在說大話 :

    1. count 陣列變數太誇張了..
    我在 db
    而且 findall 我己經下 limit 了說
    塞入 變數 的想法 真的很酷 不敢用會被打死 .

    2. 官方設計這種東西說正確 , 我有很大的意見喔.

    說明一下 :

    mysql_result 的 method 並沒有在 db_souces 下被運用.

    一般情況下 , 我會利用 mysql_result 去取 出我要的 第 n 筆的 record .

    然後用 while 告訴它我只要取幾筆..

    而在沒有 conettion close 之前.

    我就可以用 mysql_num_rows 去取得它的個數.

    而我也沒有用 limit 去 下 query .

    所以我可以 要出 我要的二種資料.

    mysql 的function 並不是只有 fetch_array ....

    為什麼一定要用 limit 去達成 page 效果

    而 model 和 db_souces 又沒有提供 result 的用法

    如此 即為 為什麼我覺得 cake在換頁的表現有待加強.
  • edited 一月 2008
    re :
    我知道下面的回應可能會變成打嘴砲,不過既然上面有問,FIEND有回,不回應一下不好意思。

    #########################################

    嘿嘿 怎麼會.. 我上了一課說 .^^

    其實 我臉皮比較厚 , 討論問題的方式也比較 晰利 .

    有更好的論點 小弟 很願意去吸收 .

    在名詞的用法上 您就給在下上了一課.

    也希望 在論點上相互推翻大家求進步 ^^



    re :
    2. 在多層架構下,每一層都有各自的MVC,FIEND認為不夠細膩可能是因為他覺得多層架構下仍維持一組MVC的緣故

    3. 整個網際網路是「網」而非「層」的概念,因此MVC常常在實做上被打破,這是寫程式的權變,不能依此對MVC做適不適合的評論

    這說法挺奇怪的,基本上 "三層式" 只是概念性的說法,以有別於主從式架構明顯的二層結構,並不是 "只有三層"。所以後來更常見的說法是 n-Tier 或 "分散式架構"。"層數"並不是愈多愈好,因此它"雖然"只有三層,並不表示它做的比較差。

    還有一點,三層式並不等於 MVC ,雖然同樣是分三塊。

    n-Tier的意義是將運算工作,分散交給不同的伺服器處理,以有效配置伺服器的處理效能。而最常見的分配策略,就是 Database, Application, Client 。

    再說到 MVC ,則是基於"程式碼功能"的隔離策略。根據程式碼的功能分成 Model, View, Control 三個功能性區塊,以降低程式碼之間的耦合度,便於程式開發之分工與維護。

    至於 n-Tier 和 MVC 之間出現扯不清的關聯,則是因為 n-Tier 在系統架構上就限制了程序員必須將不同功能的程式碼分開撰寫。在此結構性限制下,成熟的 MVC 策略顯然是程序員的上上之選。

    事實上,就 web 應用開發工作內容來看,我們其實只在 n-Tier 架構中的 Application 和一部份的 Client 內容中打轉。MVC只被我們應用在其中的2層而已。如何在這2者之間劃上等號?

    ########################################

    剛又看了一下 shirock 和 tokimeki 的文章 , 我大楖 了解 你們說的層和區的說法了

    名詞的說法 再一次 跟大家說聲報歉 .

    可能我一直看不起 mvc 而不接受使用它的名詞所致 .

    也謝謝 shirock 將 model view control 和 content layer logic layer presentation 的 區別 細分給在下 .

    #

    網站程式 多層式架構設計 觀念 是在 mvc 還沒有出來前就有了 ..

    當然小弟沒有想扭轉大家對名詞用法的 觀念 .

    因為我的名詞用法是錯的 .


    套用大大們的說法 小弟具體的表達對 mvc 架構的 不滿 .

    一般網站應用程式中設計常見的層級 最常見的是三種 :

    1. CONTENT LAYER

    2. LOGIC LAYER

    3. PRESENTATION LAYER

    而 w3c 發佈的 xml 架構將它切割的更加的細 .

    目地是讓它可以達到 :

    1. 可維護性

    2. 模組化

    3. 展示邏輯層獨立 (標準化)

    4. 內容邏輯層獨立 (標準化)

    5. 可跨平台

    6. 輕鬆變更資料庫

    7. 程式碼可重覆使用


    等等......


    小弟指的 三層應該不同 mvc 指出的 三個模組 封裝 沒錯 .

    但是不能把它 說成是三層式架構 再次謝謝二位老大的指教.

    而我常把名詞混合使用 真的該打 ^^!! 覺得自己溝通能力要加強 呼呼

    而這三層 mvc 則是 將 三種特性 邏輯特性 封裝起來 .

    但也因此 使得 網站程式的架構上 變的不易維護 .

    所以 在 真的專案實作上 我是完全不考慮使用 mvc 的做法的 .

    但是不可否認的是 .

    在多層的考量下 程式設計 會變的更加的困難 ....

    新進工程師在入門上也會覺得寸步難行.

    要教育到 維持 w3c 發佈出來的 xml 架構也還有一段距離.

    所以 mvc 反而變的比 多層式架構的設計來的 活潑.

    而讓許多 人淡忘 多層架構的好處.

    也因為沒有多層的觀念.

    在程式設計上 可以看到很多很頭大的設計 ......

    所以我才對 mvc 這麼不爽 ^^!!
  • edited 一月 2008
    FIEND:

    如果 我的 db 有 一千萬筆資料
    我全部都要入 變數 再 count ...
    應該會 error ....


    1. count 陣列變數太誇張了..
    我在 db
    而且 findall 我己經下 limit 了說
    塞入 變數 的想法 真的很酷 不敢用會被打死 .

    你不了解查詢結果的記憶體管理機制。

    當 PHP 向 DB 查詢資料後,資料內容就已經被儲存在 PHP 這方的記憶體內容了。說的更明白些:
    $resource = mysql_query($query);
    

    此時 $resource 已經是一個儲存了查詢結果的變數(但型態不是陣列)。如果查詢結果有一千萬筆資料,那麼 $resultSet 就會配置那麼多的記憶體空間儲存那一千萬筆資料。接著再使用 mysql_fetch_array() 等方法取出個別資料記錄。

    RTM:
    PHP Manual::mysql_query

    The returned result resource should be passed to mysql_fetch_array(), and other functions for dealing with result tables, to access the returned data.

    ----
    PHP Manual::mysql_free_result

    mysql_free_result() will free all memory associated with the result identifier result.

    mysql_free_result() only needs to be called if you are concerned about how much memory is being used for queries that return large result sets.

    那被查詢出的一千萬筆資料並不是保存在 DB 端。

    有碼有真相,來一段測試過程。首先查詢一個上萬筆的記錄結果,然後 sleep() 暫停 PHP 程式執行。再以 top(Unix) 或 工作管理員(windows) 觀看 PHP 程式查詢前後使用的記憶體變化量,就知道查詢的資料結果是被儲存在哪了。
    <?php
    $link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
    
    $query = 'SELECT * FROM my_table'; //有幾萬筆資料的查詢結果
    
    $resource = mysql_query($query, $link);
    
    sleep(120); //暫停2分鐘
    
    mysql_free_result($resource);
    
    sleep(120);
    
    ?>
    

    你說把一千萬筆資料"塞入變數"會被打死,那你連 findAll() 都不能用,因為會被打死。你只能每次都向 DB 查詢"剛好一筆"資料。但我相信,這樣做絕對死更快。

    ps. PHP 口中的 resource 其實就是一個"塞入很多內容的變數"。

    FIEND:

    mysql_result 的 method 並沒有在 db_souces 下被運用.
    一般情況下 , 我會利用 mysql_result 去取 出我要的 第 n 筆的 record .
    然後用 while 告訴它我只要取幾筆..
    而在沒有 conettion close 之前.
    我就可以用 mysql_num_rows 去取得它的個數.
    而我也沒有用 limit 去 下 query .
    所以我可以 要出 我要的二種資料.
    mysql 的function 並不是只有 fetch_array ....
    為什麼一定要用 limit 去達成 page 效果
    而 model 和 db_souces 又沒有提供 result 的用法
    如此 即為 為什麼我覺得 cake在換頁的表現有待加強.

    一個經驗老道的 PHP programmer ,會定義一個 SPL::ArrayObject 的衍生類別,將 resource 及存取函數都封裝進去。
    $resource = mysql_query($query);
    
    class ResultSet extends ArrayObject {
        protected $resource;
    
        public function __construct($resource) {
            $this->resource = $resource;
        }
        
        public function count() {
            return mysql_num_rows($this->resource);
        }
    
        public function offsetGet($index) {
            return mysql_result($this->resource, $index);
        }  
    }
    
    $dbResultSet = new ResultSet($resource);
    
    echo count($dbResultSet);
    
    
    $record = mysql_result($resource, 0);
    
    $record = $dbResultSet[0];
    
    

    如此一來,用 count() 取得資料結果筆數有何問題?

    啥,CakePHP 沒用 ArrayObject ,而是直接把 resource 的記錄內容轉成 array ?那也沒什麼不好啊,直接用陣列運算子就能操作資料了,比 mysql_xxx() 更簡單。所以你抱怨 "mysql_result 的 method 並沒有在 db_souces 下被運用",這毫無意義。


    再說到 limit 的問題,這實際上就是記憶體管理的問題,也就是我在本文第一段回應的內容,但由於你不清楚這方面的機制,所以我再強調一次,在換頁功能中使用 limit 是為了節省記憶體。

    最後,我要修正一下我前篇回應的錯誤。我前篇回應了忽略了 findAll() 有 limit n 的敘述在內,所以用 count() 得到的數目必定小於等於 n ,而不是結果總筆數。因為一次 交易只能回傳一個結果。就此而言,先用 findAll() 再用 findCount() 是不得已的舉措。

    就算我在一次交易中塞入2行查詢敘述,一個查內容(limit n),一個查 count ,但 DB 只會回傳最後一行查詢敘述的結果,第一行敘述的結果則不會回傳。

    不過老練的 SQL 人員會一招 UNION 指令,可以把2個查詢敘述併成一行,使得2個查詢結果合併為一個,就可以傳回來了。
  • edited 一月 2008
    你說的 W3C 架構,就是基於 MVC 策略的架構。你一邊說多層架構多好,又一邊抱怨 MVC 。顯然你還是沒搞清楚那些是什麼。

    Anyway, 免費授業到此為止, 請你務必去找"觀念正確的書"看看。
  • edited 一月 2008
    你不了解查詢結果的記憶體管理機制。

    當 PHP 向 DB 查詢資料後,資料內容就已經被儲存在 PHP 這方的記憶體內容了。說的更明白些:

    $resource = mysql_query($query);


    此時 $resource 已經是一個儲存了查詢結果的變數(但型態不是陣列)。如果查詢結果有一千萬筆資料,那麼 $resultSet 就會配置那麼多的記憶體空間儲存那一千萬筆資料。接著再使用 mysql_fetch_array() 等
    方法取出個別資料記錄。


    謝謝您花這麼多時間 不厭其煩的解說 這個問題 .

    很高興可以跟您討論這個問題 .


    我對您的講法保留很大的疑問 也對您的認知感到不可思議.

    另外... 看到你做的實驗我不小心放聲大笑了 ^^!! SORRY .

    我的認知 查詢結果是放在 DB 的 記憶體內容 .

    然後 PHP 再去跟 DB SERVER 要.

    當然對您做的實驗 我覺得 這個實驗不代表什麼.

    如果 說 PHP 將 配置記憶體切到這麼大?

    但不代表 它將 DB 的內容也全都放進記憶體了.

    一般正常流程 PHP 會保留一些做為 連結用的 快取資料 量是很小的.

    所以為何 PHP.INI 開 8MB 記憶體 我照樣可以 QUERY 3千萬筆資料做 REPORT

    將 1G 的 TABLE 的資料 拉出來?

    若如您所說 全都拉出來 放在 PHP MEMORY , 我覺得 這真的有點奇怪?

    應該說 您的觀念 讓我覺得不可思議 DB 的連結 不可能是這樣設計的.

    如果 遇到 TB 級的 DB 這種方式 大家都不用寫程式了.

    所以 我實在無法說服自己 去相信 QUERY後 東西會全都放入 PHP 記憶體.

    的說法.



    $resource = mysql_query($query);

    class ResultSet extends ArrayObject {
    protected $resource;

    public function __construct($resource) {
    $this->resource = $resource;
    }

    public function count() {
    return mysql_num_rows($this->resource);
    }

    public function offsetGet($index) {
    return mysql_result($this->resource, $index);
    }
    }

    $dbResultSet = new ResultSet($resource);

    echo count($dbResultSet);


    $record = mysql_result($resource, 0);

    $record = $dbResultSet[0];


    如此一來,用 count() 取得資料結果筆數有何問題?

    我覺得你可能搞錯了.

    上述的 SPL::ArrayObject 內容就是我習慣的寫法.

    我很清礎上述的 OBJ 的用法 .

    怎麼可能這樣用 , 你是不是搞錯了?

    我覺得你對 OO 的觀念怪怪的 這個 OBJ 是去模擬 ARRAY

    所以你 呼叫 COUNT 時 .

    它其實等於 是去呼叫 ARRAY OBJ 的 COUNT

    也就是去 呼叫

    public function count() {
    return mysql_num_rows($this->resource);
    }

    ###

    這個 用法 .NET 的 DBO 也是這樣用的.

    所以不懂程式的人

    都會以為是 DB 丟到 ARRAY ..

    這個我很久以前就跟同事討論過了.

    你不能說它是陣列.

    而是它是去模擬 ARRAY .
    你說的 W3C 架構,就是基於 MVC 策略的架構。你一邊說多層架構多好,又一邊抱怨 MVC 。顯然你還是沒搞清楚那些是什麼。

    Anyway, 免費授業到此為止, 請你務必去找"觀念正確的書"看看。

    W3C 就是基於 MVC 策略的架構?

    你發明的嗎?

    CCCC
  • edited 一月 2008
    你運氣不錯,剛好有這麼一篇:PHP Large result sets and summary tables.

    文章中說:
    Because by default mysql_query uses mysql_store_result C library call and buffers all result set in the process memory. Not good if there are over 50 millions of rows. Note this limit is not controlled by memory_limit PHP config variable because that only controls memory which passes via PHP memory management which does not apply to MySQL result set.

    我苦命些,寫PHP程式時搭配過 MS SQL, PostgreSQL, DB2 。所以我還真的遇到查詢結果筆數太多,必須提高 memory_limit 設定值的時候。

    ==================

    我從頭到尾都沒說 ResultSet 這個"我定義的類別"的實例是 array.

    第一,我說 CakePHP 的 findAll() 回傳的是一個 array 。請問這有錯嗎?

    第二,我說對於 $resource 這樣"不是陣列"的變數,我們會定義一個類別去封裝它,使得我們可以用"陣列運算子"去操作它的實例。老兄,運算子覆載是什麼知道吧?我有說 $dbResultSet 這個實例是一個 PHP 原生型態陣列(primitive type array)嗎?

    第三,從純OO的角度來看,一個 ArrayObject 類別的實例就是一個 array。只是 Java/C#/PHP 在這方面做得不夠純,所以有 primitive type array ,也有 array object。

    要不要看看 Ruby, JavaScript 的 array 是怎麼做的?如果你覺得那是初學者玩的,不妨再看看 SmallTalk 。它們的陣列可沒有分 primitive type array 和 array object 。

    你把 Java/C# 這些混血OOPL 當作 OOPL 的標準來說別人不懂程式或不懂OO,未免可笑。
  • edited 一月 2008
    我突然覺得自己很像烏賊 ^^!!

    縮短一下 討論的內容.


    1.

    MVC != n-Tier

    MVC 將 N-TIER 各別放在自己的 元件內 但也可以不要尊守 N-TIRE

    導致 很多人太 FOCUS MVC 而忽略 N-TIER .

    謝謝指教 .




    RE :
    ####################################
    我苦命些,寫PHP程式時搭配過 MS SQL, PostgreSQL, DB2 。所以我還真的遇到查詢結果筆數太多,必須提高 memory_limit 設定值的時候。
    ####################################

    2. 有點偏離主題了 ^^!! SORRY 用詞激烈 跟您說聲報歉.

    拉回主題 :

    大家實驗的數據和想法盡然有這麼大的落差 .

    A . 我的認知是 QUERY 完 資料會放在 DB 的記憶體 , PHP 端只會有一些快取的資料 .

    B . shirock 的認知是 QUERY 完 資料會放在 PHP 的記憶體 .
    (不知道有沒有看錯 因為我覺得這個想法很怪)
  • edited 一月 2008
    我都把參考文章貼出來了,你還不看就回覆啊。真服了你。

    參考文章中已經很明白指出 mysql_query 跳過 PHP 內建記憶體配置機制,而直接使用 mysql C library 的函數儲存資料在 PHP 程序這端。而 memory_limit 只會管制到 PHP 內建記憶體配置機制的使用上限。所以 mysql_query 查詢大量資料時,不會受到 memory_limit 的限制。

    你說你用 PHP +MySQL 查詢大量資料時不會受到 memory_limit 限制,原因在此。

    但是其他資料庫的查詢函數則是用 PHP內建記憶體配置機制儲存資料。所以當我用 PHP 向 PostgreSQL, MS SQL 等 DB 查詢大筆資料內容時,就會受到 memory_limit 的限制。

    這不是我的"認知",而是程式碼明擺著的事實。
  • edited 一月 2008
    RE :
    Because by default mysql_query uses mysql_store_result C library call and buffers all result set in the process memory. Not good if there are over 50 millions of rows. Note this limit is not controlled by memory_limit PHP config variable because that only controls memory which passes via PHP memory management which does not apply to MySQL result set.

    mysql_query 跳過 PHP 內建記憶體配置機制,而直接使用 mysql C library 的函數儲存資料在 PHP 程序這端。而 memory_limit 只會管制到 PHP 內建記憶體配置機制的使用上限。所以 mysql_query 查詢大量資料時,不會受到 memory_limit 的限制。



    ###########

    因為你是截取 BLOG 的文章 所以我覺得它只是 加強你的說法.


    "result set" 這個字眼 小弟覺得它並不全然表示把資料全往 PHP C LIB MEMORY 放 .

    而是把 OBJ INDEX 暫存在 MEMORY .

    如果說 它會將 QUERY 結果全撈到記憶體

    那為什麼上頭的說法 建議五千萬筆 以內呢?

    假設我的 DB 是 TB 級的 .

    不要說 五千萬 像你的說法 .

    幾百萬筆資料 我在 SELECT * 時就灌爆 SERVER 的記憶體了.

    根本就玩不下去.

    站在 系統設計的角度.

    您的論點我覺得根本就站不住腳 .
  • edited 一月 2008
    這個問題嘛~我個人是覺得有圖有真相,看一下這篇:
    http://blog.felho.hu/what-is-new-in-php-53-part-3-mysqlnd.html

    我能確定的是,無論如何,在取回結果時,那些結果的資料有一份副本在PHP所管理的記憶體中(否則 php便無法存取它)。
    但是這個複製資料的機制,我並不確定是否在query完成之後便會啟動。

    另外,對於石頭所說的:
    mysql_query 跳過 PHP 內建記憶體配置機制,而直接使用 mysql C library 的函數儲存資料在 PHP 程序這端
    以及
    但是其他資料庫的查詢函數則是用 PHP內建記憶體配置機制儲存資料。

    這點持保留意見,因為我去翻過PHP的原始碼,找不出 mysql_query 有改成跳過 memory_limit 的地方。

    如果石頭有找到,請幫我指出來~

    另外,從我個人貧乏的實務經驗裡,我看不出來如果必須要知道某個查詢的總筆數跟內容時,為何總筆數要在內容後去查詢?

    一般而言,會先查出總筆數,再根據其數目作分頁~
    或者根本不管總筆數為何,直接用頁碼跟每頁幾筆直接作分頁查詢。

    另外之二,假設內容資料是非常龐大的,且必須利用查詢內容作某些運算(例如:矩陣運算之類的),那麼這樣的應用不該由PHP程式來完成,應由其他的方式來作計算(例如:資料庫作OLAP或是Server上某個用C/C++寫的程式定期跑),Web這邊只做顯示以及計算排程即可。
  • edited 一月 2008
    看得好累....

    1. 請先看一下 mysql C API 的 mysql_store_result 和 mysql_use_result 再來說道理.
    mysql 不可能以資料直來直往的方式把結果回傳給 user , 因為一個 mysql_query 就把資料直接全部倒給 User . 如果msqyl系統這樣設計的話, 就真的太恐怖了, 一個 user 的 select * from any_large_table 就可能把系統撐爆.

    2. result set 應該是指結果集, 並不是指資料 row. 應說像是資料Row的抽象資料, 雖然會比實際的資料Row所使用的空間小許多, 但在筆數太大的情況下, Result Set 仍然可能變得很大(請注意大小是與資料筆數有關, 而不是與每個row 的資料大小有關). 從 PHP Large result sets and summary tables.這個 Blog 中可以看到 Result Set 是可以放在 PHP 端或是 mysql server 端 .

    而為什麼要考慮result set兩種方法? 看起來是為了效率和空間的考量.
    重點是在資料筆數的多少, 如果在不超過一定筆數(要看PHP端的記憶體配置), 用 mysql_query 是 OK 的, 而且因為 Result Set 就放在 PHP 端, 所以速度上也會比較快.
    但如果筆數過多, 則必須採用 mysql_unbuffered_query , 以每次讀取 Result Set都要詢問Server , 換取 PHP 端記憶體的空間.

    該用哪一種方法要看系統的環境如何.

    3. OO 在各個語言的好壞是見仁見智的, 一般使用者只會看開發工具及支援廠商. 和 OO 的表現方式關聯不大. (要不然是不是大家都要學 small talk??) 重點在習慣每個語言 OO 的特性, 才能寫出一個漂亮的程式.

    4. 一般的評價上, cakePHP 的確有它的缺點, 但是只要慬得避開缺點(像 FIEND 提到的 cakePHP 對大型資料庫的效率問題, 試問有多少人會用到大型資料庫??) , 一樣可以完成一個系統.


    5. 而 MVC 和多層式架構本來就不是該放在同一個水平上比較的, MVC 因為把系統切成多個 Model-View-Controler , 所以多人或漸進式的開發和維護上比較方便; MVC 內部一樣可以切分很多層級, 所以只能說 MVC 並不完全 follow W3C 的多層式架構; 我覺得兩個架構應該是相輔相成的.

    看到上面的討論, 只能讓我想到"文人相輕"這個成語; 系統本來就有很多面向的思考方式, 各種架構都各有好壞, 只能以各人習慣與使用範圍來決定各人的喜好.
    在我以往的經驗上, 很難只用一種程式寫法就能走天下. 我覺得要能夠適應系統限制, 找出適合使用的架構和流程, 才是一個合格的程式設計人員.

    在表達自己的意見之外, 也該多想想別人的講法是否正確,
    是否該調整自己的想法?
  • edited 一月 2008
    感謝各位,這個討論串讓我收穫良多!

    不過,FIEND 的以下這段的說明不對喔!

    #########################################

    網站程式 多層式架構設計 觀念 是在 mvc 還沒有出來前就有了 ..

    #########################################

    internet 開放給商業使用是在 1992 年左右,網站程式更是在那之後才發展的。資料參見:

    History of the Internet 的 6 Opening the network to commerce

    而 MVC 模式則是在 1979 年被提出,1987就已經被實做出來。資料參見:

    Model-view-controller - Wikipedia

    另外,對於 query 之後,資料在何處的問題,提個想法。不知能不能將 web 程式放在 Server A,MySQL 放在 Server B,實際觀察看看,是否就能確定了呢?
  • edited 一月 2008
    對 MVC 的年代做更正:

    MVC 最遲是 1978 就已被實做出來,所以 MVC 觀念應該是在那之前,就已經被提出。

    資料來源:XEROX PARC 1978-79
  • edited 一月 2008
    很多深入的討論都是靠"文人相輕"來的. 太客氣的話,大家都是點到為止,辯不出真理!!
    我在旁邊看的很高興勒,雖然大部分都看不懂 :-D
  • edited 一月 2008
    對 MVC 的年代做更正:

    MVC 最遲是 1978 就已被實做出來,所以 MVC 觀念應該是在那之前,就已經被提出。


    哈 ~沒錯 後來我去看 wiki mvc 的說明 .

    1978 年就出來了.

    而且 mvc 比 mfc 還更早.

    更有人說過 mfc 是 mvc 的失敗作品 .

    原來我也是印象泒的.

    沒有去求證.

    謝謝指正.

    ###

    哇 天啊!~~NORMAN 老大 來了 ^^!! ...

    謝謝 老大 提出 更具體的答案 .

    我的經驗 比起您來差太多了 跟人家辨論像墨魚一樣

    謝謝您的指點.

    ##########
    很多深入的討論都是靠"文人相輕"來的. 太客氣的話,大家都是點到為止,辯不出真理!!
    我在旁邊看的很高興勒,雖然大部分都看不懂

    您客氣了我 也學到很多東西...

    謝謝大家給小弟的意見和指教.
  • edited 一月 2008
    另外,從我個人貧乏的實務經驗裡,我看不出來如果必須要知道某個查詢的總筆數跟內容時,為何總筆數要在內容後去查詢?

    一般而言大家的理解都是這樣,
    但是我看到 FIEND 的程式中,
    內容和分頁是在兩個不同的 MVC 模組中,
    所以才會出現要下兩次 QUERY 的情況.

    似乎 cakePHP 在這個部份的架構設計的怪怪的.
  • edited 一月 2008
    我的經驗 比起您來差太多了 跟人家辨論像墨魚一樣

    Web 上的經驗我還算是新手而已...

    只能說工作的時間比較多, 看過的系統也比較多一點.
  • edited 一月 2008
    一般而言大家的理解都是這樣,
    但是我看到 FIEND 的程式中,
    內容和分頁是在兩個不同的 MVC 模組中,
    所以才會出現要下兩次 QUERY 的情況.

    是啊...

    其實 db 可以一個 query 就把 資料全拉回來.

    很多 db system 根本連 limit 都不削使用.

    把 limit 的記憶從自己腦裡清除.

    就可以知道為什麼我覺得 cake 在這段的設計上

    讓我覺得 設計的很不好.



    很多深入的討論都是靠"文人相輕"來的. 太客氣的話,大家都是點到為止,辯不出真理!!

    是啊~

    本來我的個性也是 覺得會得罪人就不敢再討論下去.

    很多年前 .

    剛好也是為了 db query 的問題跟同事辨論了好久.

    例如 :

    db 撈回資料會全數放在 php陣列的看法.

    在 很久以前(大楖六年前吧) 因為自己不是科班畢業的 只看結果

    所以 我也以為是這樣.

    後來在一次的在跟同事的辨論中 , 我才發現自己錯的太嚴重了.

    db 下 query 頂多 會把一些 key 做好 放在 db 端和 php 端做對應.

    如果全部都要拉成 arryay 一定會灌 爆 server .

    如果 不打破沙鍋辨論到底.

    就不知道自己錯的有多誇張.

    所以小弟的討論風格 會比較講求實證和比較站的住腳的理論.

    這裡也跟 "shirock" 老大說聲報歉 , 若您可以提出更有利的證據來證明

    您提出的 結果是對的.

    小弟會很感謝您給小弟上了一堂免費的課.
  • edited 一月 2008
    FIEND,

    你提到:
    其實 db 可以一個 query 就把 資料全拉回來.

    很多 db system 根本連 limit 都不削使用.

    能否請教一下,就我知道除了 Oracle, SQL Server 2005 不支援 LIMIT 外 (所以只好提供變相的 ROWNUM 、 ROW_NUMBER 等方法) ;請你把這些 DB System 列出來讓我們參考,我很有興趣知為什麼這些 DB System 不「屑」 LIMIT 的理由。

    另外也要請教你如何用一次 Query 就可以做到分頁的方法 (而且要能顯示) ,除了上面 tokimeki 提到不理會總筆數的方法以外。

    我個人所做過的方法是先將總筆數算到另一個表格,但這還是得要兩次 Query 才能完成 (一次抓總筆數、一次抓該分頁的資料) 。
    本來我的個性也是 覺得會得罪人就不敢再討論下去.

    討論是好事一件,尤其技術含量高的討論更是讓大家意猶未盡。不過身為朋友,我強烈建議你的用詞應該多加以考慮,很多時候這就是你得罪別人的原因。因為也許你寫者無意,但看者有心。

    至於其他方面,就我淺薄的知識,就只能同意 tokimeki 和 normansu 的部份了。

    ===

    ...這個論壇只有版主才能編輯自己的文章...Orz
    FIEND 寫道:
    一般而言大家的理解都是這樣,
    但是我看到 FIEND 的程式中,
    內容和分頁是在兩個不同的 MVC 模組中,
    所以才會出現要下兩次 QUERY 的情況.

    是啊...

    其實 db 可以一個 query 就把 資料全拉回來.

    很多 db system 根本連 limit 都不削使用.

    把 limit 的記憶從自己腦裡清除.

    就可以知道為什麼我覺得 cake 在這段的設計上

    讓我覺得 設計的很不好.



    很多深入的討論都是靠"文人相輕"來的. 太客氣的話,大家都是點到為止,辯不出真理!!

    是啊~

    本來我的個性也是 覺得會得罪人就不敢再討論下去.

    很多年前 .

    剛好也是為了 db query 的問題跟同事辨論了好久.

    例如 :

    db 撈回資料會全數放在 php陣列的看法.

    在 很久以前(大楖六年前吧) 因為自己不是科班畢業的 只看結果

    所以 我也以為是這樣.

    後來在一次的在跟同事的辨論中 , 我才發現自己錯的太嚴重了.

    db 下 query 頂多 會把一些 key 做好 放在 db 端和 php 端做對應.

    如果全部都要拉成 arryay 一定會灌 爆 server .

    如果 不打破沙鍋辨論到底.

    就不知道自己錯的有多誇張.

    所以小弟的討論風格 會比較講求實證和比較站的住腳的理論.

    這裡也跟 "shirock" 老大說聲報歉 , 若您可以提出更有利的證據來證明

    您提出的 結果是對的.

    小弟會很感謝您給小弟上了一堂免費的課.
  • edited 一月 2008
    能否請教一下,就我知道除了 Oracle, SQL Server 2005 不支援 LIMIT 外 (所以只好提供變相的 ROWNUM 、 ROW_NUMBER 等方法) ;請你把這些 DB System 列出來讓我們參考,我很有興趣知為什麼這些 DB System 不「屑」 LIMIT 的理由。

    另外也要請教你如何用一次 Query 就可以做到分頁的方法 (而且要能顯示) ,除了上面 tokimeki 提到不理會總筆數的方法以外。

    我個人所做過的方法是先將總筆數算到另一個表格,但這還是得要兩次 Query 才能完成 (一次抓總筆數、一次抓該分頁的資料) 。

    我在這串討論內有提到 mysql + php 的 query 一次下完不用下二次的方法.

    而 array obj 那篇文章 也是如此應用.

    您可以詳加閱讀.

    討論是好事一件,尤其技術含量高的討論更是讓大家意猶未盡。不過身為朋友,我強烈建議你的用詞應該多加以考慮,很多時候這就是你得罪別人的原因。因為也許你寫者無意,但看者有心。

    至於其他方面,就我淺薄的知識,就只能同意 tokimeki 和 normansu 的部份了。

    呵呵 ~ 感謝你的建議.
  • edited 一月 2008
    有些 db 系統不屑使用limit 的理由 和 query一次下完 不用再下二次的 好處.

    其實以前私底下我有跟 nroamn 去探討更深層的問題.

    不過我常表達 錯誤 引起更大串的討論 想要結束話題了 ^^!!

    看看 norman 或是其它大大 願不願意跳出來解說.
  • edited 一月 2008
    mysql_result 的 method 並沒有在 db_souces 下被運用.
    一般情況下 , 我會利用 mysql_result 去取 出我要的 第 n 筆的 record .
    然後用 while 告訴它我只要取幾筆..
    而在沒有 conettion close 之前.
    我就可以用 mysql_num_rows 去取得它的個數.
    而我也沒有用 limit 去 下 query .
    所以我可以 要出 我要的二種資料.

    你的一次 Query 是指這個方式嗎?恕我愚昧,我不懂「利用 mysql_result 去取 出我要的 第 n 筆的 record .」以後,再「用 mysql_num_rows 去取得它的個數.」抓出來的 n 會是「總筆數」?還是說你並沒有要「總筆數」?懇請賜教。 (當然這裡的總筆數我指的是有下過條件式過濾後的值;另外 result set 未關閉前可以抓接下來的資料這部份我能理解,在 ASP 也有類似的做法。)

    所以以下我大膽整理一下你上面的說法:

    1. Query 一個不帶 LIMIT 的 SQL ,這時 MySQL 內建的 Cache 機制會將查詢結果放在 MySQL Host 的記憶體。
    2. 利用 while 迴圈找出你要的分頁結果 (含完整的 Row Data) 。
    3. 在 result set 未關閉前,用 mysql_num_rows 抓出總筆數。

    這是你要表達的嗎?還是我理解有誤呢?當然這些步驟有空我會實作看看,也許會讓我有新的想法。
    有些 db 系統不屑使用limit 的理由 和 query一次下完 不用再下二次的 好處.

    現在先「假設」你上面那個 Query 一次的作法是對的,但這也只是在程式部份的考量。我想請教的是,這些 DB System 廠商想的真的只有這樣嗎?而且你是從何得知他們不屑把 LIMIT 做進去呢?既然講求實事求是,那麼有任何資訊可以讓孤陋寡聞的我參考一下嗎?我試著到 Google 找了一下,似乎沒有人討論這個問題, DB 廠商也沒有比較明確的解釋 (也許我眼拙沒找到) 。
  • edited 一月 2008
    謝謝你的整理 , 其實這個問題我跟不少人討論過 .

    但是從來沒有拿來論壇討論過 ^^!! 用打的很麻煩 .

    畫一些圖反而比較好溝通 所以不太想去重覆再去討論 ,

    即然你想了解 , 可能是在下的文字表達能力太差.

    我稍微解釋一下 希望可以讓你理解我這墨魚 胡亂打字不加思考的文章.


    一.

    n 不是總筆數 是我放入 php 記憶體的資料.


    二.
    1. Query 一個不帶 LIMIT 的 SQL ,這時 MySQL 內建的 Cache 機制會將查詢結果放在 MySQL Host 的記憶體。
    2. 利用 while 迴圈找出你要的分頁結果 (含完整的 Row Data) 。
    3. 在 result set 未關閉前,用 mysql_num_rows 抓出總筆數。
    #########

    其實這裡我有講錯 mysql 配置的也是 配合它演算法而放入 db server 記憶體 不是全部都 放入 db 記憶體 .

    不是利用 wile 去找出分頁結果

    而是利用 while + mysql_result 的特性去 做出分頁效果.

    ###############

    三 .

    您好像不喜歡我用不屑這個字眼.

    不過你說的對 , 不屑這個字眼好像我就是官方在講話一樣 .

    只是 有些db廠商 在設計 db 時 不採用 limit 確實有它的考量 .

    詳細的情況 我想寫信去 各 db廠商官方去問比較實際.

    我也請教過一些 資格 十幾年的 工程師

    他們提出 過一些 不支援的論點 不是只有 換頁那個問題而已.


    ps :
    三年前 在您出 smarty 新書後 , 直接在 msn 上跟您說 smarty 是玩具也跟你說聲報歉.

    雖然五年前 我們一起玩過 smarty , 只是後來我放棄 smarty 全都採用 xml 架構去寫.

    所以 當時我對 smarty 沒有惡意 , 只是覺得它不是標準化的東西.

    無法去 支援 多層 架構做開發 ..

    即然 能在 展示邏輯做到切割的表現 , 而無法重覆的使用 ,

    原罪在於它不是 標準化的工具.


    #############

    這次我打字有稍微想過囉 ^^ 謝謝你的建議.
  • edited 一月 2008
    其實這種長篇的討論很難回,因為到討論最後會失焦。
    不過反正已經失焦了,乾脆再回一篇好了,這次我要回的是:為何有些DB不做LIMIT功能。

    這件事情講起來原因可以舉很多,不過我用最直接、最重要的原因來說好了(底下純屬個人意見):
    因為LIMIT對於DB來說不是個條件!

    知道目前大多數DB運作背後最重要的數學模型是什麼嗎?是集合~
    LIMIT敘述是一種結果的循序(搜尋),而非集合運算。

    剩下其他的理由我也不多說了,說下去就是扯皮。
    另外我想問的是:第二版和時會出來阿?:-)
  • edited 一月 2008
    我分享一下 以前 在一位前輩指導下 修正 我換頁寫法的程式.

    來 讓您 驗證 為什麼我認為 php+mysql 一句 query 就可以達到換頁效果.

    而 在 不下 limit 的情況下

    效能反而會比多下一個 count 來的好.




    ###############################

    底下這二支 function 是很久以前寫的 .

    不嫌棄的話可以參考看看 :
    function ecstart_record_page($ecstart , $max_page ,$max_range , $rows){
            if(!isset($ecstart["input"]["get"]["p"])){$ecstart["input"]["get"]["p"] = "" ;}
            if(trim($ecstart["input"]["get"]["p"])=="" or $ecstart["input"]["get"]["p"] < 1){$ecstart["input"]["get"]["p"] = 1  ; }
            $lastpage = ceil($rows/$max_page);
            if($ecstart["input"]["get"]["p"] > $lastpage){ $ecstart["input"]["get"]["p"]=1 ;}
            $rec_start= ($ecstart["input"]["get"]["p"]-1)*$max_page +1;
            $rec_end  = $rec_start + $max_page -1;
            $ploops   = floor(($ecstart["input"]["get"]["p"]-1)/$max_range)*$max_range + 1 ;
            $ploope   = $ploops + $max_range -1;
            if($ploope >= $lastpage){ $ploope=$lastpage;}
            $ppg      = $ecstart["input"]["get"]["p"] - 1 ;
            $npg      = $ecstart["input"]["get"]["p"] + 1 ;
            if($ppg<= 0) $ppg=$lastpage;
            if($npg > $lastpage) $npg=1;
            if($rec_end > $rows) $rec_end=$rows;
    
            $ecstart_page["rec_start"]       = $rec_start ;
            $ecstart_page["rec_end"]         = $rec_end ;
            $ecstart_page["firstpage"]       = 1 ;
            $ecstart_page["lastpage"]        = $lastpage ;
            $ecstart_page["previousrange"]   = $ploops - $max_range ;
            $ecstart_page["nextrange"]       = $ploops + $max_range ;
            $ecstart_page["previouspage"]    = $ppg ;
            $ecstart_page["nextpage"]        = $npg ;
            $ecstart_page["thispage"]        = $ecstart["input"]["get"]["p"] ;
            $ecstart_page["total"]           = $rows ;
            $ecstart_page["loop"]            = $lastpage+1 ;
            for($i=$ploops;$i <= $ploope;$i++){
                    $ecstart_page["item"][]["p"] = $i ;
            }
            return $ecstart_page;
    }
    
    function ecstart_get_page_record($query){
            global $ecstart , $db_params,$database_name , $msg,$max_page ,$max_range ,$no_escape_string;
            if(is_array($ecstart["record"]) != ""){
                    unset($ecstart["record"]);
            }
            $error = @MetabaseSetupDatabase($db_params,$db);
            @MetabaseSetDatabase($db,$database_name);
            $db_res = @MetabaseQuery($db,$query);
            if(!$db_res){ ecstart_alert_err($msg["system_err"]);}
            $rows= @MetabaseNumberOfRows($db,$db_res);
            @MetabaseGetColumnNames($db,$db_res,$db_field_name) ;
            $ecstart["page"] = ecstart_record_page($ecstart , $max_page ,$max_range , $rows);
            if($rows)
            {
                    $sno     = 0 ;
                    for($row=$ecstart["page"]["rec_start"];$row<=$ecstart["page"]["rec_end"];$row++)
                    {
                            if(is_array($db_field_name)){
                                    $sno++ ;
                                    foreach($db_field_name as $key => $value){
                                            $list[$row] = $row ;
                                            $ecstart["record"][$sno-1]["no"]  = $row ;
                                            $ecstart["record"][$sno-1]["sno"] = $sno ;
                                            if($no_escape_string == "Y"){
                                                    $ecstart["record"][$sno-1][$key] = @MetabaseFetchResult($db,$db_res,$row-1,$key) ;
                                            }
                                            else{
                                                    $ecstart["record"][$sno-1][$key] = ecstart_smarty_escape_string(@MetabaseFetchResult($db,$db_res,$row-1,$key)) ;
                                            }
                                    }
                            }
                    }
            }
            @MetabaseCloseSetup($db);
            if($list != ""){
                    $ecstart["status"]["list"] = implode(",",$list);
            }
            return  $ecstart ;
    }
    
    
    
  • edited 一月 2008
    其實這種長篇的討論很難回,因為到討論最後會失焦。
    不過反正已經失焦了,乾脆再回一篇好了,這次我要回的是:為何有些DB不做LIMIT功能。

    這件事情講起來原因可以舉很多,不過我用最直接、最重要的原因來說好了(底下純屬個人意見):
    因為LIMIT對於DB來說不是個條件!

    知道目前大多數DB運作背後最重要的數學模型是什麼嗎?是集合~
    LIMIT敘述是一種結果的循序(搜尋),而非集合運算。

    剩下其他的理由我也不多說了,說下去就是扯皮。

    是啊 就像 have 一樣 .

    謝謝你詳加解說.
  • edited 一月 2008
    另外我想問的是:第二版和時會出來阿?

    老大 我很想哭 說..

    我後來又發現 primary key composed 的

    因為 hasmany 和 hasone .....

    也不支援 primary key composed

    所以我這版寫的 get_page_record 根本就是垃圾 , 沒有考慮 primary key composed 和 hasxxx 的問題..

    第二版 工程很大 哈哈哈...

    而且我最近己經把評估報告做完了 .

    上頭決定叫我不要用 cakephp 了 要玩真的要找時間了

    鳴鳴~~
  • edited 一月 2008
    FIEND:
    請恕小弟眼拙,我看了好幾次,還是沒看到$max_page在何處被設定?
  • edited 一月 2008
    ecstart_record_page($ecstart , $max_page ,$max_range , $rows) <<< 從這裡用參數的方式丟進來.
  • edited 一月 2008
    這裡 一般使用者不能改文章真不方便 再發一篇... 愈來愈長 ^^!!

    ecstart_get_page_record($query){
    global $ecstart , $db_params,$database_name , $msg,$max_page ,$max_range ,$no_escape_string;

    這支我用 global 把它叫進來.

    sorry這支寫很久了 寫了很亂 ^^ 看懂流程抄一下就出來了.
Sign In or Register to comment.