Blog

concrete5管理画面ページ検索機能のコントローラー部を読む

Posted by admin at 13:08 日時 2013/01/25

#

concrete5には管理画面にページ検索機能があり、非常に優秀です。様々な条件で複合的に検索が可能です。なので、検索機能を開発する場合は非常に参考になるのですが、コメントが全然書かれていないので、さらっと自分なりにコメントを足す目的で、ブログに残しておきます。

管理画面の各ページのコントローラー部は concrete/controllers/dashboard 以下に格納されています。該当のファイルはこちら。

concrete/controllers/dashboard/sitemap/search.php

class DashboardSitemapSearchController extends Concrete5_Controller_Dashboard_Sitemap_Search {}

ところが、中身は別のクラスを継承しているだけの空のクラスです。これは、メソッド単位でのカスタマイズを可能にするための配慮です。もし管理画面のページ検索の挙動をカスタマイズしたい場合は、DashboardSitemapSearchController クラスをオーバーライドすればメソッド単位でのカスタマイズが可能です。

継承元のクラスはこちら

concrete/core/controllers/dashboard/sitemap/search.php

class Concrete5_Controller_Dashboard_Sitemap_Search extends Controller {    	public $helpers = array('form');

最初の $helpers というインスタンスは、必要なヘルパーが自動的に読み込まれるようにするためのものです。view.php で $form という変数からformヘルパーにアクセスできるようになります。いまいちピンとこないので個人的には使ってませんが。。

参考:How-To: Build a Single-Page Powered Editing Interface for concrete5 Pages

次にview.phpに値を渡す view メソッドの中身です。

public function view() {  	$html = Loader::helper('html');    	$pageList = $this->getRequestedSearchResults();  	if (is_object($pageList)) {  		$searchInstance = 'page' . time();    		$this->addHeaderItem('<script type="text/javascript">$(function() { ccm_sitemapSetupSearch(\'' . $searchInstance . '\'); });</script>');  		$pages = $pageList->getPage();    		$this->set('pageList', $pageList);  		$this->set('pages', $pages);  		$this->set('searchInstance', $searchInstance);  		$this->set('pagination', $pageList->getPagination());    	}  }

基本的には getRequestedSearchResults メソッドの結果を view に渡しています。が、そこから getPage メソッドの結果や getPagination メソッドの結果も渡しています。この内容は getRequestedSearchResults メソッドの中身を見ないと分からないですね。

$this->addHeaderItem() は header 要素の中に出力を追加できます。
$this->set() で view に値を渡すことができます。$this->set(‘pages’, $pages); の場合であれば view.php で $pages として呼び出せます。

次に、getRequestedSearchResults メソッドの中身です。

public function getRequestedSearchResults() {    	$dh = Loader::helper('concrete/dashboard/sitemap');  	if (!$dh->canRead()) {  		return false;  	}    	$pageList = new PageList();  	$pageList->ignoreAliases();  	$pageList->enableStickySearchRequest();    	if ($_REQUEST['submit_search']) {  		$pageList->resetSearchRequest();  	}    	$req = $pageList->getSearchRequest();  	$pageList->displayUnapprovedPages();    	$pageList->sortBy('cDateModified', 'desc');    	$columns = PageSearchColumnSet::getCurrent();  	$this->set('columns', $columns);    	$cvName = htmlentities($req['cvName'], ENT_QUOTES, APP_CHARSET);    	if ($cvName != '') {  		$pageList->filterByName($cvName);  	}    	if ($req['numResults'] && Loader::helper('validation/numbers')->integer($req['numResults'])) {  		$pageList->setItemsPerPage($req['numResults']);  	}    	if ($req['ctID']) {  		$pageList->filterByCollectionTypeID($req['ctID']);  	}    	if (is_array($req['selectedSearchField'])) {  		foreach($req['selectedSearchField'] as $i => $item) {  			// due to the way the form is setup, index will always be one more than the arrays  			if ($item != '') {  				switch($item) {  					case 'keywords':  						$keywords = htmlentities($req['keywords'], ENT_QUOTES, APP_CHARSET);  						$pageList->filterByKeywords($keywords);  						break;  					case 'num_children':  						$symbol = '=';  						if ($req['cChildrenSelect'] == 'gt') {  							$symbol = '>';  						} else if ($req['cChildrenSelect'] == 'lt') {  							$symbol = '<';  						}  						$pageList->filterByNumberOfChildren($req['cChildren'], $symbol);  						break;  					case 'owner':  						$ui = UserInfo::getByUserName($req['owner']);  						if (is_object($ui)) {  							$pageList->filterByUserID($ui->getUserID());  						} else {  							$pageList->filterByUserID(-1);  						}  						break;  					case 'theme':  						$pageList->filter('ptID', $_REQUEST['ptID']);  						break;  					case 'parent':  						if (isset($req['_cParentAll'])) {  							$req['cParentAll'] = $req['_cParentAll'];  						}  						if ($req['cParentIDSearchField'] > 0) {  							if ($req['cParentAll'] == 1) {  								$pc = Page::getByID($req['cParentIDSearchField']);  								$cPath = $pc->getCollectionPath();  								$pageList->filterByPath($cPath);  							} else {  								$pageList->filterByParentID($req['cParentIDSearchField']);  							}  						}  						break;  					case 'version_status':  						if (isset($req['_cvIsApproved'])) {  							$req['cvIsApproved'] = $req['_cvIsApproved'];  						}  						$pageList->filterByIsApproved($req['cvIsApproved']);  						break;  					case "date_public":  						$dateFrom = $req['date_public_from'];  						$dateTo = $req['date_public_to'];  						if ($dateFrom != '') {  							$dateFrom = date('Y-m-d', strtotime($dateFrom));  							$pageList->filterByPublicDate($dateFrom, '>=');  							$dateFrom .= ' 00:00:00';  						}  						if ($dateTo != '') {  							$dateTo = date('Y-m-d', strtotime($dateTo));  							$dateTo .= ' 23:59:59';  							$pageList->filterByPublicDate($dateTo, '<=');  						}  						break;  					case "last_modified":  						$dateFrom = $req['last_modified_from'];  						$dateTo = $req['last_modified_to'];  						if ($dateFrom != '') {  							$dateFrom = date('Y-m-d', strtotime($dateFrom));  							$pageList->filterByDateLastModified($dateFrom, '>=');  							$dateFrom .= ' 00:00:00';  						}  						if ($dateTo != '') {  							$dateTo = date('Y-m-d', strtotime($dateTo));  							$dateTo .= ' 23:59:59';  							$pageList->filterByDateLastModified($dateTo, '<=');  						}  						break;  					case "date_added":  						$dateFrom = $req['date_added_from'];  						$dateTo = $req['date_added_to'];  						if ($dateFrom != '') {  							$dateFrom = date('Y-m-d', strtotime($dateFrom));  							$pageList->filterByDateAdded($dateFrom, '>=');  							$dateFrom .= ' 00:00:00';  						}  						if ($dateTo != '') {  							$dateTo = date('Y-m-d', strtotime($dateTo));  							$dateTo .= ' 23:59:59';  							$pageList->filterByDateAdded($dateTo, '<=');  						}  						break;    					default:  						Loader::model('attribute/categories/collection');  						$akID = $item;  						$fak = CollectionAttributeKey::get($akID);  						if (!is_object($fak) || (!($fak instanceof CollectionAttributeKey))) {  							break;  						}    						$type = $fak->getAttributeType();  						$cnt = $type->getController();  						$cnt->setRequestArray($req);  						$cnt->setAttributeKey($fak);  						$cnt->searchForm($pageList);  						break;  				}  			}  		}  	}    	$this->set('searchRequest', $req);  	return $pageList;  }

長いのでポイントだけ見てみます。

$dh = Loader::helper('concrete/dashboard/sitemap');  if (!$dh->canRead()) {  	return false;  }

これはサイトマップへのアクセス権限があるかどうかですね。

$pageList = new PageList();  $pageList->ignoreAliases();  $pageList->enableStickySearchRequest();

PageListクラスのインスタンスを呼び出しています。PageListクラスはこれだけのクラスを継承しています。親クラスになるほど抽象度が上がっている…はず

PageList < Concrete5_Model_PageList < DatabaseItemList < Concrete5_Library_DatabaseItemList < ItemList < Concrete5_Library_ItemList

ignoreAliases メソッドはエイリアスを無視します。enableStickySearchRequest メソッドは、ページを離れて戻ってきても、セッション中であれば検索結果を保持します。

if ($_REQUEST['submit_search']) {  	$pageList-&gt;resetSearchRequest();  }

入力があった際は検索結果をリセットしています。enableStickySearchRequest してるからですね。

このあたりの検索対象に限らず検索処理に共通であるメソッドは ItemList に書かれていて、公式ドキュメントの System > Search/Sort/Pagination ページで紹介されています。自前で開発した機能でも、検索処理はこのライブラリーを継承したほうが良いとされています。

$req = $pageList-&gt;getSearchRequest();  $pageList-&gt;displayUnapprovedPages();    $pageList-&gt;sortBy('cDateModified', 'desc');

getSearchRequest メソッドで検索リクエストを取得しています。
displayUnapprovedPages メソッドで未承認のページも含めています。これは管理画面の機能ならでは。
sortBy メソッドで検索結果のソートを行なっています。ここでは変更した日時で。

$cvName = htmlentities($req['cvName'], ENT_QUOTES, APP_CHARSET);    if ($cvName != '') {  	$pageList-&gt;filterByName($cvName);  }

filterByName メソッドでページ名でフィルターしています。

if ($req['numResults'] &amp;&amp; Loader::helper('validation/numbers')-&gt;integer($req['numResults'])) {  	$pageList-&gt;setItemsPerPage($req['numResults']);  }

1ページの表示件数を指定しています。整数のバリデーションにはバリデーション・ヘルパーを使っています。

if (is_array($req['selectedSearchField'])) {  	foreach($req['selectedSearchField'] as $i =&gt; $item) {  		if ($item != '') {  			switch($item) {

ここからさらに色んな条件で複合検索をしています。

$keywords = htmlentities($req['keywords'], ENT_QUOTES, APP_CHARSET);  $pageList-&gt;filterByKeywords($keywords);

キーワード検索

$symbol = '=';  if ($req['cChildrenSelect'] == 'gt') {  	$symbol = '&gt;';  } else if ($req['cChildrenSelect'] == 'lt') {  	$symbol = '&lt;';  }  $pageList-&gt;filterByNumberOfChildren($req['cChildren'], $symbol);

子ページ数で絞り込み

$ui = UserInfo::getByUserName($req['owner']);  if (is_object($ui)) {  	$pageList-&gt;filterByUserID($ui-&gt;getUserID());  } else {  	$pageList-&gt;filterByUserID(-1);  }

ページのオーナーで絞り込み

$pageList-&gt;filter('ptID', $_REQUEST['ptID']);

ページのテーマで絞り込み

if (isset($req['_cParentAll'])) {  	$req['cParentAll'] = $req['_cParentAll'];  }  if ($req['cParentIDSearchField'] &gt; 0) {  	if ($req['cParentAll'] == 1) {  		$pc = Page::getByID($req['cParentIDSearchField']);  		$cPath = $pc-&gt;getCollectionPath();  		$pageList-&gt;filterByPath($cPath);  	} else {  		$pageList-&gt;filterByParentID($req['cParentIDSearchField']);  	}  }

親ページから子ページを検索。filterByPath だと孫ページ以下も検索。

if (isset($req['_cvIsApproved'])) {  	$req['cvIsApproved'] = $req['_cvIsApproved'];  }  $pageList-&gt;filterByIsApproved($req['cvIsApproved']);

承認されているかどうか

$dateFrom = $req['date_public_from'];  $dateTo = $req['date_public_to'];  if ($dateFrom != '') {  	$dateFrom = date('Y-m-d', strtotime($dateFrom));  	$pageList-&gt;filterByPublicDate($dateFrom, '&gt;=');  	$dateFrom .= ' 00:00:00';  }  if ($dateTo != '') {  	$dateTo = date('Y-m-d', strtotime($dateTo));  	$dateTo .= ' 23:59:59';  	$pageList-&gt;filterByPublicDate($dateTo, '&lt;=');  }

ページの公開日時で絞り込み。FromとToで絞り込みしています。同様に最終編集日時、追加日時でも処理が書かれています。

ページのフィルター系は他にも色んなメソッドがあり便利です。公式ドキュメントの Pages > Listing & Searching で紹介されています。

Loader::model('attribute/categories/collection');  $akID = $item;  $fak = CollectionAttributeKey::get($akID);  if (!is_object($fak) || (!($fak instanceof CollectionAttributeKey))) {  	break;  }    $type = $fak-&gt;getAttributeType();  $cnt = $type-&gt;getController();  $cnt-&gt;setRequestArray($req);  $cnt-&gt;setAttributeKey($fak);  $cnt-&gt;searchForm($pageList);

検索系の処理では一番ややこしいのが属性の処理。属性は非常に汎用的にできていますが、その分オブジェクト間のつながりがいまいち分かりにくい。でもまあ、基本コピペで動く。
$fakって何の略だろう。FileAttributeKeyの処理からコピペして忘れたのかな?


Share this entry