タイトルだけだと意味がわからないと思うので例をあげますね*1 。
「カテゴリ」と「オプション」の定義
例えば自動車。
自動車を購入しようとするとき、車種、排気量、色、トランスミッション、駆動方式など、いくつかの選択基準がありますよね?
これがタイトルの「カテゴリ」に当たる部分です。
そして、それぞれの選択基準に複数の選択肢があります。これがタイトルの「オプション」の部分。
これから話すことのために、これを配列にしてみます。
$data = array( 0 => array( 'Category' => '車種', 'Option' => array( 0 => 'セダン', 1 => 'ワンボックス', ), ), 1 => array( 'Category' => '排気量', 'Option' => array( 0 => '3ナンバー', 1 => '5ナンバー', 2 => '軽自動車', ), ), 2 => array( 'Category' => '色', 'Option' => array( 0 => '白', 1 => '黒', 2 => '赤', ), ), 3 => array( 'Category' => 'トランスミッション', 'Option' => array( 0 => 'オートマチック', 1 => 'マニュアル', ), ), 4 => array( 'Category' => '駆動方式', 'Option' => array( 0 => '2WD', 1 => '4WD', ), ), );
ちなみにこれ、CakePHPでCategoryにhasManyでOptionをアソシエーションした時の出力を参考に書きました。正確にはCategoryとOptionの中身も配列になりますが説明に不要なので省略してあります。
オプションの組み合わせを全通り取得する
自動車を選ぶときに、各カテゴリから1つずつオプションを選択していくと、2x3x3x2x2 = 72通りの選択が可能です。
これを取得する方法をメモしたいというのがこの記事の趣旨です(ようやくここまで来た)。
多分これ、きちんとプログラミングを学んだ方なら初歩的な処理だと思うのですが、僕の場合は独学でここまで来たので、こういう基礎的なロジックパターンが頭に入っていないんですよね。ですから、これを実現するのには少し骨が折れました。
今の僕にとって再帰処理を頭に描くのは、まだすんなりいかない感じですが、なんとか完成したソースがこちらです。
$dataは先程の配列です。
function saiki($categoryKey = 0, $spec = '') { global $data; // 先程定義した配列だと思ってください。 if (!empty($data[$categoryKey]['Option'])) { foreach ($data[$categoryKey]['Option'] as $option) { // ここで自分自身(saiki関数)を呼ぶ処理です。引数の$specに取得したカテゴリとオプションを加えて渡します。 saiki($categoryKey + 1, $spec.$data[$categoryKey]['Category'].":".$option."/"); } } else { // カテゴリの最後までたどったらechoします。 echo $spec.'<br />'; } }
と、こんな感じになりました。
実際に動かして確認したい方もいると思うので、動くサンプルを用意してみました*2 。
サンプルソース:saiki.zip
配列にカテゴリやオプションを加えて試してみてね。
応用について
アパレル関係のオーダーメイドECサイトの案件で、今回の処理を応用してオプションの全通りの組み合わせの商品を扱えるシステムを制作しました。
上記の処理を応用すると、オプションごとに振られた規定桁数のコードを組み合わせて商品コードや画像名を生成したり、オプションごとの金額を加算して組み合わせによって金額を変えるといったことも可能です。
また、処理を変えることで、例えば特定のカテゴリーから複数のオプションを選択したりすることも可能だと思います。
応用次第で色々な事に使えそうですね。
あとがき
なぜ恥を晒してこの記事を書こうと思ったかというと、この処理を書くにあたってディレクトリ探査や総当りの再帰処理のサンプルソースがとても参考になったからです。
ソースがあればそれを頭の中で想像して動かすことができるので、理解がしやすいですよね。
なので、僕も拙いながらここに残しておくことで誰かの役に立てたらいいなと思った次第です。*3
また、こうして記事にしたことで頭が整理できたのもよかったです。
再帰に対する苦手意識も少しは克服できました^^