現在対応している案件で CS‑Cart(マーケットプレイス版 / Multi‑Vendor)を使っています。マーケットプレイス版では管理画面が「運営会社用」と「出品者用」に分かれているのですが、社内で次の要望がありました。
- 出品者管理画面のみにスタイルを適用したい
- コアファイルは変更したくない
そこで、コアファイルを触らずにアドオンを作成して、出品者管理画面にのみ読み込まれる css を用意しました。今回はその実装内容とポイントをまとめてご紹介します。
ちなみに、社内でもAI使用の推進が行われているということもあり、今回Cursorを使用してAIペアプログラミングでの実装を進めました。進め方(ワークフロー)と得られた効果は以下になります。
【ワークフロー】
・問題を自然言語で提示し、Cursor に実装案を提示してもらう
・生成されたコードをローカルで実行・テストし、動作確認を行う
・「実装内容の説明」をしてもらって確認しながら、不具合など必要に応じて適宜指示を出し改善する
・最終的に人間がレビューして品質担保
【得られた効果】
・実装速度が向上し、反復サイクルが短縮された
・単純ミスや型の不整合が事前に減り、レビュー負荷が軽減
個人的な感想としましては、やはり「実装速度向上」の一言になるかと思います。
それでは、まずは実際に作成したCS‑Cartアドオンの構成と役割について書きたいと思います。
.
├── app/
│ └── addons/
│ └── vendor_styles/
│ ├── addon.xml 1.
│ ├── init.php 2.
│ └── func.php 3.
├── design/
│ └── backend/
│ ├── css/
│ │ └── addons/
│ │ └── vendor_styles/
│ │ └── styles.css 4.
│ └── templates/
│ └── addons/
│ └── vendor_styles/
│ └── hooks/
│ └── index/
│ └── styles.post.tpl 5.
└── var/
└── langs/
├── ja/
│ └── addons/
│ └── vendor_styles.po 6.
└── en/
└── addons/
└── vendor_styles.po 7.
今回の css は管理画面用に design/backend/… に配置しています。管理画面のテンプレートやアセットとして読み込みされるため、ストアフロント(ユーザー向けページ)には影響しません。ストアフロントに反映させたい場合は、以下のようにテーマ配下
- design/themes/<テーマ名>/css
- design/themes/<テーマ名>/templates
- design/themes/responsive/…
に配置して読み込みする必要があります。
(番号を振りましたが)それぞれの役割について概要は以下の通りです。
- アドオンのメタ情報を記述する(必須)
- フックの登録や初期化処理を記述する(今回は空)
- アドオンの関数群(フックハンドラや、テンプレートから呼ぶヘルパーなど)を記述する
- 出品者管理画面専用の css
- フック index:styles.post を利用して出品者管理画面のときのみ styles.css を読み込むテンプレート
- 日本語用の言語エントリ(名前 / 説明)
- 英語用の言語エントリ(名前 / 説明)
次に、1. 〜 7. の実装内容について説明します。
- addon.xml
<?xml version="1.0"?>
<addon scheme="3.0">
<id>vendor_styles</id>
<name>出品者管理画面専用スタイル</name>
<description>出品者管理画面にのみカスタムCSSを適用します。</description>
<version>1.0.0</version>
<priority>4294967294</priority>
<position>0</position>
<default_language>en</default_language>
</addon>
アドオンのメタ情報を記述しています。メタ情報は色々と種類があるので詳細は割愛しますが、例示すると以下のようなものがあります。
基本メタ情報
・id — アドオン識別子(必須的に使用)
・name — 表示名(多言語で直接書かれることあり)
・description — 説明文
・version — バージョン
・priority — 読み込み優先度(数値)
・position — 表示順などの位置
・default_language — デフォルト言語コード
・status — active / disabled 等
・unmanaged — 管理対象外フラグ(例: 1)
・has_icon — アイコン有無フラグ(例: Y)
・supplier — 供給者名
・supplier_link — 供給者 URL
・auto_install — 自動インストール対象エディション(例: MULTIVENDOR,ULTIMATE)
今回はアドオンインストール時に自動で有効化されないようにあえてauto_installを記述していなかったり、アドオン用のアイコンファイルは準備していなかったのでhas_iconを記述していなかったりします。
2. init.php
役割の概要にもある通り、今回は空です。
3. func.php
<?php
if (!defined('BOOTSTRAP')) { die('Access denied'); }
// Reserved for hooks if needed in future
この一行は、ファイルがCS‑Cartの共通初期化(通常は index.php や init.php )を経由して読み込まれた場合にのみ中身を実行するための簡単な安全チェックになります。
CS‑Cartの共通初期化時には、以下が設定されるため
define('BOOTSTRAP', true);
その場合はファイル内の処理が通常通り動作しますが、ブラウザなどから直接このPHPファイルへアクセスされた場合には BOOTSTRAP が未定義となり、処理を止めて不要な実行を防ぎます。
4. styles.css
出品者管理画面に適当したいスタイルを記述
詳細の内容は割愛しますが、cssのスタイルを記述します。
5. styles.post.tpl
{if $auth.user_type == 'V'}
<link rel="stylesheet" href="{$config.current_location}/design/backend/css/addons/vendor_styles/styles.css" data-ca-external="Y" />
{/if}
index.styles.post フックを使用して、出品者の時に styles.css を読み込みします。今回の場合ここが肝になるので、以下説明が長くなります・・・。
data-ca-external=”Y” をつけた <link> タグはCS‑Cartのアセット結合処理の対象とはならず、結合済みの css の後にそのままブラウザに読み込まれます。これによりテーマや他アドオンが生成する結合 css に干渉せず、後から確実に上書きしたいスタイルを適用できます。今回はフック index:styles の「post」位置で <link> タグを出力しているため、コア側のスタイル出力の直後に読み込まれ、順序が重要な上書き用途に向いた方法となります。
ただし、結合処理の外に出すことが「常に最後に読み込みされる」ことを完全に保証するわけではありません。(他のアドオンが同様に外部リンクを出している場合などは互いに上書きされる可能性があります。)必要に応じてスタイルのセレクタの詳細度を上げるか、読み込み順を確認する必要があります。
次にフックの話について説明しますと、実際のフック呼び出しはコアファイル側の styles.tpl にあります。該当箇所は以下です。
{hook name="index:styles"}
このフック領域の中でコアのスタイルが出力されるので、アドオン側で同名のフックテンプレートを用意すれば、自動的にその領域に差し込まれます。アドオン側のファイル配置(命名)と読み込みルールは次の通りです。
分かりやすくまとめると:
・フック本体の「直前(pre)」に挿入される
design/backend/templates/addons/<addon>/hooks/<controller>/<hookname>.pre.tpl
・フック本体の「直後(post)」に挿入される → 今回の場合はこれ
design/backend/templates/addons/<addon>/hooks/<controller>/<hookname>.post.tpl
・フック本体を丸ごと置き換える
design/backend/templates/addons/<addon>/hooks/<controller>/<hookname>.override.tpl
これにより、コア側のスタイルが出力された後にアドオンのスタイルを差し込むということを実現しています。
補足ですが、コア側では同じフック内で
{if $smarty.const.ACCOUNT_TYPE === "vendor"}
のような条件を使用している可能性もあるため、アドオン側のテンプレートで今回のように
{if $auth.user_type == 'V'}
・
・
・
{/if}
のような判定条件を入れることで、読み込みを特定のユーザー(今回は出品者)に限定するなど、コードで制御することが可能です。
ちなみに、これは少々愚痴になりますがCS‑Cartには色々なところにフックが存在するのですが、探すのがちょっと大変・・・です。
6. vendor_styles.po
msgid ""
msgstr ""
"Project-Id-Version: cs-cart-latest\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
"Plural-Forms: nplurals=1; plural=0;\n"
msgctxt "Addons::name::vendor_styles"
msgid "Vendor styles (vendor panel only)"
msgstr "出品者管理画面専用スタイル"
msgctxt "Addons::description::vendor_styles"
msgid "Loads custom styles only for the vendor (company) admin panel"
msgstr "出品者管理画面にのみカスタムCSSを適用します。"
7. vendor_styles.po
msgid ""
msgstr ""
"Project-Id-Version: cs-cart-latest\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Language-Team: English\n"
"Language: en\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgctxt "Addons::name::vendor_styles"
msgid "Vendor styles (vendor panel only)"
msgstr "Vendor styles (vendor panel only)"
msgctxt "Addons::description::vendor_styles"
msgid "Loads custom styles only for the vendor (company) admin panel"
msgstr "Loads custom styles only for the vendor (company) admin panel"
6. と 7. は言語ファイルになります。以下のように構成されています。
・ヘッダ(最初の空の msgid/msgstr ブロック)
ファイル全体のメタ情報(Project-Id-Version, Content-Type, Language, Plural-Forms など)が入る。
・msgctxt(任意)
文脈(context)を与えるためのフィールド。CS‑Cart では「Addons::name::vendor_styles」や「Addons::description::vendor_styles」のように使われる。
・msgid
翻訳元の文字列(通常は英語)。
・msgstr
翻訳後の文字列(日本語など)。空欄なら未翻訳扱い。
以上で一通りの説明となります。
今回のアドオンは、フックによるテンプレート差し込みと条件分岐、外部リンク読み込みを組み合わせることで、コアファイルを触らずに出品者管理画面だけにスタイルを当てられるようにしました。今回の作成でフックについての理解が深まったと個人的には思っています。
※今回の実装はマーケットプレイス版前提での実装となります。(標準版では出品者管理が機能として存在しないので)