■ユーザーズフォーラム リニューアルのお知らせ
新規投稿は新ユーザーズフォーラムにお願いします。

特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか?

well > 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/4/28 1:40
 baserCMSの関係者の皆様、いつもお世話になっております。

現在、検索が中枢機能となるサイトをbaserCMS3で作りたいと考えております。
フロント機能一覧にて、「サイト内検索:サイト内のコンテンツをカテゴリ別に検索できます。各コンテンツは検索除外設定を行う事ができます。」
という情報を知り、早速試しに実装テストをしてみたところ、
 例えば、3つの親カテゴリーを作成した下記の例では、

・カテゴリ1
    ├── ニュース
    ├── スポーツ
    ├── 経済
    ├── 芸能
    └── 海外
    
・カテゴリ2
    ├── サービス
    ├── 製品
    ├── 問合せ
    └── ブログ
    
・ブログカテゴリ1 (ブログカテゴリー登録されたもの)
    ├── 2012
    ├── 2013
    └── 2014


サイト内検索のセレクトボックスには、

 カテゴリー:指定しない
 カテゴリ1
 カテゴリ2
 ブログカテゴリ1


と表示されます。
残念ながらこれは本意の選択肢ではなく、
親カテゴリーがカテゴリ1の子カテゴリーのリストである、

 カテゴリー:指定しない
 ニュース
 スポーツ
 経済
 芸能
 海外


以上のように表示させたい状況です。

親カテゴリーを全部削除して、カテゴリ1の子カテゴリーリストのみで
親カテゴリーを構成すれば、表示は可能ですが、
カテゴリー機能は非常に便利ですので、確実に複数回使いそうですし、
また、親カテゴリーとして、/category/を作成すると、
のちのち全体の条件分岐処理で、非常に重宝すると考えております。


ファイル \basercms\lib\Baser\View\Elements\search.php の中身を
デバッガを使って変数を確認してみましたが、

	<?php if ($this->BcBaser->siteConfig['content_categories']) : ?>
		<?php echo $this->BcForm->input('Content.c', array('type' => 'select', 'options' => BcUtil::unserialize($this->BcBaser->siteConfig['content_categories']), 'empty' => 'カテゴリー: 指定しない ')) ?>
	<?php endif ?>


'options' => BcUtil::unserialize($this->BcBaser->siteConfig['content_categories']), 

この部分ですが、今の私の理解では改変は難しい状況です。

少し話が離れますが、
固定ページのリスト取得では、下記のように関数getPageList()にて、
親となるページカテゴリのidを指定することで、
子のページカテゴリリストを表示させられることを確認しました。

<?php
$pages = $this->BcBaser->getPageList(5);
foreach($pages as $page){
    echo '<li><a href="'.$page['url'].'">'.$page['title'].'</a></li>';
}
?>


getPageList()のように親カテゴリのidを指定することで、子カテゴリのリストをカテゴリ検索のセレクトボックスに対応させられる方法がありましたら、どうかご教示いただけますよう、よろしくお願いいたします。

■ BaserCMS:3.0.6.1
■ サーバー名:CentOS 6.5 on VirtualBox(PHP 5.4.x / MySQL 5.5.x)
■ スマートURL:ON
■ 設置フォルダ: [ドキュメントルート]
■ PHPスキル:C
■ IDE:NetBeans7.4

n1215 > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/5/1 19:27
well 様

こんばんは。

追記:下記でなんとかなると思って試してみたらそんなことありませんでした。申し訳ないです。
検索用のインデックスが入っているcontentsテーブルに、最上位カテゴリのデータだけしか登録されていないようので、
ほかの部分もごっそりいじる、ないしプラグインとして作成しないと辛そうです。


お察しの通り、BcFormヘルパーのinputメソッドでは
 ・第一引数にinputやselectタグのid属性やname属性と対応する値
 ・第二引数のフォームのタイプなどの設定を表す連想配列
が利用されます。

$this->BcForm->input('Content.c', array('type' => 'select', 'options' => BcUtil::unserialize($this->BcBaser->siteConfig['content_categories']), 'empty' => 'カテゴリー: 指定しない ');


search.phpから出力されているHTMLのソースを見ていただければわかりやすいと思いますが、
第二引数の'options'にはそれぞれoptionタグの属性のvalueと表示に対応した連想配列が入っています。(今回はbaserCMSの仕様でたまたま一致していますが)

・HTMLソース
<form action="/index.php/contents/search" novalidate="novalidate" id="ContentSearchForm" method="get" accept-charset="utf-8">
<select name="c" id="ContentC">
<option value="">カテゴリー: 指定しない </option>
<option value="ニュース">ニュース</option>
<option value="スポーツ">スポーツ</option>
</select>
<input name="q" type="text" id="ContentQ"/>
<input class="submit_button" type="submit" value="検索"/>
</form>


・連想配列
array (size=2)
  'ニュース' => string 'ニュース' (length=12)
  'スポーツ' => string 'スポーツ' (length=12)


つまり、カテゴリ1の子カテゴリを引っ張ってきて、この形の連想配列に整形してやれば同じように検索フォームが使えるはずです。


・参考:baserCMSの処理
BcUtil::unserialize($this->BcBaser->siteConfig['content_categories'])

baserCMSは検索に利用するカテゴリー一覧をシリアライズして(Base64エンコードを行い)文字列に変換し、
site_configsテーブルに格納しています。
これはそのカテゴリ一覧のデータを引き出す際に、シリアライズした文字列からあるべき連想配列へと戻すという処理です。


●テーマヘルパーを作りました
-----------------------------------------------------------------
Blogヘルパーにあるカテゴリの子カテゴリを返してくれる機能があるかと期待したのですがなかったので、
BlogCategoryモデルから必要な値を引っ張ってくるコードを書いてみました。
テンプレートに直書きすると見栄えもメンテナンス性も悪いのでテーマヘルパーにしています。

・app/webroot/theme/テーマ名/Helper/MyBlogCategoryHeper.php
<?php 
/**
 *  Myブログカテゴリーヘルパー
 *
 */
App::uses('AppHelper', 'View/Helper');

class MyBlogCategoryHelper extends AppHelper {

/**
 * ブログカテゴリーモデル
 * 
 * @var BlogCategory
 */
	private $BlogCategory;

/**
 * コンストラクタ
 *
 * @param object $View
 * @param array $settings
 * @return void
 */
	public function __construct(View $View, $settings = array()) {
		parent::__construct($View, $settings);
		if (ClassRegistry::isKeySet('Blog.BlogCategory')) {
			$this->BlogCategory = ClassRegistry::getObject('Blog.BlogCategory');
		} else {
			$this->BlogCategory = ClassRegistry::init('Blog.BlogCategory');
		}
	}

/**
 * IDで指定したカテゴリの子カテゴリを、検索に利用するselectタグのoptionのリストとして取得する
 * 
 * @param int $parent_id 親カテゴリのID
 * @return array $selectOptions
 */

	public function getSelectOptions($parent_id) {
		$conditions = array('parent_id' => $parent_id); //検索条件
		$fields = array('title'); //取得するフィールド名

		//カテゴリを取得
		$categories = $this->BlogCategory->find('all', array(
			'conditions' => $conditions,
			'fields' => $fields
		));

		// Hashクラスで連想配列を加工
		$selectOptions = Hash::combine($categories, '{n}.BlogCategory.title', '{n}.BlogCategory.title');
		return $selectOptions;
	}
}



・テンプレートでの使い方
※テーマディレクトリの中:app/webroot/theme/テーマ名/Elements/search.phpに設置するとbaserコアを書き換えることなく上書きできます
<?php $select_options = $this->MyBlogCategory->getSelectOptions(1) //引数には親カテゴリのIDを調べて入れてください ?>
<div class="section search-box">
	<?php echo $this->BcForm->create('Content', array('type' => 'get', 'action' => 'search', 'url' => $url)) ?>
	<?php echo $this->BcForm->input('Content.c', array('type' => 'select', 'options' => $select_options, 'empty' => 'カテゴリー: 指定しない ')) ?>
	<?php echo $this->BcForm->input('Content.q') ?>
	<?php echo $this->BcForm->submit('検索', array('div' => false, 'class' => 'submit_button')) ?>
	<?php echo $this->BcForm->end() ?>
</div>

Twitter: @n_1215

well > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/5/1 22:24
n1215様
初めまして、wellと申します。
この度は、私の不確定要素の多い質問に対して、
ご丁寧に、分析過程とヘルパーの作り方、使い方まで教えていただき、誠にありがとうございました。
オブジェクト指向プログラミングは、私は初心者で目で追ってなんとか理解する程度ですので
n1215様がご自身で書かれたプロセスはとても勉強になりました。ありがとうございます。

引用:

追記:下記でなんとかなると思って試してみたらそんなことありませんでした。申し訳ないです。
検索用のインデックスが入っているcontentsテーブルに、最上位カテゴリのデータだけしか登録されていないようので、
ほかの部分もごっそりいじる、ないしプラグインとして作成しないと辛そうです。

結論として以上の点を、n1215様に明確に示していただけましたので、私もなんとか気持ち的に一区切り出来そうです。
contentsテーブルが検索用のインデックスの出どころなんですね。

私もこの2日ぐらいxdebugで変数を追ったりして悪足掻きしていましたが、行き来するファイルの数が尋常じゃないのでw、
コアプログラムを根本から理解するのは無理だと悟り、とりあえず当初に考えた妥協案
引用:
親カテゴリーを全部削除して、カテゴリ1の子カテゴリーリストのみで
親カテゴリーを構成すれば、表示は可能ですが、
カテゴリー機能は非常に便利ですので、確実に複数回使いそうですし、
また、親カテゴリーとして、/category/を作成すると、
のちのち全体の条件分岐処理で、非常に重宝すると考えております。

カテゴリ1の子カテゴリのみを親カテゴリとして登録して、それらのリストをセレクトボックスに表示することができました。

また検索のセレクトボックスの件から少し離れる話題ですが、付随する話として、
分類に便利な/category/フォルダはなくなりましたが、
代わりに各カテゴリページ名に_(アンダースコア)をつけて条件分岐を働かせ、
ウィジェットに、各カテゴリページ専用のリストをPHPテンプレートを使って出すこととしました。
<?php
$thisUrl = $this->BcBaser->getUrl('', false);
echo $thisUrl;

if($thisUrl) {
    if(preg_match('{^/_}', $thisUrl)) {    
        switch(true){
            //カテゴリ1
            case preg_match('{^/_news}', $thisUrl):
                $pages = $this->BcBaser->getPageList(5);
            break;

            //カテゴリ2
            case preg_match('{^/_sports}', $thisUrl):
                $pages = $this->BcBaser->getPageList(6);
            break;

            //カテゴリ3
            case preg_match('{^/_economy}', $thisUrl):
                $pages = $this->BcBaser->getPageList(7);
            break;
                
            //カテゴリ4
            case preg_match('{^/_show_biz}', $thisUrl):
                $pages = $this->BcBaser->getPageList(8);
            break;

            //それ以外
            default:     
            break;
        }
    } else {
        $all_pages = $this->BcBaser->getPageList();
        $pages = array_slice($all_pages, 17, 4);
        //var_dump($pages);
    }
    if(isset($pages)) {
       foreach($pages as $page){
           echo '<li><a href="'.$page['url'].'">'.$page['title'].'</a></li>';
       }
    }
}
?>

以上のコードを修正しているうちに思い出しました。
そもそもなぜ、わざわざ親カテゴリとしてカテゴリ(/category/)を設定して、表示したいカテゴリリストを子カテゴリにしたのか?

n1215様も触れられていましたが、
blogヘルパーには、[getCategoryList] カテゴリーの一覧をリストタグで取得する、という関数がありますが、
固定ページには同様なものが見つからず、
少し似た関数で、BcBaserヘルパーに、
[getPageList] ページ機能で作成したページの一覧データを取得する、という関数があり、
引数を指定しなければ全ての固定ページの一覧データを取得し、
引数にページカテゴリに割り振られたNOを指定すれば、そのページカテゴリに属するすべての固定ページの一覧データを取得してくれます。

このことから例えばトップページには、親カテゴリのNOを指定するだけですべての子カテゴリページリストを出せました。
しかし、親カテゴリとしてカテゴリ(/category/)をなくしたことで、それが出来なくなりました。
その代用として、用いたのが、上のコードの下の部分です。
引用:

} else {
$all_pages = $this->BcBaser->getPageList();
$pages = array_slice($all_pages, 17, 4);
//var_dump($pages);
}

配列の一部を取得して配列で返す関数 array_slice()
引数の17は要素番号、4はそこから要素4つの意味です。
ちょっと苦しいですが、一応対応させることは出来ました。

以上で、なんとか当初に希望していた動作はしてくれるだろうとほっとしましたが、
また問題が一つ見つかってしまいました。

セレクトボックスに表示されるリストが、想定していた「配列順」ではなく、
どうやら「文字のコード番号の昇順」に表示されていることです。
これは、ウィジェットに乗せたリストの順序と異なりますので困ってしまいました。
これも解決は難しそうですね。

以上より、今回は、検索のセレクトボックスのリストについて、当初の質問と合わせて2点、困難な問題がわかりましたので、
フィードバック・改善要望 の方に投稿してみたいと思っております。

n1215様、誠にありがとうございました。











■ BaserCMS:3.0.6.1
■ サーバー名:CentOS 6.5 on VirtualBox(PHP 5.4.x / MySQL 5.5.x)
■ スマートURL:ON
■ 設置フォルダ: [ドキュメントルート]
■ PHPスキル:C
■ IDE:NetBeans7.4

n1215 > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/5/2 14:57
well 様

ブログのコードを載せましたが、固定ページのほうだったんですね。
いろいろ勘違いしていてすみません。

下記のようにしておけば、特にアンダースコアで判別せずとも問題ないかと思います。

<?php

//ウィジェットの管轄するカテゴリ一覧
$catNames = array('news', 'sports', 'economy', 'show_biz');

//固定ページかどうか判別
if($this->BcBaser->isPage()) {

	//現在のページが属するカテゴリの先祖カテゴリの最上位を取得(カテゴリに属していなければfalse)
	$rootCategory = $this->BcPage->getParentCategory(true);
	//var_dump($rootCategory);

	if($rootCategory && in_array($rootCategory['PageCategory']['name'], $catNames)) {
	//最上位カテゴリが存在し、かつ名前が$catNamesの値に含まれる場合はそのカテゴリのページ一覧を取得
		$pages = $this->BcBaser->getPageList($rootCategory['PageCategory']['id']);

	} else {
	//それ以外の場合は$catNamesで指定した各カテゴリのトップページを取得

		//検索条件
		$conditions = array(
			'Page.name' => 'index',
			'PageCategory.name' => $catNames
		);

		//取得フィールド
		$fields = array('url', 'title');

		//並び順
		$order = array('Page.id'=> 'asc');

		
		$pages = $this->BcPage->Page->find('all', array('conditions' => $conditions, 'fields' => $fields, 'order' => $order));

		//BcBaserヘルパーのgetPageListメソッドの戻り値と形式を統一
		$pages = Set::extract('/Page/.', $pages);
		
		//var_dump($pages);
	}

}
?>

<?php if(isset($pages)): ?>
<?php foreach($pages as $page) :?>
<li><a href="<?php $this->BcBaser->url($page['url']) ?>"><?php echo $page['title']?></a></li>
<?php endforeach ?>
<?php endif ?>

Twitter: @n_1215

well > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/5/2 20:55
n1215様
ご返信ありがとうございます。

引用:

ブログのコードを載せましたが、固定ページのほうだったんですね。
いろいろ勘違いしていてすみません。

こちらこそ、説明が足りなかったようで失礼しました。

引用:
下記のようにしておけば、特にアンダースコアで判別せずとも問題ないかと思います。

basercmsは関数がいろいろありますので、工夫次第でいろいろな解決法がつくれますね。

else文以下のコードは、cakephpをこなす方がよく駆使される、findを使ったデータ取得ですね。
動かしてみましたが、else側が通らなかったので少し考えてみましたが、
basercmsで各カテゴリページを作ってもそのindexページは作られないからかもしれませんね。
(的外れだったらスミマセン)

cakeのfindを使いこなせる方は検索プラグインも自作される方も多いんでしょうね。
n1215様、cakeも勉強させていただきまして誠にありがとうございました。

■ BaserCMS:3.0.6.1
■ サーバー名:CentOS 6.5 on VirtualBox(PHP 5.4.x / MySQL 5.5.x)
■ スマートURL:ON
■ 設置フォルダ: [ドキュメントルート]
■ PHPスキル:C
■ IDE:NetBeans7.4

n1215 > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/5/2 22:26
well 様

本当はbaserCMSで用意されているヘルパーを組み合わせて作れると後々のメンテナンスも楽なんですけどね。
Pageヘルパーあたりに機能が追加されると嬉しいところです。

検索に関してはネストしたカテゴリの情報をcontentsテーブルに格納する仕様になれば子カテゴリの検索にも対応できそうですが。
後方互換性を考えると難しいかもしれません。


引用:
else文以下のコードは、cakephpをこなす方がよく駆使される、findを使ったデータ取得ですね。
動かしてみましたが、else側が通らなかったので少し考えてみましたが、
basercmsで各カテゴリページを作ってもそのindexページは作られないからかもしれませんね。
(的外れだったらスミマセン)


ご推察のとおりです。各カテゴリのトップとしてindexページがあることを前提としたコードです。
カテゴリを作っただけでindexページが登録されていない場合にも対応させるには少々変更しないといけないですね。

自分もbaserCMSの仕様はまだまだ把握しきれていない部分があるので、
コアのHelperを参考にしたりModelのソースを読んだりしながら書いています。

Twitter: @n_1215

well > Re: 特定の親カテゴリーIDに属する子カテゴリーのリストで、[カテゴリ検索]は出来ないでしょうか? @ 2014/7/7 20:28
自己レスです。
個人的にbaserCMSのデータベースで事故り、多くのテーブルを手動で直す作業で難儀しましたが、
おかげでbaserCMSについてまた少し知識が向上したようで、上記の一つの課題が解決しました。
1行追加しただけですが、初めてコアを弄ることもあり、備忘録として記させていただきます。

引用:

セレクトボックスに表示されるリストが、想定していた「配列順」ではなく、
どうやら「文字のコード番号の昇順」に表示されていることです。
これは、ウィジェットに乗せたリストの順序と異なりますので困ってしまいました。
これも解決は難しそうですね。


検索窓の横につくカテゴリー検索のセレクトボックスですが、idの昇順にするには、
まず、
\lib\Baser\Model\Behavior\BcContentsManagerBehavior.php を
\app\Model\Behavior\BcContentsManagerBehavior.php に複写し、このファイルに以下の修正を加える。


<updateContentMetaメソッドを修正>

	public function updateContentMeta(Model $model) {
		$db = ConnectionManager::getDataSource('baser');
		$contentCategories = array();
		$contentTypes = array();
		if ($db->config['datasource'] == 'Database/BcCsv') {
			// CSVの場合GROUP BYが利用できない(baserCMS 2.0.2)
			$contents = $this->Content->find('all', array('conditions' => array('Content.status' => true),
 'order' => array('Content.id' => 'asc'))); // ●追加:'order' => array('Content.id' => 'asc')●
			foreach ($contents as $content) {
				if ($content['Content']['category'] && !in_array($content['Content']['category'], $contentCategories)) {
					$contentCategories[$content['Content']['category']] = $content['Content']['category'];
				}
				if ($content['Content']['type'] && !in_array($content['Content']['type'], $contentTypes)) {
					$contentTypes[$content['Content']['type']] = $content['Content']['type'];
				}
			}
		} else {
			$contents = $this->Content->find('all', array('fields' => array('Content.category'), 'group' => array('Content.category'), 'conditions' => array('Content.status' => true),
 'order' => array('Content.id' => 'asc'))); //●追加:'order' => array('Content.id' => 'asc')●
			foreach ($contents as $content) {
				if ($content['Content']['category']) {
					$contentCategories[$content['Content']['category']] = $content['Content']['category'];
				}
			}
			$contents = $this->Content->find('all', array('fields' => array('Content.type'), 'group' => array('Content.type'), 'conditions' => array('Content.status' => true)));
			foreach ($contents as $content) {
				if ($content['Content']['type']) {
					$contentTypes[$content['Content']['type']] = $content['Content']['type'];
				}
			}
		}

		$siteConfigs['SiteConfig']['content_categories'] = BcUtil::serialize($contentCategories);
		$siteConfigs['SiteConfig']['content_types'] = BcUtil::serialize($contentTypes);
		$SiteConfig = ClassRegistry::init('SiteConfig');
		return $SiteConfig->saveKeyValue($siteConfigs);
	}


以上でidの昇順になりました。

■ BaserCMS:3.0.6.1
■ サーバー名:CentOS 6.5 on VirtualBox(PHP 5.4.x / MySQL 5.5.x)
■ スマートURL:ON
■ 設置フォルダ: [ドキュメントルート]
■ PHPスキル:C
■ IDE:NetBeans7.4

ログイン
ユーザー名:
パスワード:


  新規登録 / パスワード紛失

検索

facebook
フォーラムで悩みが解決した場合など、よかったら「いいね!」をポチっとクリックしてください!質問の回答者や開発者の励みになります

フォーラムガイド


関連リンク

オンライン状況
22 人のユーザが現在オンラインです。 (22 人のユーザが フォーラム を参照しています。)

登録ユーザ: 0
ゲスト: 22