
この前、廃番になった商品を非公開設定したのですが、
あまりにも多かったので
こちらでSQL文作って作業をしました。
このままだと、SQL使う人しか大量の品を変更する作業を
できなくなってしまいます。
簡単に誰でもできるようにするため
一括で公開・非公開と削除できるようにカスタマイズしました。
管理画面のイメージ

商品を一括で公開・非公開や削除する場合は
関連する商品も表示させれるといいので
商品検索機能がついている
商品管理→商品マスター画面で作業できるようにしました。
実装する内容はこちらです。
・商品検索結果一覧にチェックボタンを付ける
・チェックボタンがついているものだけ公開・非公開を切り替える
・チェックボタンがついているものだけ削除させる
LC_Page_Admin_Products.phpを編集
/data/class/pages/admin/products/LC_Page_Admin_Products.phpを編集します。
103行目辺りにある
switch ($this->getMode()) {
のswitch文の前後に以下を追加します。
※一部記述漏れがありましたので訂正しました。(2013.5.21)
※s.suzukiさんありがとうございます。
//表示件数を取得を追加
$n = $_POST["search_page_max"];
//表示件数をパラメータに渡す
$this->lfInitParam($objFormParam, $n);
switch ($this->getMode()) {
//一括ボタン追加ここから
case 'check_status':
for ($i=0; $i<$n; $i++){
if(isset($this->arrForm["product_id$i"]["value"])){
if($this->arrForm["check$i"]["value"] == 1){
if($this->arrForm["status$i"]["value"] == 1){
$cols['status'] = 2;
}elseif($this->arrForm["status$i"]["value"] == 2){
$cols['status'] = 1;
}
$col['update_date'] = 'CURRENT_TIMESTAMP';
$wheres = "product_id = ?";
$objQuery->begin();
$objQuery->update('dtb_products', $cols, $wheres, array($this->arrForm["product_id$i"]["value"]));
$objQuery->commit();
}
}
}
$objFormParam->convParam();
$objFormParam->trimParam();
$this->arrErr = $this->lfCheckError($objFormParam);
$arrParam = $objFormParam->getHashArray();
if (count($this->arrErr) == 0) {
$where = "del_flg = 0";
foreach ($arrParam as $key => $val) {
if($val == "") {
continue;
}
$this->buildQuery($key, $where, $arrval, $objFormParam, $objDb);
}
$order = "update_date DESC";
// 行数の取得
$this->tpl_linemax = $this->getNumberOfLines($where, $arrval);
// ページ送りの処理
$page_max = SC_Utils_Ex::sfGetSearchPageMax($objFormParam->getValue('search_page_max'));
// ページ送りの取得
$objNavi = new SC_PageNavi_Ex($this->arrHidden['search_pageno'],
$this->tpl_linemax, $page_max,
'fnNaviSearchPage', NAVI_PMAX);
$this->arrPagenavi = $objNavi->arrPagenavi;
// 検索結果の取得
$this->arrProducts = $this->findProducts($where, $arrval,
$page_max, $objNavi->start_row, $order, $objProduct);
// 各商品ごとのカテゴリIDを取得
if (count($this->arrProducts) > 0) {
foreach ($this->arrProducts as $key => $val) {
$this->arrProducts[$key]['categories'] = $objDb->sfGetCategoryId($val["product_id"], 0, true);
$objDb->g_category_on = false;
}
}
}
break;
case 'check_delete':
for ($i=0; $i<$n; $i++){
if(isset($this->arrForm["product_id$i"]["value"])){
if($this->arrForm["check$i"]["value"] == 1){
// 商品、子テーブル(商品規格)、顧客お気に入り商品の削除
$cols['del_flg'] = 1;
$cols['update_date'] = 'CURRENT_TIMESTAMP';
$wheres = "product_id = ?";
$objQuery->begin();
$objQuery->update('dtb_products_class', $cols, "product_id IN (SELECT product_id FROM dtb_products WHERE $wheres)", array($this->arrForm["product_id$i"]["value"]));
$objQuery->delete('dtb_customer_favorite_products', "product_id IN (SELECT product_id FROM dtb_products WHERE $wheres)", array($this->arrForm["product_id$i"]["value"]));
$objQuery->update('dtb_products', $cols, $wheres, array($this->arrForm["product_id$i"]["value"]));
$objQuery->commit();
// 件数カウントバッチ実行
$objDb->sfCountCategory($objQuery);
$objDb->sfCountMaker($objQuery);
}
}
}
$objFormParam->convParam();
$objFormParam->trimParam();
$this->arrErr = $this->lfCheckError($objFormParam);
$arrParam = $objFormParam->getHashArray();
if (count($this->arrErr) == 0) {
$where = "del_flg = 0";
foreach ($arrParam as $key => $val) {
if($val == "") {
continue;
}
$this->buildQuery($key, $where, $arrval, $objFormParam, $objDb);
}
$order = "update_date DESC";
// 行数の取得
$this->tpl_linemax = $this->getNumberOfLines($where, $arrval);
// ページ送りの処理
$page_max = SC_Utils_Ex::sfGetSearchPageMax($objFormParam->getValue('search_page_max'));
// ページ送りの取得
$objNavi = new SC_PageNavi_Ex($this->arrHidden['search_pageno'],
$this->tpl_linemax, $page_max,
'fnNaviSearchPage', NAVI_PMAX);
$this->arrPagenavi = $objNavi->arrPagenavi;
// 検索結果の取得
$this->arrProducts = $this->findProducts($where, $arrval,
$page_max, $objNavi->start_row, $order, $objProduct);
// 各商品ごとのカテゴリIDを取得
if (count($this->arrProducts) > 0) {
foreach ($this->arrProducts as $key => $val) {
$this->arrProducts[$key]['categories'] = $objDb->sfGetCategoryId($val["product_id"], 0, true);
$objDb->g_category_on = false;
}
}
}
break;
//一括ボタン追加ここまで
case 'delete':
// 商品、子テーブル(商品規格)、顧客お気に入り商品の削除
・・・
//以下デフォルトのまま
次にパラメーターを設定します。
lfInitParam関数に引数とfor文を追加します。
function lfInitParam(&$objFormParam, $page) {
// POSTされる値
$objFormParam->addParam('商品ID', 'product_id', INT_LEN, 'n', array('NUM_CHECK', 'MAX_LENGTH_CHECK'));
$objFormParam->addParam('カテゴリID', 'category_id', STEXT_LEN, 'n', array('SPTAB_CHECK', 'MAX_LENGTH_CHECK'));
$objFormParam->addParam('ページ送り番号','search_pageno', INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
$objFormParam->addParam('表示件数', 'search_page_max', INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
//追加ここから
// 追加POSTされる値 表示件数によって数を変える
for($i=0; $i < $page; $i++){
$objFormParam->addParam('チェックボタン', 'check'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
$objFormParam->addParam('チェックボタン用id', 'product_id'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
$objFormParam->addParam('ステータス', 'status'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
}
//ここまで
// 検索条件
$objFormParam->addParam('商品ID', 'search_product_id', INT_LEN, 'n', array('NUM_CHECK', 'MAX_LENGTH_CHECK'));
// ・・・
//以下同じ
【解説】※読み飛ばしても構いません。
//表示件数を取得を追加 $n = $_POST["search_page_max"];
まず、ポストされる表示件数を取得します。
取得した表示件数でパラメーターの個数を決めてパラメーターを作ります。
action内でこのように記述しておきます。
$this->lfInitParam($objFormParam, $n);
lfInitParam関数で引数に件数を入れられるようにします。
function lfInitParam(&$objFormParam, $page) {
次に件数を繰り返し回数としてパラメーターを作ります。
for($i=0; $i < $page; $i++){
$objFormParam->addParam('チェックボタン', 'check'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
$objFormParam->addParam('チェックボタン用id', 'product_id'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
$objFormParam->addParam('ステータス', 'status'.$i, INT_LEN, 'n', array('MAX_LENGTH_CHECK', 'NUM_CHECK'));
}
以上で、フォームから送られるデータの準備ができました。
次に、switch文です。
一括で公開・非公開するボタン名は 【check_status】
一括で削除するボタン名は 【check_delete】
としたのでswitch文にボタンごとの挙動を書きます。
for ($i=0; $i<$n; $i++){
if(isset($this->arrForm["product_id$i"]["value"])){
if($this->arrForm["check$i"]["value"] == 1){
if($this->arrForm["status$i"]["value"] == 1){
$cols['status'] = 2;
}elseif($this->arrForm["status$i"]["value"] == 2){
$cols['status'] = 1;
}
$col['update_date'] = 'CURRENT_TIMESTAMP';
$wheres = "product_id = ?";
$objQuery->begin();
$objQuery->update('dtb_products', $cols, $wheres, array($this->arrForm["product_id$i"]["value"]));
$objQuery->commit();
}
}
}
まず、表示件数10件で5件しかデータがない場合もあるので
変数の中身を確認します。
次に、値が入っていたら商品に関連した商品IDで「公開・非公開」を確認して
変数に入れ変えた値を代入して、
SQLで書き換えます。
$objFormParam->convParam(); ~ break;
までは、処理後に検索結果を表示させたいので
もともとswitch文のdelete以下にある部分で
必要なところだけ追加しました。
次に【check_delete】です。
for ($i=0; $i<$n; $i++){
if(isset($this->arrForm["product_id$i"]["value"])){
if($this->arrForm["check$i"]["value"] == 1){
// 商品、子テーブル(商品規格)、顧客お気に入り商品の削除
$cols['del_flg'] = 1;
$cols['update_date'] = 'CURRENT_TIMESTAMP';
$wheres = "product_id = ?";
$objQuery->begin();
$objQuery->update('dtb_products_class', $cols, "product_id IN (SELECT product_id FROM dtb_products WHERE $wheres)", array($this->arrForm["product_id$i"]["value"]));
$objQuery->delete('dtb_customer_favorite_products', "product_id IN (SELECT product_id FROM dtb_products WHERE $wheres)", array($this->arrForm["product_id$i"]["value"]));
$objQuery->update('dtb_products', $cols, $wheres, array($this->arrForm["product_id$i"]["value"]));
$objQuery->commit();
// 件数カウントバッチ実行
$objDb->sfCountCategory($objQuery);
$objDb->sfCountMaker($objQuery);
}
}
}
これも【check_status】とほぼ同じで
変数の値の確認後、
消す内容が同じなのでswitch文のdelete項目のdoDeleteメソッドを利用しています。
$objFormParam->convParam(); ~ break;
上記の部分は【check_status】と同じです。
【check_status】と【check_delete】のボタン名を登録
【check_status】と【check_delete】のボタン名を登録します。
/js/site.jsのファイルを編集します。
// モードとキーを指定してSUBMITを行う。
function fnModeSubmit(mode, keyname, keyid) {
switch(mode) {
case 'delete_category':
if(!window.confirm('選択したカテゴリとカテゴリ内のすべてのカテゴリを削除します')){
return;
}
break;
case 'delete':
if(!window.confirm('一度削除したデータは、元に戻せません。\n削除しても宜しいですか?')){
return;
}
break;
case 'delete_order':
if(!window.confirm('一度削除したデータは、元に戻せません。\n削除しても宜しいですか?\n\n※ 在庫数は手動で戻してください。')){
return;
}
mode = 'delete';
break;
case 'confirm':
if(!window.confirm('登録しても宜しいですか')){
return;
}
break;
case 'delete_all':
if(!window.confirm('検索結果をすべて削除しても宜しいですか')){
return;
}
break;
//項目追加
case 'check_status':
if(!window.confirm('チェックしたものをすべて表示/非表示しても宜しいですか')){
return;
}
break;
case 'check_delete':
if(!window.confirm('チェックしたものをすべて削除しても宜しいですか')){
return;
}
break;
default:
break;
}
document.form1['mode'].value = mode;
if(keyname != "" && keyid != "") {
document.form1[keyname].value = keyid;
}
document.form1.submit();
}
fnModeSubmitメソッドに追加します。
商品マスターにチェックボタンを追加
/data/Smarty/templates/admin/products/index.tplを編集します。
※一部記述漏れがありましたので訂正しました。(2013.5.21)
※s.suzukiさんありがとうございます。
・・・
<h2>検索結果一覧</h2>
<div class="btn">
<span class="attention"><!--検索結果数--><!--{$tpl_linemax}-->件</span> が該当しました。
<!--検索結果-->
<!--{if $smarty.const.ADMIN_MODE == '1'}-->
<a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('delete_all','',''); return false;">検索結果をすべて削除</a>
<!--{/if}-->
<a class="btn-tool" href="javascript:;" onclick="fnModeSubmit('csv','',''); return false;">CSV ダウンロード</a>
<a class="btn-tool" href="../contents/csv.php?tpl_subno_csv=product">CSV 出力項目設定</a>
<!-- ●ボタン追加始まり● -->
<a class="btn-tool" href="javascript:;" onclick="fnModeSubmit('check_status','',''); return false;">チェックしたものを「公開⇔非公開」切替</a>
<a class="btn-tool" href="javascript:;" onclick="fnModeSubmit('check_delete','',''); return false;">チェックしたものを削除</a>
<!-- ●ボタン追加終わり● -->
</div>
・・・
<!--{if count($arrErr) == 0 and ($smarty.post.mode == 'search' or $smarty.post.mode == 'delete' or $smarty.post.mode == 'check_status' or $smarty.post.mode == 'check_delete')}-->
・・・
<!--★★検索結果一覧★★-->
<form name="form1" id="form1" method="post" action="?">
・・・
<!--検索結果表示テーブル-->
<table class="list" id="products-search-result">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="8%">
<colgroup width="8%">
<colgroup width="8%">
<colgroup width="26%">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="5%">
<colgroup width="5%">
<tr>
<th rowspan="2">全て<br /><input type="checkbox" name="all_check" value="1" id="all_check" /></th>
<th rowspan="2">商品ID</th>
<th rowspan="2">商品画像</th>
<th rowspan="2">商品コード</th>
<th rowspan="2">価格(円)</th>
<th>商品名</th>
<th rowspan="2">在庫</th>
<th rowspan="2">種別</th>
<th rowspan="2">編集</th>
<th rowspan="2">確認</th>
<!--{if $smarty.const.OPTION_CLASS_REGIST == 1}-->
<th rowspan="2">規格</th>
<!--{/if}-->
<th rowspan="2">削除</th>
<th rowspan="2">複製</th>
</tr>
<tr>
<th nowrap><a href="#" onClick="lfnDispChange(); return false;">カテゴリ ⇔ URL</a></th>
</tr>
<!--{section name=cnt loop=$arrProducts}-->
<!--▼商品<!--{$smarty.section.cnt.iteration}-->-->
<!--{assign var=status value="`$arrProducts[cnt].status`"}-->
<tr style="background:<!--{$arrPRODUCTSTATUS_COLOR[$status]}-->;">
<!-- ●追加始まり● -->
<td class="check" rowspan="2">
<input type="checkbox" name="check<!--{$smarty.section.cnt.index}-->" class="check_box" value="1" />
<input type="hidden" name="product_id<!--{$smarty.section.cnt.index}-->" value="<!--{$arrProducts[cnt].product_id}-->" />
</td>
<!-- ●追加終わり● -->
<td class="id" rowspan="2"><!--{$arrProducts[cnt].product_id}--></td>
<td class="thumbnail" rowspan="2">
・・・
<!-- ●追加始まり● -->
<!--{* 表示 *}-->
<!--{assign var=key value=$arrProducts[cnt].status}-->
<td class="menu" rowspan="2">
<!--{$arrDISP[$key]}-->
<input type="hidden" name="status<!--{$smarty.section.cnt.index}-->" value="<!--{$arrProducts[cnt].status}-->" />
</td>
<!-- ●追加終わり● -->
・・・
<!--{/section}-->
</table>
<input type="hidden" name="item_cnt" value="<!--{$arrProducts|@count}-->" />
<!--検索結果表示テーブル-->
<!--{/if}-->
【解説】※読み飛ばしても構いません。
検索結果一覧の項目の場所にボタンを設置します。
次は、if文に
<!--{if count($arrErr) == 0 and ($smarty.post.mode == 'search' or $smarty.post.mode == 'delete' or $smarty.post.mode == 'check_status' or $smarty.post.mode == 'check_delete')}-->
条件式を追加します。
これを最初書いていなかったので、削除などしても
一覧が出なかったんですよ。
<!-- ●追加始まり● -->
<td class="check" rowspan="2">
<input type="checkbox" name="check<!--{$smarty.section.cnt.index}-->" class="check_box" value="1" />
<input type="hidden" name="product_id<!--{$smarty.section.cnt.index}-->" value="<!--{$arrProducts[cnt].product_id}-->" />
</td>
<!-- ●追加終わり● -->
ここの部分で結果項目に合わせた
商品IDとチェックボックスの値を取れるようにしておきます。
<!-- ●追加始まり● -->
<!--{* 表示 *}-->
<!--{assign var=key value=$arrProducts[cnt].status}-->
<td class="menu" rowspan="2">
<!--{$arrDISP[$key]}-->
<input type="hidden" name="status<!--{$smarty.section.cnt.index}-->" value="<!--{$arrProducts[cnt].status}-->" />
</td>
<!-- ●追加終わり● -->
次に、上記の部分で現在の表示か非表示かの状態をhiddenで持つようにして
POSTしたときの判断材料にします。
すべてチェックできるようにjQueryで制御します
1個1個チェックしていくのは面倒くさいので
全てチェックするというチェックボックスを作っています。
<th rowspan="2">全て<br /><input type="checkbox" name="all_check" value="1" id="all_check" /></th>
ここの部分です。
これをjQueryを使って全てチェックするをできるようにします。
/*商品検索のチェックボタン*/
$(function(){
$('#all_check:checkbox').click(function(){
if($('#all_check').attr('checked')) {
$('.check_box').attr('checked','checked');
}else{
$('.check_box').removeAttr('checked');
}
})
})
面倒くさいので同じindex.tplに記述しています。
EC CUBEの2.11.4はjQUery1.4.2なので
上記のjQueryでチェックのON/OFFができますが、
最新の1.9.1だとできません。
attrの記述が変わったようです。
これについては後日検証してみます。
【検証してみました】
jQuery チェックボックスの全チェック、外しを実装(修正版)
動作を確認します
後は動作を確認してみます。
ちなみに、削除したものを戻す場合は
dtb_productテーブルとdtb_product_classテーブルのdel_flgカラムを
【0】に戻せばも元に戻ります。
dtb_customer_favorite_productsテーブルは
del_flgのフラグがなく、データが削除されるので
dtb_customer_favorite_products使っている人は
削除の挙動の確認の時には
dtb_customer_favorite_productsに登録されていない商品で
挙動を確認してください。


はじめまして。
お世話になります。
この機能の実装を考えておりましたところ、たまたま検索でこちらの記事を発見し参考とさせて頂きました。
結果的には無事実装することができたのですが、
2箇所ほど修正が必要な箇所があったため、ご報告させていただきます。
ちなみに当方Ver2.11.0のため、そもそも記述が違っている可能性もありますが予めご了承ください。
①lfInitParam 呼び出し部分
// パラメータ情報の初期化
$this->lfInitParam($objFormParam, $n); //表示件数を引数に追加しました。
②index.tplにhidden値の追加
<input type="hidden" name="status” value=”” />
上記2箇所を修正することで、この機能を実装することができました。
以上、お役に立てれば幸いです。
タグ有効なのですね。失礼しました。
②の内容を一部全角で表記しました。
<input type="hidden" name="status” value=”” />
>s.suzukiさんへ
コメントありがとうございます。
少しでもお役に立てて本当にうれしいです。
ご指摘頂いた
①lfInitParam 呼び出し部分
②index.tplにhidden値の追加
これはブログに記述忘れでした。・・・内容訂正しました。
ご指摘ありがとうございました。
お役に立てて幸いです。
こちらもその後問題なく動作しており、おかげでメンテナンスが非常に楽になりました。
改めまして、お礼申し上げます。
また時々寄らせて頂きますね。
連投すみません。
全角も無視されてしまうようですね。。。
ようするにstatusの値を指定したhiddenが抜けていたため、
公開非公開変更が機能しておりませんでした。
$(‘#all_check:checkbox’).click(function(){
if($(‘#all_check’).prop(‘checked’)) {
$(‘.check_box’).prop(‘checked’,’checked’);
}else{
$(‘.check_box’).removeProp(‘checked’);
}
})
})
感じですべてチェックできました
>shuichiさん
コメントありがとうございます。
ブログに書き忘れていましたが、
propを使って原因の解消を行ったことがありました。
jQuery チェックボックスの全チェック、外しを実装(修正版)
しかし、removePropという方法は知りませんでした。
ありがとうございます。