Phalcon 的 model search 配合 paginator 就可以很方便地幫查詢資料結果做分頁,但是如果資料筆數過多,則會有速度過慢的問題。
dev tool 產生的程式碼與問題
原本用 phalcon-devtool 產生的 search 相關 volt 檔案與 controller 檔案大概會長這樣
<div class="row">
<table class="table table-bordered">
<thead>
<tr>
<th>Field A</th>
<th>Field B</th>
<th>Field C</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% if page.items is defined %}
{% for modelobjs in page.items %}
<tr>
<td>{{ modelobjs.fielda}}</td>
<td>{{ modelobjs.fieldb}}</td>
<td>{{ modelobjs.fieldc}}</td>
<td>{{ link_to("modelobjs/edit/"~modelobjs.fielda, "Edit") }}</td>
<td>{{ link_to("modelobjs/delete/"~modelobjs.fielda, "Delete", "onclick":"return confirm('Are you sure?')") }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
<div class="row">
<div class="col-sm-1">
<p class="pagination" style="line-height: 1.42857;padding: 6px 12px;">
{{ page.current~"/"~page.total_pages }}
</p>
</div>
<div class="col-sm-8">
<nav>
<ul class="pagination">
<li>{{ link_to("modelobjs/search", "First") }}</li>
<li>{{ link_to("modelobjs/search?page="~page.before, "Previous") }}</li>
<li>{{ link_to("modelobjs/search?page="~page.next, "Next") }}</li>
<li>{{ link_to("modelobjs/search?page="~page.last, "Last") }}</li>
</ul>
</nav>
</div>
<div class="col-sm-3">
<p class="pagination" style="line-height: 1.42857;padding: 6px 12px;">
Total count: {% if page.items is defined %}
{{ page.total_items }}
{%endif%}
</p>
</div>
</div>
public function searchAction()
{
$numberPage = 1;
if ($this->request->isPost()) {
$query = Criteria::fromInput($this->di, 'Modelobj', $_POST);
$this->persistent->parameters = $query->getParams();
} else {
$numberPage = $this->request->getQuery("page", "int");
}
$parameters = $this->persistent->parameters;
if (!is_array($parameters)) {
$parameters = [];
}
$parameters["order"] = "eventtypeid";
$modelobjs = Modelobj::find($parameters);
if (count($modelobjs ) == 0) {
$this->flash->notice("The search did not find any modelobjs");
$this->dispatcher->forward([
"controller" => "modelobjs",
"action" => "index"
]);
return;
}
$paginator = new Paginator([
'data' => $modelobjs,
'limit'=> 10,
'page' => $numberPage
]);
$this->view->page = $paginator->getPaginate();
}
可以發現searchAction()中,因為做了 Model::find($parameter),且 $parameter 沒有限制回傳資料筆數區間,所以如果符合條件的資料有上萬筆,那可能光資料傳送的時間就吃掉許多。
解決方式
解決的方式就是在 $parameter 中加入 offset 與 limit (實際上還可以加入 order 等資料,可以參考 phalcon 官方文件),這樣就可以限縮回傳的資料剛好是頁面顯示需要的筆數。然後透過 Model::count($parameter) 取得筆數,來計算全部頁數以及目前頁數等原先 Paginator 會幫忙計算的東西。
public function searchAction()
{
$numberPage = 1;
if ($this->request->isPost()) {
$query = Criteria::fromInput($this->di, 'Modelobj', $_POST);
$this->persistent->parameters = $query->getParams();
} else {
$numberPage = $this->request->getQuery("page", "int");
}
$parameters = $this->persistent->parameters;
if (!is_array($parameters)) {
$parameters = [];
}
//加入 order, limit, offset 參數。limit 就等於是每頁資料筆數。
$parameters["order"] = "fielda, fieldb";
$parameters["limit"] = "10";
$parameters["offset"] = max(0,($numberPage - 1)) * 10;
$modelobjs = Modelobj::find($parameters);
if (count($modelobjs) == 0) {
$this->flash->notice("The search did not find any modelobjs");
$this->dispatcher->forward([
"controller" => "modelobjs",
"action" => "index"
]);
return;
}
//拿掉 offset, limit, order ,呼叫 count() 來計算全部資料筆數,這個 function call 實際上就是執行 select count(*)
unset($parameters["offset"]);
unset($parameters["limit"]);
unset($parameters["order"]);
$modelobj_count = Modelobj::count($parameters);
$paginator = new Paginator([
'data' => $eventlog,
'limit'=> 10,
// 'page' => $numberPage //目前在第幾頁就變成要自己計算
]);
$this->view->page = $paginator->getPaginate();
//計算全部頁數,目前在第幾頁等資訊
$total_pages = ceil($modelobj_count/10);
$this->view->page->total_pages = $total_pages;
$this->view->page->last = $total_pages;
$this->view->page->next = min($total_pages, $numberPage + 1);
$this->view->page->before = max(1, $numberPage - 1);
$this->view->page->total_items = $eventlog_count;
$this->view->page->current = max(0, ($numberPage - 1)) + 1;
}