OpenCartのエクステンションインストール&作成1

らら
らら

はじめに

OpenCartには、エクステンション(Extension)拡張の機能があります。

これは他のソフトウェアのプラグイン、アドオンの相当する機能です。

エクステンションはOpenCartのMARKETPLACEホームページで公開されており無料と有料があります。

管理画面のエクステンション(Extension)拡張機能画面からもアクセスが可能です。

また、エクステンションではOCModとvQmodの2つのモードがあるようです。

OpenCartの用語では、エクステンション、カタログ、インフォメーション、属性など少しわかりずらい部分があります。

エクステンション=プラグイン、アドオン

カタログ=商品登録

インフォメーション=ウェブページ

マーケットプレイス=エクステンション(拡張機能)販売サイト

OpenCart 1.5は、vQmodのみ

OpenCart 2.x,OpenCart 3.x からOCModが追加 OCModとvQmodの両方使用可能

また、有料のエクステンションには、ionCubeなどで暗号化されたものもあり、購入には、注意が必要です。

別途、ionCube Loaderなどインストールする必要があります。暗号化されたものは、ソースの変更ができないのでそのあたりを把握しておく必要があります。

欲しそうな機能

都道府県コード順表示 Japanese Zones Sort Order無料 こちらは完全にver2.0のみの対応でした。
テンプレートの住所部分を日本の住所形式に変更する Japanese Address Format 2.0.2無料 こちらは完全にver2.0のみの対応でした。テンプレートがtpl専用です。
メンテナンスカスタム、公開予定機能 CES Maintenance Mode Advance無料 Maintenance Mode Plus有料 $20
CSVでのインポート・エクスポート Export Sales (vQmod) Free 無料 Export All Data (vQmod) free 無料
新着・BLOG機能 Blog Module FREE 無料
フォームビルダー TMD Form Builder Pro有料 $20
割引機能 OC3 - Discounts Pack有料 $39.99
日本向け配送機能 マーケットプレイスになし
日本向け決済 (Amazon Pay、paypalは標準機能が付いています。)
税計算 切り捨て 四捨五入 切り上げ 現在はround関数を使った四捨五入 必要に応じて変更が必要

vqmodをインストールする

https://github.com/vqmod/vqmod

上記サイトよりvqmod-2.6.6-opencart.zipをダウンロードしてvqmodフォルダーをopencartのルートディレクトリにコピーします。admin,catalog,imageと同等の場所へコピーします。

筆者は結局インストールしませんでした。OCModでも同様にinstall.xmlで可能だったのでOCModで自作することにしました。


/vqmod/vqcache
/index.php
/admin/index.php

上記0755 or 0777 に変更

vqmodについて

xml形式で記載して、変更するファイルのパス+ファイル名を指定して、変更するプログラム行のプログラムコードを記載して。

その指定した行の後に、XMLで記載してある置き換えコードを追加します。

これは、実際のプログラムコードを修正するのではなく、もとのソースコードとは別に専用のキャッシュディレクトリにプログラムコードを追加したファイルが作成され、そちらのキャッシュに作成したプログラムが使用されます。なのでおおもとのプログラムコードに改行、空白など追加されると動作しないかもしれません。

試していませんが。。また、xmlで外部から投入されてしまうとあなたのドメイン/vqmod/installを実行するだけ、置き換えられるので該当フォルダーにIPなど制限をつける必要があるかもしれません。

vqmodについてはtpl,twigの置き換えもできるので、デフォルトテーマだけしか対応していないとかありそうです。vqmodの場合、バージョンが細かいところまであっているか確認が必要です。


vqmod
trim="(true|false)"
position="(replace|before|after)"
offset="(number)"

置き換えか、行の前に追加 、行の後に追加があるようです。

なので複数のエクステンションがあって、置き換えが行われた場合、1つしか動作しない場合があるかもしれません。

OCModで作ってみる

OCModでも同様にxmlの置換ができるようなので、こちらで作成

今回は、都道府県の並び順変更

こちらは、管理画面からアップロードでインストールする形式だったのでvqmodより良い感じがしたのでこちらにしました。

デフォルトはこんな感じ

opencart_ext01

まず、圧縮ファイル名を決めます。

形式はモジュール名称.ocmodなので下記のように


japanesezonesort.ocmod

このファイル名でinstall.xmlをzipで圧縮します。フォルダーは作成しません。フォルダー作って入れると動作しませんでした。

install.xmlをjapanesezonesort.ocmod.zip名でzipします。

デフォルトは名称でソートしているようなので、コードで並び変えました。01-47まで

ORDER BY nameをORDER BY codeに置換です。SQLは前回のインストール編で言語ファイルに同梱しているja.sqlを使います。

ja.sqlの一部 SQLコマンドで置き換えします。このcodeでソートするようにプログラムコードのSQL部分を置換します。

oc_の部分変更された場合、該当のプレフィックスを使用してください。


UPDATE oc_zone SET name='愛知県',code='23' WHERE zone_id=1657;
UPDATE oc_zone SET name='秋田県',code='05' WHERE zone_id=1658;
UPDATE oc_zone SET name='青森県',code='02' WHERE zone_id=1659;

install.xmlの内容


<?xml version="1.0" encoding="utf-8"?>
<modification>
    <name>Japanese Zone sort code</name>
    <code>Japanese_Zone_sort_code</code>
    <version>1.0.0</version>
    <author>omakase</author>
    <link>https://www.oamakse.net</link>
  <file path="admin/model/localisation/zone.php">
    <operation>
      <search><![CDATA[
      $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY name");
            ]]></search>
      <add position="replace">
        <![CDATA[
      $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY code");
        ]]>
      </add>
    </operation>
  </file>
  <file path="catalog/model/localisation/zone.php">
    <operation>
      <search><![CDATA[
      $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY name");
            ]]></search>
      <add position="replace">
        <![CDATA[
      $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY code");
        ]]>
      </add>
    </operation>
  </file>
</modification>

上記が作成できたら、install.xmlをjapanesezonesort.ocmod.zip名でZIP圧縮してアップロードします。

opencart_ext02

opencart_ext03

拡張機能で変更でインストール状態を確認します。正しくインストールできないとここにはリストされません。

翻訳時は、ただの変更だったんですけど。拡張機能の変更ってことなんですね。w

opencart_ext04

一度/storage/cacheの中を削除します。もしくは変更画面のリフレッシュでできます。

住所のページでブラウザーをリロードします。

プログラムが置換されたものは下記のフォルダーに作成されますので、フォルダ構造はcatalog,adminと同じ構造になります、

/storage/modification

並び変わりました!

opencart_ext05

テンプレートの住所部分を日本の住所形式に変更

下記のファイルを直接修正することにしました。ついでに郵便番号検索を追加します。設置が簡単なYubinBangoを使用します。

直接修正については、OCModとvQmodともに各エクステンションの確認が必要です。標準のエクステンションもあるので注意が必要です。


admin\view\template\customer\customer_form.twig
admin\view\template\sale\order_form.twig
catalog\view\theme\default\template\account\address_form.twig
catalog\view\theme\default\template\checkout\guest.twig
catalog\view\theme\default\template\checkout\guest_shipping.twig
catalog\view\theme\default\template\checkout\payment_address.twig
catalog\view\theme\default\template\checkout\register.twig
catalog\view\theme\default\template\checkout\shipping_address.twig

フォームの呼び出しは

{% if code == "ja" %} h-adr{% endif %}を追加


      <form action="{{ action }}" method="post" enctype="multipart/form-data" class="form-horizontal{% if code == "ja" %} h-adr{% endif %}">

フォーム項目

YubinBangoでは、入力のクラスにつけるだけで動作するので該当のclassに下記を追加する

form tag class="h-adr"

郵便番号はclass="p-postal-code"

都道府県はclass="p-region"

市区町村はclass="p-locality p-street-address p-extended-address"

国はclass="p-country-name"

先頭が大文字のJじゃないとだめなので、とりあえず固定で、input-countryとは連動していません。code == "ja"で固定しているのでとりあえず


<span class="p-country-name" style="display:none;">Japan</span>

jaの場合、並び替えで、それ以外は、以前のコードをつかう

例)\account\address_form.twig


{% if code == "ja" %}
<span class="p-country-name" style="display:none;">Japan</span>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-country">{{ entry_country }}</label>
            <div class="col-sm-10">
              <select name="country_id" id="input-country" class="form-control">
                <option value="">{{ text_select }}</option>
                {% for country in countries %}
                  {% if country.country_id == country_id %}
                <option value="{{ country.country_id }}" selected="selected">{{ country.name }}</option>
                  {% else %}
                <option value="{{ country.country_id }}">{{ country.name }}</option>
                  {% endif %}
                {% endfor %}
              </select>
              {% if error_country %}
              <div class="text-danger">{{ error_country }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-postcode">{{ entry_postcode }}</label>
            <div class="col-sm-10">
              <input type="text" name="postcode" value="{{ postcode }}" placeholder="{{ entry_postcode }}" id="input-postcode" class="form-control p-postal-code" />
              {% if error_postcode %}
              <div class="text-danger">{{ error_postcode }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-zone">{{ entry_zone }}</label>
            <div class="col-sm-10">
              <select name="zone_id" id="input-zone" class="form-control p-region">
              </select>
              {% if error_zone %}
              <div class="text-danger">{{ error_zone }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-city">{{ entry_city }}</label>
            <div class="col-sm-10">
              <input type="text" name="city" value="{{ city }}" placeholder="{{ entry_city }}" id="input-city" class="form-control p-locality" />
              {% if error_city %}
              <div class="text-danger">{{ error_city }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-address-1">{{ entry_address_1 }}</label>
            <div class="col-sm-10">
              <input type="text" name="address_1" value="{{ address_1 }}" placeholder="{{ entry_address_1 }}" id="input-address-1" class="form-control p-street-address p-extended-address" />
              {% if error_address_1 %}
              <div class="text-danger">{{ error_address_1 }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-address-2">{{ entry_address_2 }}</label>
            <div class="col-sm-10">
              <input type="text" name="address_2" value="{{ address_2 }}" placeholder="{{ entry_address_2 }}" id="input-address-2" class="form-control" />
            </div>
          </div>
{% else %}
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-address-1">{{ entry_address_1 }}</label>
            <div class="col-sm-10">
              <input type="text" name="address_1" value="{{ address_1 }}" placeholder="{{ entry_address_1 }}" id="input-address-1" class="form-control" />
              {% if error_address_1 %}
              <div class="text-danger">{{ error_address_1 }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-address-2">{{ entry_address_2 }}</label>
            <div class="col-sm-10">
              <input type="text" name="address_2" value="{{ address_2 }}" placeholder="{{ entry_address_2 }}" id="input-address-2" class="form-control" />
            </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-city">{{ entry_city }}</label>
            <div class="col-sm-10">
              <input type="text" name="city" value="{{ city }}" placeholder="{{ entry_city }}" id="input-city" class="form-control" />
              {% if error_city %}
              <div class="text-danger">{{ error_city }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-postcode">{{ entry_postcode }}</label>
            <div class="col-sm-10">
              <input type="text" name="postcode" value="{{ postcode }}" placeholder="{{ entry_postcode }}" id="input-postcode" class="form-control" />
              {% if error_postcode %}
              <div class="text-danger">{{ error_postcode }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-country">{{ entry_country }}</label>
            <div class="col-sm-10">
              <select name="country_id" id="input-country" class="form-control">
                <option value="">{{ text_select }}</option>
                {% for country in countries %}
                  {% if country.country_id == country_id %}
                <option value="{{ country.country_id }}" selected="selected">{{ country.name }}</option>
                  {% else %}
                <option value="{{ country.country_id }}">{{ country.name }}</option>
                  {% endif %}
                {% endfor %}
              </select>
              {% if error_country %}
              <div class="text-danger">{{ error_country }}</div>
              {% endif %} </div>
          </div>
          <div class="form-group required">
            <label class="col-sm-2 control-label" for="input-zone">{{ entry_zone }}</label>
            <div class="col-sm-10">
              <select name="zone_id" id="input-zone" class="form-control">
              </select>
              {% if error_zone %}
              <div class="text-danger">{{ error_zone }}</div>
              {% endif %} </div>
          </div>
{% endif %}

郵便番号検索は下記で

OpenCartでは、option value値はzone_idが使用されているのでYubinBangoでは、都道府県名で返ってくるので、そこの部分を

変換してセットしなおします。


{% if code == "ja" %}
<script src="https://yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
<script type="text/javascript"><!--
var pref_wk={};
(function(target){
  Object.defineProperty(target, 'value',{
    get : function(){
      return value;
    },
    set : function(x){
      if (x) {
        $.ajax({
          url: 'index.php?route=account/account/country&country_id=' + $('select[name=\'country_id\']').val(),
          dataType: 'json',
          success: function(json) {
            if (json['zone'] && json['zone'] != '') {
              for (i = 0; i < json['zone'].length; i++) {
                pref_wk[json['zone'][i]['name']]=json['zone'][i]['zone_id'];
              }
              $('#input-zone').val(pref_wk[x]);
            }
          },
          error: function(xhr, ajaxOptions, thrownError) {
//           alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
          }
        });
      }
      value = x;
    }
  })
})(document.querySelector('.p-region'))
//--></script> 
{% endif %}

切り替えたときはこんな感じ

opencart_ext06

opencart_ext07

税計算 デフォルトは四捨五入なので 下記のように修正すれば問題なさそう。

四捨五入のロジックはcs-cartの技術ページからのものです。


$decimals = 2;
切り上げ
$price = sprintf('%.' . $decimals . 'f', ceil($price));
切り捨て
$price = sprintf('%.' . $decimals . 'f', floor($price));
四捨五入
$price = sprintf('%.' . $decimals . 'f', round((double) $price + 0.00000000001, $decimals));

さいごに

全部紹介できるかとおもっていたら、はじめからエクステンションがつかえなかったので説明が長くなったので残りは別の機会に紹介します。

OpenCartのMARKETPLACEホームページでは、VQModかOcModか記載があり、対応バージョンが記載されているので、よく確認してから、購入等したほうがよいかも

あと、元ソースを変更できる機能は便利そうですが、同じ部分を別のエクステンションが使った場合どうなるのか、調べていません。cs-cartのhookでは優先順位ですべてのプラグインを処理してくれますがそのあたりが気になります。

送料も最近は一律で離島は、送料割引なしなどが多いかと思うので、改造はできそうです。アメリカのpostcodeの送料エクステンションがあるので、日本郵便 メール便など参考につくれそう。

CSVでのインポート・エクスポートも無料のものはvQmodなのでつかえるのか確認が必要です。

あと、数点無料のエクステンションを落としてみましたが、日本語対応はないので別途languageディレクトリに自分で日本語ファイルを作るしかなそそうです。

たぶんここまでで、やっと日本語版にできるまでに来た感じですね。個人的にはCSVでのインポート・エクスポートが標準でないのは痛い感じがします。あと新着、ブログのあたり。

在庫の管理など、yahoo、楽天、アマゾンなど在庫連動させているところも多いのでそのあたりも・・・

ECCUBEは、のしなど標準であり、ただ、それ以外はアドオンを購入などしていると10万ぐらいは、アドオン代掛かったりするので、CS-CARTは無料がなくなり標準で10万ぐらい、マーケットプレイスで55万ぐらいなので微妙なとこですね。

追記

install.xmlでOpenCartのエクステンションインストール&作成2の作業中に気が付いた点 このままだと多言語もかわってしまうので

日本語だけの場合は修正は必要ないかと・・。実際codeは英数字の頭文字がはいっているので影響はないが、2番目の文字でソートされないと思います。

置き換え側を下記に変更


			if($country_id == 107) {
				$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY code");
			} else {
				$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY name");
			}

関連記事

OpenCartのエクステンションインストール&作成3

次の記事はこちらから
https://www.omakase.net/blog/2021/08/opencart-ext2.html

OpenCartのインストール

前回の記事はこちらから
https://www.omakase.net/blog/2021/07/opencart.html

OpenCartのエクステンションインストール&作成3

記事はこちらから
https://www.omakase.net/blog/2021/08/opencart-ext3.html

OpenCartのエクステンションインストール&作成4

記事はこちらから
https://www.omakase.net/blog/2021/08/opencart-ext4.html

OpenCartのエクステンションインストール&作成5

記事はこちらから
https://www.omakase.net/blog/2021/08/opencart-ext5.html

関連記事