Labo

EC-CUBE3 分室

【3.0.14】取得データの項目名について(Doctrine)

2017年03月25日 / 投稿者名:chiharu


取得データの項目名について少し記載したいと思います。
今回はクライアントから相談?された内容が基になります。
 
相談された内容としてはよくあると云えばよくある内容です。
 「商品一覧で商品コード順にソートしたい」
プラグインにて対応する場合は、結構大変ですがコアファイルを変更する方法だと以外に簡単にできます。
 /src/Eccube/Repository/ProductRepository.php
上記のファイルの「getQueryBuilderBySearchData」を変更する形ですね。
 ※ バージョンアップの時は注意が必要ですが・・・

    /**
     * get query builder.
     *
     * @param  array $searchData
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function getQueryBuilderBySearchData($searchData)
    {
        $qb = $this->createQueryBuilder('p')
            ->andWhere('p.Status = 1');

        // category
        $categoryJoin = false;
        if (!empty($searchData['category_id']) && $searchData['category_id']) {
            $Categories = $searchData['category_id']->getSelfAndDescendants();
            if ($Categories) {
                $qb
                    ->innerJoin('p.ProductCategories', 'pct')
                    ->innerJoin('pct.Category', 'c')
                    ->andWhere($qb->expr()->in('pct.Category', ':Categories'))
                    ->setParameter('Categories', $Categories);
                $categoryJoin = true;
            }
        }

        // name
        if (isset($searchData['name']) && Str::isNotBlank($searchData['name'])) {
            $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);

            foreach ($keywords as $index => $keyword) {
                $key = sprintf('keyword%s', $index);
                $qb
                    ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR NORMALIZE(p.search_word) LIKE NORMALIZE(:%s)', $key, $key))
                    ->setParameter($key, '%' . $keyword . '%');
            }
        }

        // Order By
//        // 価格低い順
//        $config = $this->app['config'];
//        if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['product_order_price_lower']) {
//            //@see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
//            $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
//            $qb->innerJoin('p.ProductClasses', 'pc');
//            $qb->groupBy('p');
//            // postgres9.0以下は, groupBy('p.id')が利用できない
//            // mysqlおよびpostgresql9.1以上であればgroupBy('p.id')にすることで性能向上が期待できる.
//            // @see https://github.com/EC-CUBE/ec-cube/issues/1904
//            // $qb->groupBy('p.id');
//            $qb->orderBy('price02_min', 'ASC');
//            $qb->addOrderBy('p.id', 'DESC');
//            // 価格高い順
//        } else if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['product_order_price_higher']) {
//            $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
//            $qb->innerJoin('p.ProductClasses', 'pc');
//            $qb->groupBy('p');
//            $qb->orderBy('price02_max', 'DESC');
//            $qb->addOrderBy('p.id', 'DESC');
//            // 新着順
//        } else if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['product_order_newer']) {
//            // 在庫切れ商品非表示の設定が有効時対応
//            // @see https://github.com/EC-CUBE/ec-cube/issues/1998
//            if ($this->app['orm.em']->getFilters()->isEnabled('nostock_hidden') == true) {
//                $qb->innerJoin('p.ProductClasses', 'pc');
//            }
//            $qb->orderBy('p.create_date', 'DESC');
//        } else {
//            if ($categoryJoin === false) {
//                $qb
//                    ->leftJoin('p.ProductCategories', 'pct')
//                    ->leftJoin('pct.Category', 'c');
//            }
//            $qb
//                ->addOrderBy('p.id', 'DESC');
//        }

// ----------------------------------------------------------
// 上記の通常の表示順をコメントアウトします。
// ここに指定したいオーダー順を記述すると変更できます。
// ----------------------------------------------------------

        return $qb;
    }

 
しかし、すでに試したけれどエラーが出て動かないとのことでした。
エラーログを追いかけるのも大変なため、どうして良いのか困ってご相談頂いたようです。
EC-CUBE2系統は触っておられますので大丈夫かと思いましたがどこが問題なんでしょうか。
 
お聞きしてみると以下の記述をして頂いていたようです。

        $qb->addSelect('MIN(pc.product_code) as HIDDEN product_code_min');
        $qb->innerJoin('p.ProductClasses', 'pc');
        $qb->groupBy('p');
        $qb->orderBy('product_code_min', 'ASC');

ぱっと見は問題無く動きそうです。
でも、残念ながらこれはクライアントさんが仰る通りエラーになるんです。
データベースも理解して居られるクライアントさんの為起きた問題とも云えます。
 
実際には以下のように記述して頂ければ動くようになります。

        $qb->addSelect('MIN(pc.code) as HIDDEN product_code_min');
        $qb->innerJoin('p.ProductClasses', 'pc');
        $qb->groupBy('p');
        $qb->orderBy('product_code_min', 'ASC');

変更内容としては1行目の部分ですが、「MIN(pc.product_code)」→「MIN(pc.code)」に変更しました。
無事に動作を確認して頂き問題無いとの判断を頂けました。
 
さて、問題が解決した上で何が問題かをここに記載したいと思います。
今回の問題部分の「MIN(pc.product_code)」→「MIN(pc.code)」に関してなんですが、クライアントさんはデータベースを確認されています。
その際に「dtb_product_class」の「product_code」が対象のフィールドと判断して対応されています。
概ね問題は無いのですが、EC-CUBE3系統は Symfony2 を使用しているためひと手間必要なのです。
 
実際には何が必要かと云うと Doctrine の確認です。
最初は「・・・Doctrine て何!?」てなりますが簡単に言うと、PHP と データベースの橋渡しを行ってくれるものです。
これのおかげでデータベースの変更がやり易くなると云うことです。
まぁ、便利かどうかは置いておいて、EC-CUBE3 に関してはデータベースとの取次に Doctrine を使用していると云うことを理解しておかないといけません。
 
EC-CUBE3 の場合、標準となる Doctrine の設定は以下のフォルダに保存されています。
 /src/Eccube/Resource/doctrine
今回の場合「dtb_product_class」ですので「Eccube.Entity.ProductClass.dcm.yml」が対象になります。
該当のファイルを確認してみると以下のような記載があります。

~ 中略 ~
        code:
            type: text
            column: product_code
            nullable: true
            fixed: false
~ 中略 ~

ありました、この部分ですね。
いろいろ書かれていたり省略されているため一概には云えませんが、今回に関してはこの部分が肝心の部分です
先ほど変更した「MIN(pc.product_code)」→「MIN(pc.code)」に当たる部分ですが、簡単に言うと「product_code」を「code」と云う名称として扱う事を記述されています。
つまり、データベースの項目名をそのまま使用せずに「code」にしないとエラーが出る原因はここです。
 ※ 定義名と項目名が一致している場合「culumn」が設定されていないので注意が必要です。
 
と云うことで問題解決につながるかと思います。
まぁ、私自身が Doctrine の定義ファイルの場所をよく忘れるため備忘録的に記載しておきたいと思います。
 
 
ちなみに件のクライアントさんの所では、主担当者の「やっぱり指定した順に並んで欲しい」とのご要望の為プラグイン導入で対応したそうです(笑)
何はともあれ無事に納品に至りそうで良かったです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

* Copy This Password *

* Type Or Paste Password Here *

*

コメント欄にコードを挿入したい場合は、[php][/php] を使ってください。