Memo

メモ > 技術 > CMS: ECCube

■ECCube4
ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/ EC-CUBE4 理想のショップは作れる"あなたのための" EC-CUBEできました https://www.ec-cube.net/product/4.0/ EC-CUBE4 管理・運用マニュアル | shiro8 https://www.shiro8.net/manual4/v40x/ EC-CUBE 4.0 開発者向けドキュメント - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/ EC-CUBE1系・2系・3系・4系の違い - Qiita https://qiita.com/nanasess/items/dc8407c48a3dcf982ea2 以下は最近の記事で、かつ初期構築の基本が網羅されている ECCubeの基本を把握するのには良さそう さくらのレンタルサーバにてEC-CUBE4を使って格安でオンラインショップを作る(第一回) | さくらのナレッジ https://knowledge.sakura.ad.jp/25102/ さくらのレンタルサーバにてEC-CUBE4を使って格安でオンラインショップを作る(第二回) | さくらのナレッジ https://knowledge.sakura.ad.jp/25476/ さくらのレンタルサーバにてEC-CUBE4を使って格安でオンラインショップを作る(第三回) | さくらのナレッジ https://knowledge.sakura.ad.jp/26060/ ■ECCube4.1 バージョン4.1で大きな改修が行われている 2021年9月28日に正式リリースされた ・セキュリティ機能の強化 ・SEO対策可能範囲の拡張 ・プラグイン10個の同梱 ・Symfony 4.4 へのアップデート ・プラグインインストーラーの負荷軽 ・WebAPIによるカスタマイズ性向上 株式会社イーシーキューブ、理想のECサイトを実現するEC-CUBE4系の最新版「EC-CUBE4.1」を提供開始。セキュリティ強化、SEO強化、WebAPIによるカスタマイズ性向上など189件の機能追加・修正に対応|ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/press/detail.php?press_id=273 ただし利用する場合、使いたいプラグインが4.1に対応しているかどうかは確認しておく ■ECCube4.2 バージョン4.2でPHP8.1とSymfony5.4に対応される ただし4.2と4.1に互換性は無いとのこと EC-CUBE 4.2 Roadmap - Issue #5356 - EC-CUBE/ec-cube - GitHub https://github.com/EC-CUBE/ec-cube/issues/5356 ■Symfony EC-CUBE4はSymfony3.4をベースに作られている 動作は重い EC-CUBEはなぜSymfonyを選んだのか - Qiita https://qiita.com/chihiro-adachi/items/d1293ec08a5d004fac32 Symfony Book | SymDoc - PHP フレームワーク Symfony3 日本語ドキュメント Wiki http://symdoc.kwalk.jp/doc/book/start Symfony のインストールと設定 | SymDoc - PHP フレームワーク Symfony3 日本語ドキュメント Wiki http://symdoc.kwalk.jp/doc/book/installation 超入門 Symfony3 | シムノート http://symnote.kwalk.jp/public/learning_symfony ■規約 EC-CUBE標準規約 - EC-CUBE Trac http://svn.ec-cube.net/open_trac/wiki/EC-CUBE%E6%A8%99%E6%BA%96%E8%A6%8F%E7%B4%84 上記は古そうだが参考にはできそう EC-CUBE 4.0 のものは見つけられなかった ■データベース定義 EC-CUBE4開発リファレンス http://ec4.umebius.com/ 上記にデータベース定義一覧があったが、2021年2月ごろに削除されたみたい (WebArchive https://web.archive.org/web/20210122084325/http://ec4.umebius.com/ には一応ある) …だったが、2021年7月に確認すると復活していた。ドメインの更新ミスなどがあったのかもしれない また、以下にER図が作られているので参考にはできそう EC-CUBEの覚書 | ワクガンス https://amaraimusi.sakura.ne.jp/note_prg/ec_cube/note.html 以下は自分メモ 各テーブルには以下の接頭語が付いている 自分でテーブルを追加する場合も、このルールに基づいておくと良さそう mtb_ ... マスタデータ(例: mtb_pref) dtb_ ... データテーブル(例: dtb_order_detail) plg_ ... プラグイン(例: plg_vt4g_order_payment) ■オーナーズストア ECCube自体は無料だが、プラグインは有料のものが多い ECCubeの導入は結局のところ、結構なお金がかかるかもしれない オーナーズストア | ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/owners/ ■その他のシステム CS-Cartというものがあるらしい どういうものかは要検証 マーケットプレイス&ECサイト構築ならCS-Cart | 実績35,000件超 https://cs-cart.jp/ CSカートの評判は?【越境ECに強いオープンソース】 | ECシステムの比較ブログ http://ec-pkg-asp.com/cs-cart/
■仕事で使う場合の注意
仕様決めの際、以下の点などに注意する(「ECCubeの微妙な部分まとめ」とも言える) ・ユーザ側は、モックでもいいのでひととおりの流れを確認してもらう。後から注文に関する変更を行うのは大変 できればECCubeが実際に動く環境を渡し、管理画面含めてしばらく触ってもらう 「有名なプログラムだから、痒いところには大抵手が届くのだろう」と思っていると大抵届かないので苦労する 最初の段階で渡すか、ある程度仕様が固まってきた段階で「運用をイメージしてひととおり触ってください」として渡すかは考える 特に「ECCubeは触ったことがありません」というクライアントの場合、早い段階で少しでも触ってもらい、ここに記載したような注意を連携しておく ・商品の規格として色やサイズを設定できるが、1商品に対して設定できるのは2つまで それ以上の対応が必要な場合、 別の商品として登録するか、 以下などを使って価格に連動したオプション項目を追加するか、 https://www.ec-cube.net/products/detail.php?product_id=1787 別の商品としてカテゴリやタブで分類するか …などが考えられる ・商品の規格として色やサイズを設定すると、商品一覧では同一の商品とみなされる つまり商品に「赤」「青」「黒」の規格を設定していたとして、商品一覧ではバラバラに表示されない 「別々に表示したい」「商品を多く見せたい」などの場合、規格ではなく別々の商品として登録する必要がある ただし、それはそれでメンテナンスに難がある(例えば商品の説明文を編集したければ、すべての商品に対して作業する必要がある) ・注文管理の「対応状況」などについても「ECCubeのレールに乗っておけばいいだろう」と思いきや、 「入金待ちのステータスがないので対応しづらい」 「間違って入金済みにすると戻せない」 「入金日を変更できない」 など、問題になるかもしれない事項が多々あるので注意 実際の運用に当てはめて、受注から発送や返品までの流れをよく考えてから制作に入る https://www.shiro8.net/manual4/v40x/order/index.html 対応状況をカスタマイズする方法自体は、このファイル内の「カスタマイズ: 対応状況の調整」を参照 ・操作ミスを戻せないのは決済絡みもあってある程度は仕方ないのだと思われるが、 いざというときに慌てないようにミスしたあとの流れは確認しておきたい このファイル内の 「運用中のトラブル対応」の「入金済みデータの入金キャンセル」「入金済みデータの支払い方法変更」「発送後のキャンセル」と 「プラグインの導入: ECCube VeriTrans4G決済プラグイン(4.0系)」の「決済キャンセルへの対応」 も参照 ・登録項目を追加したら、検索対象にもしたくなるかもしれない。CSVの入出力にも影響する可能性がある …など項目の追加はなかなか影響が大きいので、自力で追加しようとせずに、まずはプラグインで対応できないかを確認する方がいいかもしれない ・標準機能としてお問い合わせフォームがあるが、お問い合わせ内容はメールで送信されるのみ 管理画面から確認できたり返信できたり、という機能は無い ・pageno=2 のように指定すれば次のページが表示されるが、pageno=99 のように指定すると仮に商品が存在しなくてもページにはアクセスできる このページはGoogleにもクロールされ、検索結果にも表示される可能性がある 少しでも商品への到達率を上げるため、 「商品が無ければ、もしくはpagenoが指定されていれば、noindexを出力して検索エンジンのクロール対象から外す」 というカスタマイズを行っておくといいかもしれない ・管理画面の「受注管理 > 受注一覧」の表示順は「更新日」の降順で固定されている。「商品管理 > 商品一覧」や「会員管理 > 会員一覧」も表示順は固定 それぞれ「登録順など他の表示順にも対応したい」「初期の並び順を変更したい」という要望が発生する可能性は高い ・管理画面の「受注管理 > 受注一覧」の検索欄に「注文番号・お名前・会社名・メールアドレス・電話番号」があるが、 ここに注文者のフルネームを入力してもヒットしない。名前・カナの両方に言える。詳細検索の「注文者名」ならヒットする ・管理画面の「受注管理 > 受注一覧 > 受注詳細」の「メール送信履歴」からメールを送信できるが、送信先アドレスが表示されていない ・管理画面の「受注管理 > 受注登録」から廃止済みの商品を選択できる。登録時にうっかり選択してしまう可能性があるので注意 詳細は「運用のためのメモ」の「商品の公開状態」を参照 ・管理画面に「コンテンツ管理 > レイアウト管理」があり、ここからブロックを追加削除や移動ができる すべてのブロックに対して、「どこに移動させても必ず正しく動作する」をテストするのは手間がかかるかもしれないので注意 一部のブロックのみ動作保証とするか、工数をかけて色々なパターンをテストするか ・管理画面に「コンテンツ管理 > ブロック管理」があり、ここからブロック自体を編集できる ただし default_frame.twig は全ページに影響するファイルのためか、この画面から編集できない https://homupedia.com/eccube4-template-customize.html ・プラグインは豊富にあるが有料のものが多く、動作を試したいだけの場合でも購入する必要がある。ECCubeの導入には結局結構なお金がかかるかもしれない また、プラグインは競合のリスクが常にある 詳細は「プラグインの導入」を参照 ・管理画面の「設定 → プラグイン → プラグイン一覧」に「アップデート」という列があり、アップデートがあるとここにその旨が表示される ただし気軽にアップロードされると問題が発生する可能性があるし、プラグインも含めてGit管理している場合は差分問題も発生する 「アップデート」列をコメントアウトし、プラグインはコマンドラインからインストールする運用も検討するといい ・データベースの文字コードはutf8となっている。絵文字などは扱えないので注意 文字コードをutf8mb4に変更する方法は以下で紹介されているが、文字列の長さチェックなど含めて本当に問題に無いかは要検証 https://amidaike.hatenablog.com/entry/2019/09/14/130431 ・データベース内の日時は、実際の日時より9時間前の時間が記録されている(UTCで記録されているので時差がある) ECCubeの仕様らしいが、SQLで直接データを集計するような場合に注意が必要 https://doc4.ec-cube.net/i18n_timezone https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=22997&forum=17 後述の「運用のためのメモ」の「データベースの日時」も参照 ・CSS管理、JavaScript管理、メール設定などにより、サーバ上のファイルが編集される GitからのPULLによりプログラムをサーバ上に反映している場合、コンフリクトが発生する可能性があるので注意 詳細は「運用のためのメモ」の「Git管理の考察」を参照 ・プログラムも設定ファイルも、公開ディレクトリ内に置かれる 設定ファイルを公開ディレクトリ外に移動する方法は、このファイルにある「本番環境(AWS想定)での稼働」の「設定ファイル」を参照 公式の解説どおり .htaccess で設定するのはイマイチそうなので、その対策も記載している ・ECCube4.0は、Composerのバージョン2では動作しない。バージョン1を明示して使う必要がある 詳細は「導入」の「Composerのバージョン指定」を参照
■ダウンロード
ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/ EC-CUBEダウンロード | ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/download/ ■公式サイト版とGitHub版 公式サイトからダウンロードしたものには、一部開発ツールが含まれていない (gulp、tests、.gitignore などが含まれていない) 以下のGitHubからならすべてのファイルが揃っていると思われる (ただし、逆にComposerによって作成されるvendor内は含まれていない。Composerを使って自分で作成する必要がある) EC-CUBE/ec-cube: EC-CUBE is the most popular e-commerce solution in Japan https://github.com/EC-CUBE/ec-cube 恐らく 公式サイト版 ... 丸ごとダウンロードして一部カスタマイズし、丸ごと本番環境にFTPアップロードして反映する GitHub版 ... Gitなど開発ツールを使って本格的に開発し、本番環境にもGitと連動させて反映する のような想定だと思われる よって開発について初心者なら公式サイト版を、そうでなければGitHub版を使っておくと無難
■導入
・Vagrant ・Docker ・XAMPP ・EC2 への導入について記載する 以降特に断りがなければ、各ローカル環境へは eccube4.local でアクセスできるものとする Vagrantでは動作が重いが、何とか作業できなくも無いくらいの速度だった Dockerでは遅すぎて作業にならないくらいの速度だった XAMPPでは遅くなかったが、高性能なパソコンでのみ試したので実際のところは不明 EC2では t2.micro でも、特に遅いとは感じないくらいの速度だった VagrantとDockerは、LinuxとWindowsでのファイル同期の速度が問題になっているのかもしれない ■Composerのバージョン指定 ※ECCube4.1から、Composerのバージョン2に対応しているとのこと 未検証だが、以下の問題は発生しないかもしれない 2020年10月24日にComposerのバージョンが2になったが、ECCubeはバージョン1でないと起動できない (バージョン2を使うと、composer install 実行時にエラーになる) Composer導入の際、以下のようにするとバージョンを指定して導入できる
$ curl -sS https://getcomposer.org/installer | php -- --version=1.10.15 $ mv composer.phar /usr/local/bin/composer
Composer本体のバージョンを1系に固定化する - Qiita https://qiita.com/ucan-lab/items/5a48f1a3c7a4c358223e Composer 2.0 will error というエラーが出るケースが有る - Issue #4133 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4133 AnsibleのPlaybookでは、一例だが以下のようにするとバージョンを指定して導入できる
- name: download composer shell: curl -sS https://getcomposer.org/installer | php -- --version=1.10.15 - name: rename composer.phar to composer shell: mv composer.phar /usr/local/bin/composer
■導入: Vagrant
■環境構築 ※eccube のPlaybookで環境を作成 ■ECCubeの初期設定 以下にアクセス http://eccube4.local/
■導入: Docker
Docker Composeを使用してインストールする - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/quickstart/docker_compose_install 2021年5月にVagrant+Docker環境で検証したときのメモ インストールはブラウザ上で行うようにしている 上記に一応の手順はあるが、PHP7.4やComposerのバージョンアップのことは考えられていないみたい? あまりきちんとメンテナンスされていないのかもしれない WLS2環境でDockerを検証した時のメモは以下に置いている ECCube\WLS2+Docker\メモ.txt ■Dockerfileの調整 Composerのバージョン問題で、「You can not install the EC-CUBE plugin via `composer` command.」のエラーになるので調整する
RUN curl -sS https://getcomposer.org/installer \ ↓ RUN curl -sS https://getcomposer.org/installer | php -- --version=1.10.15 \
プラグイン追加済みのECCubeをインストールする場合、以下も調整する
RUN composer install \ ↓ RUN composer install \ --no-plugins \
PHP7.4で動作させる場合、以下も調整する(FROM php:7.4-apache-stretch は現時点で存在しないみたい)
FROM php:7.3-apache-stretch ↓ FROM php:7.4-apache
■起動
$ docker-compose build $ docker-compose up -d Creating network "html_backend" with driver "bridge" Creating html_mysql_1 ... done Creating html_ec-cube_1 ... done Creating html_mailcatcher_1 ... done Creating html_postgres_1 ... done $ docker-compose ps 指定されたパスが見つかりません。 Name Command State Ports ------------------------------------------------------------------------------------------------------------ html_ec-cube_1 docker-php-entrypoint apac ... Up 0.0.0.0:4430->443/tcp, 0.0.0.0:8080->80/tcp html_mailcatcher_1 mailcatcher --no-quit --fo ... Up 0.0.0.0:1025->1025/tcp, 0.0.0.0:1080->1080/tcp html_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:13306->3306/tcp, 33060/tcp html_postgres_1 docker-entrypoint.sh postgres Up 0.0.0.0:15432->5432/tcp
以下でアクセスを確認 http://192.168.99.100:8080/robots.txt ■インストール 以下にアクセスしてインストール http://192.168.99.100:8080/install 「ようこそ」画面が表示された 「 [推奨] apc拡張モジュールが有効になっていません。」と表示されているが無視して進める 「次へ進む」とエラーになった src\Eccube\Controller\Install\InstallController.php の209行目あたりの以下をコメントアウトすると動いた チェック処理が重すぎるのかもしれない
// 対象ディレクトリ配下のディレクトリ・ファイルの書き込み権限をチェック $finder = Finder::create()->in($eccubeDirs); foreach ($finder as $file) { if (!is_writable($file->getRealPath())) { $noWritePermissions[] = $file; } }
「サイトの設定」は通常どおり作業できた 「データベースの種類」は「PostgreSQL」がデフォルトになっていたが、「MySQL」を選択する
データベースの種類: MySQL データベースのホスト名: mysql データベース名: eccubedb ユーザ名: dbuser パスワード: secret
「データベースの初期化」は通常どおり作業できた インストールが完了したが、「管理画面を表示」からアクセスするとエラーになった。タイム・アウトしている可能性が高い コマンドラインから以下を実行
$ docker exec -it html_ec-cube_1 bash $ php bin/console cache:clear --no-warmup
以下にアクセスできるようになった http://192.168.99.100:8080/ http://192.168.99.100:8080/system/ 以降は基本的にVagrantで起動した場合と同じ手順で大丈夫だった キャッシュの再生性も、コマンドで削除した上でブラウザからアクセスすれば大丈夫だった インストール直後のキャッシュ生成だけ特別重いのかもしれない ■メモ 躓いた箇所をメモしておく 「composer install」を実行すると認証を求められた プラグインを購入する際に登録したメールアドレスとパスワードを入力すると進めた 成功とエラーの両方が表示されたが、どうやら進めているみたい
# composer install Deprecation warning: require.ec-cube/CsvImportProductExt4 is invalid, it should not contain uppercase characters. Please use ec-cube/csvimportproductext4 instead. Make sure you fix this as Composer 2.0 will error. 〜中略〜 Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error. Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`. Prefetching 7 packages ・・・ - Downloading (14%) Authentication required (package-api.ec-cube.net): Username: example@gmail.com Password: (100%) 3: : https://package-api.ec-cube.net/ec-cube/VeriTrans4G/1.2.1/VeriTrans4G-1.2.1.tgz 〜中略〜 3: : https://package-api.ec-cube.net/ec-cube/CsvImportProductExt4/1.0.5/CsvImportProductExt4-1.0.5.tgz Finished: success: 0, skipped: 0, failure: 7, total: 7 Package operations: 7 installs, 0 updates, 0 removals [RuntimeException] You can not install the EC-CUBE plugin via `composer` command. Please use the `bin/console eccube:composer:require ec-cube/csvimportproductext4` instead. install [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--no-suggest] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--ignore-platform-reqs] [--] [<packages>]...
■導入: XAMPP + 公式サイト版
■環境の構築 XAMPPをインストールしてPHP7環境を作っておく 今回は http://localhost/~eccube4/ にインストールするものとする ■データベースを作成 XAMPP付属のphpMyAdminで、eccube4 データベースを作っておく ■ECCubeの初期設定 https://www.ec-cube.net/download/ からダウンロード C:\localhost\home\eccube4\public_html に配置するものとする。配置したら以下にアクセス http://localhost/~eccube4/ インストール画面が表示されたが、以下が表示されている
[必須] intl拡張モジュールが有効になっていません。 システム要件をご確認ください [推奨] wincache拡張モジュールが有効になっていません。
C:\xampp\php\php.ini で以下のコメントアウトを外す
;extension=intl ↓ extension=intl
これで「必須」が消えた 「推奨」は表示されたままだが、「wincache拡張モジュール」はキャッシュ作成の機能みたいなので無視して良さそう ただし速度は落ちるらしい EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=21086&forum=2
■導入: EC2 + GitHub版
■環境の構築 ※eccube のPlaybookで環境を作成 ■ECCubeの初期設定 以下にアクセス http://203.0.113.1/
■導入: EC2 + 公式サイト版
■環境の構築 ※eccube のPlaybookで環境を作成 ■ECCubeの初期設定
# mkdir /var/www/eccube # cd /var/www/eccube # wget http://downloads.ec-cube.net/src/eccube-4.0.3.zip # unzip eccube-4.0.3.zip # chown -R apache. eccube-4.0.3 # find eccube-4.0.3 -type d -print | xargs chmod 775 # find eccube-4.0.3 -type f -print | xargs chmod 664
パーミッション変更の際に以下の警告が表示されたが、無視して進めて問題無さそう chmod: `eccube-4.0.3/vendor/symfony/finder/Tests/Fixtures/with' にアクセスできません: No such file or directory chmod: `space' にアクセスできません: No such file or directory
# cp -rp /var/www/eccube/eccube-4.0.3/. /var/www/main/html … /var/www/main/html/index.php は上書き配置でいい
以下にアクセス http://203.0.113.1/ インストール画面が表示されたが、以下が表示されている
[推奨] apc拡張モジュールが有効になっていません。
「apc拡張モジュール」はPHP5の機能みたいなので無視して良さそう EC-CUBE4のインストール方法と具体的な手順 - たぶろぐ https://tab-log.com/eccube4-install#apc
■導入: 高速化
■OPcache OPcache導入してみた!(速さ検証もあるよ!) - Qiita https://qiita.com/morimorim/items/fb39ae7d673a8b88f413 OPcacheを利用してPHPを高速にしよう | https://weblabo.oscasierra.net/php-opcache/
# yum -y install --enablerepo=remi-php73 php-opcache … OPcacheをインストール # php -v PHP 7.3.24 (cli) (built: Oct 27 2020 11:01:59) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.24, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.3.24, Copyright (c) 1999-2018, by Zend Technologies … OPcacheのインストールを確認 # systemctl restart httpd
いくらか早くなったと思うが、さらに設定を調整する
# vi /etc/opt/remi/php73/php.d/10-opcache.ini (環境によっては /etc/php.d/10-opcache.ini などにある)
; The OPcache shared memory storage size. opcache.memory_consumption=256 … 128 → 256 ; The amount of memory for interned strings in Mbytes. opcache.interned_strings_buffer=16 … 8 → 16 ; The maximum number of keys (scripts) in the OPcache hash table. ; Only numbers between 200 and 1000000 are allowed. opcache.max_accelerated_files=8000 … 4000 → 8000
# systemctl restart httpd # systemctl restart php-fpm … php-fpm を使っている場合、あわせて再起動する
これでいくらか高速に動作する なお
# yum -y install --enablerepo=remi-php73 php-opcache
というコマンドでインストールしているが、環境によっては以下のようなコマンドとなるので適宜調整する php73 部分も、その環境のPHPバージョンにあった値にする
# yum -y install php-opcache # yum -y install php73-php-opcache
■導入: 環境の再構築
念のためキャッシュをクリアする
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console cache:clear --no-warmup
データベースを作成し直す(必要に応じてバックアップは作成しておく)
$ mysql -u root -p (パスワードなし) mysql> DROP DATABASE main; mysql> CREATE DATABASE main DEFAULT CHARACTER SET utf8mb4; mysql> QUIT;
html/.env を削除する(必要に応じてバックアップは作成しておく) 以下にアクセスし、以降は通常の手順でインストールを行う http://eccube4.local/
■インストール
※以降特に断りがなければ、Vagrantで環境を構築したものとする ※インストール画面は非常に重いので、気長に待つ 実行環境の性能によっては、php.ini で max_execution_time を 600 などに伸ばしておく必要がある 「ようこそ」が表示されているので「次へ進む」をクリック 「権限チェック」で「アクセス権限は正常です」と表示されていることを確認して「次へ進む」をクリック 「サイトの設定」で以下を入力して「次へ進む」をクリック あなたの店名: テスト店 (一例) メールアドレス: example@gmail.com (一例。自身のメールアドレス) 管理画面ログインID: admin (一例) 管理画面パスワード: abcd1234 (一例) 管理画面のディレクトリ名: system (一例) 「データベースの設定」で以下を入力して「次へ進む」をクリック データベースのホスト名: localhost データベース名: main ユーザ名: webmaster パスワード: 1234 「データベースの初期化」が表示されるので「次へ進む」をクリック 「インストールが完了しました!」が表示されたら「管理画面を表示」をクリック 上で設定した管理画面情報でログインする http://eccube4.local/ http://eccube4.local/system/
■インストール後の設定
■メール送信 メールを送信する場合は以下を編集する 以下は暗号化なしのSMTPを使う例 .env
25:- MAILER_URL=smtp://localhost:25 25:+ MAILER_URL=smtp://mail.example.com:587?username=example&password=XXXXXXXXXXXXXXXX
以下は465番ポートで送信する例 試した際は「encryption=ssl」のオプション指定をしないと接続できなかった .env
25:- MAILER_URL=smtp://localhost:25 25:+ MAILER_URL=smtp://mail.example.com:465?username=example&password=XXXXXXXXXXXXXXXX&encryption=ssl
以下はGmailのSMTPを使う例 「encryption=ssl」に加えて「auth_mode=login」の指定も必要だった .env
25:- MAILER_URL=smtp://localhost:25 25:+ MAILER_URL=smtp://smtp.gmail.com:465?username=example@gmail.com&password=XXXXXXXXXXXXXXXX&encryption=ssl&auth_mode=login
「正しいSMTP情報のはずなのに、ECCubeに組み込んでもメールが送信されない」 という場合、このファイルの「トラブル」の「.env の値に「#」を使用できない」を参照する (「#」や「$」が含まれている場合の挙動が怪しい) ■コマンドラインインターフェイス コマンドラインインターフェイス - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/quickstart_cli 以下で実行できる
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console list Symfony 3.4.26 (kernel: Eccube, env: prod, debug: false) Usage: command [options] [arguments]
XAMPP環境なら以下のようにして実行する
>C:\xampp\php\php.exe bin/console list
■認証キー 管理画面 → オーナーズストア → 認証キー設定 から、認証キーの新規発行と登録ができる すでに案件用に発行したキーがあるなら、その値を登録する オーナーズストアでメンバー登録&ログインし、マイページで「登録サイト」にサイトを追加すると認証キーが発行される 基本的にはプラグイン購入後、その認証キーをECCubeに登録すればいい 以下はプラグイン購入後に案内されたページ 4系のプラグインをオーナーズストアで購入後、どのようにインストールするのかわかりません。 - よくあるご質問 https://support.ec-cube.net/hc/ja/articles/360038927991 認証キーが共通なら、以前に購入した有料プラグインを再度インストールしたりできるみたい 認証キーを登録しないと、プラグインの一覧など一部機能が使えない 上記手順で発行した認証キーを登録すると、composer.json に
"header": ["X-ECCUBE-KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
のように値が記録される 稼働後に認証キーを変更すると、プラグイン一覧で 「オーナーズストアへの接続に問題があるため、詳細な情報を取得できませんでした。」 と表示されることから考えても、 「最初に案件専用の認証キーを発行して、すべての環境でそのキーを使い回す」 とする必要がありそうな ■開発用モードに切り替え .env を以下のように変更する
APP_ENV=prod APP_DEBUG=0 ↓ APP_ENV=dev APP_DEBUG=1
変更後、以下のコマンドでキャッシュを削除しておく
$ php bin/console cache:clear --no-warmup
ただしこの設定にすると、キャッシュされない代わりに動作が重くなるので注意 なお、APP_ENV を使って公開サーバで「本番環境」「検収環境」のようなものは作らないほうがいいかもしれない 以下のページで 「サイトをインターネット上で公開される際は必ずプロダクションモードを利用してください。」 とある 環境設定 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/environmental_setting よって「本番環境なら」「検収環境なら」のような分岐を作りたければ、別途独自に .env 内で判定用の変数を定義する方がいいかもしれない 具体的には、このファイル内にある「運用のためのメモ」の「環境による分岐」を参照 なお、開発者用モードならデバッグ用の dump 命令を使えるようになる 一例だが app\template\default\Product\detail.twig に以下を追加すると、データの内容が表示される
{{ dump(Product) }}
EC-CUBE4 突然の死!! 本番モードでdump関数を使うと死ぬ | seiyaan teck blog https://seiyaan.com/2019/06/ec-cube4-dump-die/ ■リポジトリへの登録 初回構築時なら、これくらいのタイミングでいったんリポジトリにコミット&プッシュしておくといい ■店舗設定 管理画面 → 設定 → 店舗設定 → 基本設定 最低限、4つある各メールアドレスを設定しておく 注文メールやお問い合わせメールは「問い合わせ受付メールアドレス」に対してBccで送信される ■マイグレーション SSHで接続して以下を実行する (実行が必要なマイグレーションがあれば実行される)
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console doctrine:migrations:migrate
マイグレーションファイル内で 「trans('admin.product.analytic_csv.row.product_id')」 のような言語データを参照している場合、キャッシュによって正しくマイグレーションデータが保存されない可能性がある (上記以外にも、必要なデータを参照できない可能性がある) この場合、以下のように更新すると対応できる
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ git pull origin main $ php bin/console cache:clear --no-warmup … この時点でキャッシュをクリアしておく $ php bin/console doctrine:migrations:migrate $ php bin/console cache:clear --no-warmup
むしろ「キャッシュクリア → マイグレーション実行」という手順が正しくて、 マイグレーション実行直後のキャッシュクリアは不要か もしくは、そもそも「trans」などの内容によってマイグレーション内容が変化する可能性がある方が好ましくないか プログラムの内容によって、「本番環境だとAと登録されているが、検収環境だとBと登録されている」が起こり得る それを調整するためにマイグレーションを作成したとしても、やはり環境によって登録内容が変わってしまう 意図しないデータが登録されてしまった場合、一例だが以下のようなSQLで内容を調整する (「admin.product.analytic_csv.row.product_id」のような値が登録されている箇所を書き換える)
SELECT * FROM mtb_csv_type; SELECT * FROM dtb_csv WHERE id > 230; UPDATE mtb_csv_type SET name = '商品集計CSV' WHERE id = '8'; UPDATE dtb_csv SET disp_name = '商品ID' WHERE id = '236'; UPDATE dtb_csv SET disp_name = '商品名' WHERE id = '237'; UPDATE dtb_csv SET disp_name = '購入件数' WHERE id = '238';
■キャッシュクリア SSHで接続して以下を実行する 管理画面からよりも確実みたい
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console cache:clear --no-warmup
以下により、Composerのキャッシュをクリアできる 現状必要になったことは無いが備忘録として
$ composer clear-cache $ composer dump-autoload
composerについて初期学習まとめ - Qiita https://qiita.com/eidera/items/3e0b2b41253e1563be46 入力項目を追加したい場合、Entityのカスタマイズを行うことになる この作業を行った際は、トレイトを認識させるためのプロキシファイルを作成しなおす必要がある
$ php bin/console eccube:generate:proxies
またブランチ切り替えなどでプロキシファイルがおかしな状態になることがある この場合、あらかじめ /app/proxy/entity/src/Eccube/Entity 内のファイルを削除したうえで、 以下を実行することで再生成ができる
$ php bin/console eccube:generate:proxies
EntityのカスタマイズとTraitの具体例については、「カスタマイズ: 会員情報に項目を追加」も参照 マイグレーションが絡む場合、このファイル内にある「インストール後の設定」の「マイグレーション」も参照 またキャッシュについては、このファイル内にある「カスタマイズ」の「キャッシュの扱い」も参照 まとめると、以下を実行することでキャッシュの完全クリアができる…はずだが、引き続き要検証 (「cache:clear --no-warmup」は「eccube:generate:proxies」の後に行うべきかも) 念のため、実行前にユーザ側&管理側ともログアウトしておくといいかもしれない また、var/log/prod 内にログファイルが出力されているはずなので、その内容を確認することで原因を特定できることもある
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ rm -rf app/proxy/entity/src $ composer clear-cache $ composer dump-autoload $ php bin/console cache:clear --no-warmup $ php bin/console eccube:generate:proxies
この後で、もう一度キャッシュをクリアしておくといいかもしれない
$ php bin/console cache:clear --no-warmup
それでも一部のページでエラーになることがあった 経緯は 「一時的に本番環境の状態を確認するため、masterブランチに切り替えた。元に戻したが、トップページでのみシステムエラーが表示されるようになった」 というもの このとき、ログには以下のエラーが記録されていた
[2022-08-30 11:18:17] front.ERROR [vhdde1qc9n71d7p8n7vsakqiou] [55901fc] [anon.] [Eccube\Log\Logger:log:66] - システムエラーが発生しました。 ["Attempted to call an undefined method named \"getCacheRegion\" of class \"Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\".","/var/www/html/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php",348
「php bin/console list」で表示される doctrine の命令のうち、キャッシュをクリアしそうなものを色々実行し、 「app/proxy/entity/src/Eccube/Entity」内に差分ファイルが発生していたので元に戻し、 さらに通常のキャッシュクリアなどを何度か行う…ということをしていると解決した どれが直接の原因だったのかは不明 それでも駄目なら、 「最近何らかのプラグインが追加されていて、それが有効になっている前提で作られている」 など他の要因が無いかも確認する 例えば「ベリトランス4G」のプラグインを導入している場合、 プラグインの設定画面で「登録」ボタンを押すことで必要データが作成され、 これによって問題が解消することがある
■運用のためのメモ
■基本設定 「設定 → 店舗設定 → 基本設定」 店名は、titleタグの内容などに使われる 会社名、住所、電話番号、店舗営業時間、取り扱い商品説明文、店舗からのメッセージは、 ユーザ側のフッタにある「当サイトについて」のリンク先ページに表示される ■商品規格 商品ごとに「この色でこの大きさなら○円で在庫は○円」を登録できる ただし規格は1商品に対して2つまでしか紐付けられないので注意 以下のプラグインを使えばある程度柔軟に対応できるかもしれないが、 注文内容の商品に対するオプション内容を、管理画面から変更できないので注意 (開発元に確認済み。いったん商品を削除しての再追加はできる) 4.0系|商品オプションプラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1787 また、規格は例えば 1. 商品登録時に商品コードを入力する 2. 1の商品に商品規格を登録し、商品コードを入力する として商品を登録すると、1の商品コードはECCube上で使われなくなる(規格の商品コードが使われるため)が、データベース上には残ったままとなる ただし2で登録した規格を初期化(全削除)すると再度1の商品コードが使われるようになるので、これは「初期化のことを想定したECCubeの仕様」だと思われる これは「初期化のことを想定したECCubeの仕様」だと思われるが、 1. 商品登録時に商品コードを入力する 2. 1の商品に商品規格を登録し、商品コードを入力する 3. 商品規格の商品コードを変更する 4. しばらく運用したあと、商品規格を削除する(規格なしで運用する) という手順で、古い商品コード(意図しない値)が現れる可能性はある。 気持ち悪ければ、データベースの値を直接編集することはできる 恐らく問題無いと思われるが、独自カスタマイズした部分に影響しないかも含めて検証が必要 以下は実際に商品情報を登録して検証したときのメモ 商品を登録すると、以下のようになった
> SELECT id, product_id, price01, price02, product_code, stock, visible FROM dtb_product_class WHERE product_id = 363; +------+------------+---------+---------+--------------+-------+---------+ | id | product_id | price01 | price02 | product_code | stock | visible | +------+------------+---------+---------+--------------+-------+---------+ | 1089 | 363 | NULL | 1000.00 | TEST | 10 | 1 | +------+------------+---------+---------+--------------+-------+---------+
商品に規格を登録すると、以下のようになった
> SELECT id, product_id, price01, price02, product_code, stock, visible FROM dtb_product_class WHERE product_id = 363; +------+------------+---------+---------+--------------+-------+---------+ | id | product_id | price01 | price02 | product_code | stock | visible | +------+------------+---------+---------+--------------+-------+---------+ | 1089 | 363 | NULL | 1000.00 | TEST | 10 | 0 | | 1090 | 363 | NULL | 2000.00 | TEST-S | 20 | 1 | | 1091 | 363 | NULL | 3000.00 | TEST-M | 30 | 1 | | 1092 | 363 | NULL | 4000.00 | TEST-L | 40 | 1 | +------+------------+---------+---------+--------------+-------+---------+
商品規格を初期化すると、以下のようになった
> SELECT id, product_id, price01, price02, product_code, stock, visible FROM dtb_product_class WHERE product_id = 363; +------+------------+---------+---------+--------------+-------+---------+ | id | product_id | price01 | price02 | product_code | stock | visible | +------+------------+---------+---------+--------------+-------+---------+ | 1089 | 363 | NULL | 1000.00 | TEST | 10 | 1 | | 1090 | 363 | NULL | 2000.00 | TEST-S | 20 | 0 | | 1091 | 363 | NULL | 3000.00 | TEST-M | 30 | 0 | | 1092 | 363 | NULL | 4000.00 | TEST-L | 40 | 0 | +------+------------+---------+---------+--------------+-------+---------+
■ページ管理 「コンテンツ管理 → ページ管理」 各ページの表示内容を管理できる 「ページ名」を変更すれば、そのページのtitleタグの内容が変わる ただしここからページ名などを変更すると、「コード」の内容を変更していなくても差分が発生する(ファイルの最終行の改行が削除されてしまう) ECCubeの仕様かもしれないが、ファイルをGit管理していると差分が発生するので注意(差分の取り消しを行うなどの作業が必要) また、例えば「当サイトについて」や「プライバシーポリシー」ページの見出し文字は上記ではなく以下に記述されている src\Eccube\Resource\locale\messages.ja.yaml 上記ファイルの内容をもとに、以下に新たなタイトルを定義することで、見出し文字を変更できる app\Customize\Resource\locale\messages.ja.yaml 入門:EC-CUBE 4のデフォルトページを編集する - 眠るシーラカンスと水底のプログラマー http://coelacanth.jp.net/eccube4_change_page_html/ ■CSS管理 「コンテンツ管理 → CSS管理」 コードを登録すると以下のファイルに反映され、ユーザ側で読み込まれる user_data\assets\css\customize.css ■JavaScript管理 「コンテンツ管理 → JavaScript管理」 コードを登録すると以下のファイルに反映され、ユーザ側で読み込まれる user_data\assets\js\customize.js ■送料設定 標準の挙動は ・配送方法ごとに、「都道府県ごとの送料」を設定できる(◯◯業者なら大阪への配送は800円、東京への配送は1000円、など) ・特定商品に対して、↑に上乗せの送料を設定できる(◯◯商品は送料1,000円上乗せ、など) ・購入金額が一定以上なら、送料無料とできる ・購入個数が一定以上なら、送料無料とできる となっている 以下、標準の挙動を調査したときのメモ 「設定 → 店舗設定 → 配送方法設定 → サンプル業者」では、配送料は全国一律で「1000円」となっている 「設定 → 店舗設定 → 基本設定 → 送料設定」で「商品ごとの送料設定」を有効にしてみる そうすると、商品登録画面に「商品送料」の項目が現れた 規格がある商品の場合、商品規格登録画面に「商品送料」の項目が現れた 「商品送料」が「500円」の商品を登録してみる 「商品送料」が「500円」「1000円」「1500円」の規格商品を登録してみる 「商品送料」が無い商品を購入してみる 全国一律の「1000円」が送料として登録された 送料は「商品情報」から編集できる 「商品送料」が無い規格商品を購入してみる 全国一律の「1000円」が送料として登録された 送料は「商品情報」から編集できる 「商品送料」がある(500円)商品を購入してみる 全国一律の「1000円」に上記が追加され、「1500円」が送料として登録された 送料は「商品情報」から編集できる 「商品送料」がある(500円)規格商品を購入してみる 全国一律の「1000円」に上記が追加され、「1500円」が送料として登録された 送料は「商品情報」から編集できる 上記のことから、「商品ごとの送料設定」は上乗せ料金の設定となっている なお管理画面から受注登録する場合「商品ごとの送料設定」は自動に受注明細に登録されないみたい 具体的には注文登録の際に「その他の明細を追加」ボタンから送料を追加し、金額を手入力する必要があるみたい 運用上問題無いかは検討してから導入したい また「商品ごとの送料設定」を有効にして商品ごとの送料を登録した場合でも、 受注情報として記録されるのは「合計の送料」のみとなっている 「その時点での商品ごとの送料」を求めるのは、データベースの構造上不可能のようなので注意 ECCubeでの送料計算については以下も参照 EC-CUBE4系で送料がどのように計算されるのか解説します | (株)カジヤ https://cajiya.co.jp/column/6827 ■メール設定 「設定 → 店舗設定 → メール設定」 以下のメール設定が用意されている ・注文受付メール ・会員仮登録メール ・会員本登録メール ・会員退会メール ・問合受付メール ・パスワードリセット ・パスワードリマインダー ・出荷通知メール 「会員退会メール」「パスワードリセット」「パスワードリマインダー」はテキストメールのみ、 それ以外はHTMLメールとテキストメールの内容を設定する 「パスワードリマインダー」は現行バージョンでは使われていない 旧バージョンのなごりらしい EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=24938&forum=11 例えば「注文受け付けメール」の「テキスト」と「HTML」を編集すると、以下のファイルが編集される 件名はデータベースで管理されているが、本文はファイルとして管理されている app\template\default\Mail\order.html.twig app\template\default\Mail\order.twig 「パスワードリマインダー」メールは送信されないらしい ■CSV出力項目設定 「設定 → 店舗設定 → CSV出力項目設定」 ただし、書き出したCSVでそのまま一括登録はできないようだが、一括登録用に項目を合わせば対応はできるみたい また、ダウンロードできるCSVファイルはShift-JISで、一括登録もShift-JISで行う必要があるみたい UTF-8など他の文字コードにすると、正しく登録できないようなので注意(規格品が重複して登録されるような挙動になった) 項目の調整は、以下のプラグインを導入するなどして対応できるみたい 4.0系|商品CSV登録拡張プラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1802 ■権限設定 「設定 → システム設定 → マスターデータ管理 → mtb_authority」 初期状態で「システム管理者」が登録されているが、ここで「店舗オーナー」「販売管理部」などのような権限を追加できる 「設定 → システム設定 → 権限管理」 例えば「拒否URL」に「/setting/system/authority」と登録すると、権限管理ページにのみアクセスできなくなる 例えば「拒否URL」に「/setting」と登録すると、「/setting」以下のページにアクセスできなくなる 左メニューからも項目が非表示になる 「/setting」を登録した場合、「設定」の親メニューごと非表示になる これを踏まえて、一例だが以下のように設定する 例えば「拒否URL」に以下を登録すると、店舗オーナー用にシステムページ全般にアクセスできないアカウントを作れる /content/css /content/js /content/block /content/cache /content/maintenance /setting/system /store 例えば「拒否URL」に以下を登録すると、販売管理部用に「商品管理」「受注管理」「会員管理」のみにアクセスできるアカウントを作れる /content /setting /store 例えば「拒否URL」に以下を登録すると、商品登録用に「商品一覧」「商品登録」「規格管理」のみにアクセスできるアカウントを作れる /product/category /product/tag /product/product_csv_upload /product/category_csv_upload /order /customer /content /setting /store ■管理画面URLを変更 「設定 → システム設定 → セキュリティ管理」 「管理画面URL」を変更して「登録」をクリック 強制的にログイン画面へ遷移し、「管理画面のURLを変更しました。再度ログインを行ってください。」と表示された キャッシュが再作成されるのか重い この内容はデータベースではなく .env に記録される 具体的には以下のように記録されている
ECCUBE_ADMIN_ROUTE=system
※.env を .htaccess に移行すると、この設定内容は管理画面から変更できなくなる ■管理画面のIP制限 「設定 → システム設定 → セキュリティ管理」 「IP制限」に開業区切りでIPアドレスを入力して「登録」をクリック 例えば以下を入力した場合、192.168.33.1 と 192.168.33.2 からのアクセスのみが許可される
192.168.33.1 192.168.33.2
この内容はデータベースではなく .env に記録される 例えば上記設定を行った場合、.env が以下のように変更される
ECCUBE_ADMIN_ALLOW_HOSTS=[] ↓ ECCUBE_ADMIN_ALLOW_HOSTS='["192.168.33.1","192.168.33.2"]'
ロードバランサーのある環境なら、この機能は使いづらいかもしれない もしくは管理画面にはサーバを直接指定してアクセスするか ※.env を .htaccess に移行すると、この設定内容は管理画面から変更できなくなる ■環境による分岐 「検収環境では専用の表示を行いたい」「本番環境でのみアクセス解析タグを挿入したい」のような分岐を行う方法 まずは準備として eccube.yaml 内で html\app\config\eccube\packages\eccube.yaml
env(ECCUBE_PACKAGE_API_URL): 'https://package-api.ec-cube.net' env(ECCUBE_OWNERS_STORE_URL): 'https://www.ec-cube.net' env(ECCUBE_MAINTENANCE_FILE_PATH): '%kernel.project_dir%/.maintenance' env(ECCUBE_ENV): 'local' … 環境判定用に追加 〜中略〜 eccube_result_cache_lifetime: 3600 # doctrineのresult cacheのlifetime. eccube_result_cache_lifetime_short: 10 # doctrineのresult cacheのlifetime. 商品一覧画面など長期間キャッシュできない箇所で使用する. eccube_content_maintenance_file_path: '%env(ECCUBE_MAINTENANCE_FILE_PATH)%' eccube_env: '%env(ECCUBE_ENV)%' # 環境判定用 … 環境判定用に追加
このように定義しておく これで例えば本番環境の .env に
ECCUBE_ENV=production
と追加することにより、テンプレート内で以下のような分岐を行うことができる
{% if eccube_config.eccube_env == 'production' %} 〜本番環境〜 {% elseif eccube_config.eccube_env == 'staging' %} 〜検収環境〜 {% endif %}
つまり 「ECCUBE_ENV=production」が定義されていれば本番環境、 「ECCUBE_ENV=staging」が定義されていれば検収環境、 何も定義されていなければローカル開発環境、 という分岐ができる 上記のとおり app/config/eccube/packages/eccube.yaml で定義できるが、 app/config/eccube/packages/customize.yaml で定義することにより、本体更新の際に上書きしてしまうリスクを無くすことができるみたい(未検証) EC-CUBE4 独自の定数を定義 - PukiWiki https://yassu.jp/pukiwiki/index.php?EC-CUBE4%20%C6%C8%BC%AB%A4%CE%C4%EA%BF%F4%A4%F2%C4%EA%B5%C1 ■商品の公開状態 各商品には、公開状態として「公開」「非公開」「廃止」があり、「削除」も含めると合計4つの状態がある 外部キー制約から商品を削除できないことが多いので、管理画面上で閲覧不要な場合は「廃止」にするといい 商品管理での商品の取扱操作のラベルの見直し - Issue #4375 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4375 上記ページより、以下のようなマトリクスになっているとのこと 商品一覧・詳細 マイページ購入履歴 商品管理 受注管理 備考 公開 ○ ○ ○ ○ 非公開 × ○ ○ ○ 廃止 × ○ ×(※) ○ ※商品管理では検索条件に指定することで閲覧可 削除 × × × × DBから物理削除 上記のように、廃止の商品でも管理画面の受注登録からは選択できてしまう 選択できないと、商品の編集時などに問題があるためだと思われる ただし運用時の選択ミスを防ぐため、 ・運用ルールとして、廃止にした商品名の先頭には必ず「【廃止】」と付ける ・プログラムを調整し、廃止にした商品は商品名の先頭に「【廃止】」を自動付加して表示する のいずれかの対応を行うと良さそう ■商品の販売種別 商品登録時に「販売種別」を登録できる これは「通常販売」「ダウンロード販売」など販売方法を分けるための機能 種別が変わるとカート内で区別され、種別ごとに「レジに進む」ボタンが表示される(同時購入できなくなる) また購入が完了すると「購入を続ける」ボタンが表示され、引き続きカートに残っている商品の購入が促される 販売種別はマスターデータとして管理されているので、 「設定 → システム設定 → マスターデータ管理 → mtb_sale_type」 から値を追加できる 配送方法が設定されていない販売種別をカートに入れようとすると、以下のメッセージが表示される 「◯◯◯」はまだ配送の準備ができておりません。恐れ入りますがお問い合わせページよりお問い合わせください。 ■商品の在庫数 内部的には、在庫数は2ヶ所で管理されている EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=17797&forum=2 上記ページにて、以下の説明がされている
1. 商品詳細にて、在庫有無を表示する→ dtb_product_class.stock 2. 商品購入時、在庫有無をチェックする → dtb_product_stock.stock となっています。 商品購入時は、排他制御が必要になる(同時に購入されないように厳密に制御する)ため、dtb_product_stock.stock を使用しますが、 この情報を商品詳細でも参照すると、とても処理が遅くなってしまうので、 商品詳細では在庫数の目安として dtb_product_class.stock を使用するようになっています。
在庫に関する処理は、以下の理由で調整が大変なので注意 ・在庫数はWeb注文・社内受注・受注編集・注文取消し・注文取消し解除など、様々なシーンで増減する ・在庫数を増減する際、テーブルロックによる排他制御の考え方が必要 ・ECCubeではPurchaseFlowという仕組みで在庫数の増減を実現しているが、ECCubeのとてもコアな部分でソースコードが難解 以下にも調査内容がある C:\Users\Yamano\Dropbox\技術\ECCube\調査.txt ■お届け先の登録件数上限について お届け先を20件登録すると、お届け先一覧に 「お届け先登録の上限の20件に達しています。お届け先を入力したい場合は、削除か変更を行ってください。」 と表示される この件数は app\config\eccube\packages\eccube.yaml にある
eccube_deliv_addr_max: 20
この部分を編集すれば増減できる ただしあまり多くすることは想定されていないような作りには見えるので注意 (どれだけお届け先を増やしても、改ページが表示されたりはしないなど) EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?viewmode=flat&order=DESC&topic_id=2498... なお、上記設定の下にある
eccube_deliv_date_end_max: 21
これはお届け日のプルダウンの選択肢の数を制御しているもの EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?viewmode=flat&order=DESC&topic_id=2450... ■お届け先の追加と、対応状況の「発送済み」と出荷情報の「出荷済みにする」について 以下の注文があるとする 対応状況: 入金済み 支払方法: クレジットカード決済 「対応状況」を「発送済み」にすると「出荷日」が記録された この際「商品出荷のお知らせ」というメールは送信されなかった(送信確認も無い) 「出荷情報の追加」から「出荷メール送信」をクリックすると「商品出荷のお知らせ」というメールが送信された このメールは何度でも送信できる 「対応状況」や「出荷情報」には影響しない 「出荷情報の追加」から「出荷済みにする」をクリックすると「出荷日」が記録された 必要なら、同時に「商品出荷のお知らせ」というメールを送信することもできる(送信確認される) 対応状況は「発送済み」になった 「出荷情報の追加」は何のためにあるのか、の考察 複数の商品を注文する場合、商品ごとにお届け先を設定できる また注文済みの商品に対して、管理画面からさらに商品をお届け先を追加できる これらは別々に出荷情報を管理できるが、対応状況や支払情報は「受注」単位で共通となる つまり、「同じ注文で複数のお届け先がある」という場合に別々に管理できるようにしたのが「出荷情報」だと思われる ■ログ表示 「設定 → システム設定 → ログ表示」 複数台構成でログを同期しないなら、そのときアクセスしているサーバのログだけが表示される スティッキーセッションを設定しておけば、再読み込みごとに変わることはないが、ロードバランサーの設定を確認しておく ■マスターデータ管理 「設定 → システム設定 → マスターデータ管理」 実際に権限を追加してみる mtb_authorityを選択して以下を登録して「保存」をクリック ID: 2 Name: テスト これで「メンバー管理」や「権限管理」に反映される ■テンプレートをダウンロード 「オーナーズストア → テンプレート → テンプレート一覧」 「デフォルト」のダウンロードボタンを押すと、以下の内容をダウンロードできる app/template/default ■ログイン時間を伸ばす EC-CUBE4カスタマイズ - 4系でログイン・カートのセッション持続時間を伸ばす方法 https://umebius.com/eccube/eccube4-session-lifetime-logout/ app\config\eccube\packages\eccube.yaml でセッション持続時間が以下のように定義されている デフォルトの「1440」は「60 * 24」の計算結果。つまり24分
env(ECCUBE_COOKIE_LIFETIME): 0 env(ECCUBE_GC_MAXLIFETIME): 1440
例えば3時間にする場合、.env の最後に以下を追加する 「10800」は「60 * 60 * 3」の計算結果。つまり3時間
ECCUBE_COOKIE_LIFETIME=10800 ECCUBE_GC_MAXLIFETIME=10800
なお、ALB(AWSのロードバランサー)でスティッキーセッションを使用している場合、 上記設定を行っても24分でログアウトされた(ロードバランサー側の時点でセッションが切れるためだと思われる) この場合、ALB側の設定でもセッションの持続時間を伸ばしておく(対象のターゲットグループで「維持設定の期間」を変更する) ■セッションをデータベースに保存する ※未検証 EC-CUBE4カスタマイズ - セッションをファイルではなくデータベースに保存する方法 PdoSessionHandler https://umebius.com/eccube/eccube4-db-session/ ■セキュリティチェック 以下のプラグインを導入して実行するといい 4.0系|EC-CUBEセキュリティチェックプラグイン(4.0系)|株式会社イーシーキューブ https://www.ec-cube.net/products/detail.php?product_id=2040 ただし本体やプラグインを改造している場合、改造内容が先祖返りする可能性があるみたい また、データベース定義を変更しているとチェック途中で落ちることがあるみたい 実行する場合、これらには気を付ける ■受注データの一覧 一例だが、以下のようにして受注データを一覧できる
SELECT id, order_no, name01, name02, kana01, kana02, email, phone_number, postal_code, addr01, addr02, payment_total, payment_method FROM dtb_order WHERE pref_id = 13 # 一例として東京都の受注データを指定 ORDER BY id DESC ;
■受注データのお問い合わせ番号 受注一覧や受注詳細にある「お問い合わせ番号」を入力すると、送信されるメールに記載される 自動で何かと連動するのではなく、単に番号を表示するだけみたい? ■受注データの注文番号 注文番号は「注文時に自動で採番される管理番号」と説明されている ただ、受注データは「注文完了時に登録される」のではなく 1. カート 2. お客様情報入力 3. ご注文手続き(支払い方法の選択がある画面) 4. ご注文内容確認画面 5. 完了 画面遷移のうち「2→3」のタイミングで作成されるが、4のタイミングなどで離脱すると、このデータは残り続ける またこのとき、対応状況は「購入処理中」となっている よって受注一覧のみを見ていると、注文番号に歯抜けがあるように見える 歯抜けデータは、受注一覧で「購入処理中」の対応状況で絞り込むと表示される 同様に「決済処理中」という対応状況もあり、カード決済画面まで進んで止めるとここに表示されると思われる(要検証 / カード決済を導入した場合のみ?) ■受注データのステータス order_status_id の値は、mtb_order_status テーブルに格納されている ECCubeインストール直後は、以下が登録されている 1 ... 新規受付 3 ... 注文取消し 4 ... 対応中 5 ... 発送済み 6 ... 入金済み 7 ... 決済処理中 8 ... 購入処理中 9 ... 返品 プログラムでの扱いについては、このファイル内の「対応状況の遷移を変更」を参照 ■受注データの取消し 以下はID「12345」の注文を取消しにする場合の例 UPDATE dtb_order SET order_status_id = 3 WHERE id = 12345; ■受注データの削除 外部キー制約のため、単純に削除したりはできない 以下は「WHERE id = 12345」の注文を削除する場合のSQL例(実際の仕事案件で改造されたECCubeに対し作業) 未検証なので注意
DELETE FROM plg_vt4g_order_payment WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM plg_vt4g_order_log WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_mail_history WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_url_token WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_order_item_cst WHERE order_item_id IN (SELECT id FROM dtb_order_item WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345)); DELETE FROM dtb_order_item WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_shipping WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_order_cst WHERE order_id IN (SELECT id FROM dtb_order WHERE id = 12345); DELETE FROM dtb_order WHERE id = 12345;
■受注データの初期化 外部キー制約のため、単純に削除したりはできない EC-CUBE4データ移行:受注データ(MySQL) | ITOBEN STYLE Blog https://itoben.com/blog/4279.html 上のページによると、以下の手順で削除できるみたい ただしカスタマイズでテーブルが増えている場合などは対応できなかった
ALTER TABLE dtb_shipping DROP FOREIGN KEY `FK_2EBD22CE8D9F6D38`; ALTER TABLE dtb_mail_history DROP FOREIGN KEY `FK_4870AB118D9F6D38`; ALTER TABLE dtb_order_item DROP FOREIGN KEY `FK_A0C8C3ED8D9F6D38`; DELETE FROM dtb_shipping WHERE order_id NOT IN( SELECT id FROM dtb_order ); DELETE FROM dtb_mail_history WHERE order_id NOT IN( SELECT id FROM dtb_order ); DELETE FROM dtb_order_item WHERE order_id NOT IN( SELECT id FROM dtb_order ); ALTER TABLE dtb_shipping ADD CONSTRAINT FK_2EBD22CE8D9F6D38 FOREIGN KEY (order_id) REFERENCES dtb_order (id) ON DELETE RESTRICT ON UPDATE RESTRICT; ALTER TABLE dtb_mail_history ADD CONSTRAINT FK_4870AB118D9F6D38 FOREIGN KEY (order_id) REFERENCES dtb_order (id) ON DELETE RESTRICT ON UPDATE RESTRICT; ALTER TABLE dtb_order_item ADD CONSTRAINT FK_A0C8C3ED8D9F6D38 FOREIGN KEY (order_id) REFERENCES dtb_order (id) ON DELETE RESTRICT ON UPDATE RESTRICT; ALTER TABLE dtb_order_item DROP FOREIGN KEY `FK_A0C8C3ED4887F3F8`; DELETE FROM dtb_order_item WHERE shipping_id NOT IN( SELECT id FROM dtb_shipping ); ALTER TABLE dtb_order_item ADD CONSTRAINT FK_A0C8C3ED4887F3F8 FOREIGN KEY (shipping_id) REFERENCES dtb_shipping (id) ON DELETE RESTRICT ON UPDATE RESTRICT; ALTER TABLE dtb_order_item DROP FOREIGN KEY `FK_A0C8C3ED4887F3F8`; DELETE FROM dtb_order_item WHERE shipping_id NOT IN( SELECT id FROM dtb_shipping ); ALTER TABLE dtb_order_item ADD CONSTRAINT FK_A0C8C3ED4887F3F8 FOREIGN KEY (shipping_id) REFERENCES dtb_shipping (id) ON DELETE RESTRICT ON UPDATE RESTRICT;
以下は参考までに、実際の案件で受注データを削除したときのコード FOREIGN_KEY_CHECKS を操作することにより、データの削除を可能としている 先頭に「plg_」が付いたテーブルは、プラグインが独自に作成したテーブル 末尾に「_cst」が付いたテーブルは、カスタマイズのため独自に作成したテーブル なので、必要に応じて省く必要がある
TRUNCATE TABLE dtb_mail_history; TRUNCATE TABLE dtb_order_cst; TRUNCATE TABLE dtb_order_item_cst; TRUNCATE TABLE dtb_order_pdf; TRUNCATE TABLE dtb_url_token; TRUNCATE TABLE plg_productset_order_item; TRUNCATE TABLE plg_vt4g_order_log; TRUNCATE TABLE plg_vt4g_order_payment; TRUNCATE TABLE plg_productoption_dtb_order_item_option_category; SET FOREIGN_KEY_CHECKS = 0; TRUNCATE TABLE plg_productoption_dtb_order_item_option; TRUNCATE TABLE dtb_order_item; TRUNCATE TABLE dtb_shipping; TRUNCATE TABLE dtb_order; SET FOREIGN_KEY_CHECKS = 1;
データベースをバックアップ&リストアする際に、リストア時に外部キーエラーになったとき、 SQLの最初と最後に以下のSQLを追加すると大丈夫だった 場合によるかもしれないので、あくまでも参考程度に
SET FOREIGN_KEY_CHECKS = 0; SET FOREIGN_KEY_CHECKS = 1;
なお、データベースの内容は即座に反映されないことがあるので注意 コンソールからキャッシュの削除をすると反映されることがある ■データベースの日時 データベース内の日時は、実際の日時より9時間前の時間が記録されている(UTCで記録されているので時差がある) ECCubeの仕様らしいが、SQLで直接データを集計するような場合に注意が必要 タイムゾーン - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/i18n_timezone EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=22997&forum=17 一例だが、以下のようにすると9時間前の状態で日時を扱うことができる
DATE_FORMAT(o.order_date, '%Y-%m-%d') ↓ DATE_FORMAT(DATE_ADD(o.order_date, INTERVAL 9 HOUR),'%Y-%m-%d')
■Git管理の考察 ※要検証&要検討 CSS管理、JavaScript管理、メール設定 などにより、サーバ上のファイルが編集される GitからのPULLによりプログラムをサーバ上に反映している場合、コンフリクトが発生する可能性がある Git管理をどうするかは検討が必要 管理画面からファイルを編集すると、最終行の改行を削除されてしまう エディタ内で改行を追加しても削除されてしまう そうでなくてもコンフリクトの原因になるので、管理画面からの編集は禁止しておくべきか ただし、それはそれで利便性を損なっている GitHubなら、サーバ上から定期的に自動コミットさせるという手はありそう 加えて、専用画面から任意のタイミングでもコミットできるツールや、 サーバ上のファイルに変更があったものを一覧&詳細表示するツールもあると良さそう それなら、Gitを扱う開発者が気を付けておけば、管理画面からはGitの知識がなくても操作できそう デプロイキーに書き込み権限を設定できない環境用には、サーバ上での差分一覧を表示するツールを作って手動で取り込むか ただしテスト環境(先方確認用環境)など他環境も作るなら、そこでの変更を取り込むのは問題がある 例えばdevelopブランチやreleaseブランチの内容をテスト環境に反映したとして、テストで変更された内容を取り込んでmasterにマージしてしまうことになる Git.txt の「独自ブランチモデルの考察」のブランチモデルをもとにした運用とし、testブランチの内容をテスト環境に反映するか テスト環境での変更は定期的にtestブランチに取り込むが、testブランチ自体を他ブランチにマージすることは無いため、何とかなりそう 例えば本番環境で差分の発生したファイルをダウンロードして、そのままmasterにコミット&プッシュして、そのまま本番環境にデプロイできるかは要検証 競合エラーになるとして、強制的にデプロイできるかも要検証 何かしら対応はできそうだが gitでリモートのブランチにローカルを強制一致させたい時 - Qiita https://qiita.com/ms2sato/items/72b48c1b1923beb1e186 git pull を強制し、リモートでローカルを上書きする方法 | WWWクリエイターズ http://www-creators.com/archives/1097 上記の考察を経て、いったんは ・デプロイの際に競合があれば、その内容をエラメッセージに表示できるようにデプロイツールを調整 ・競合ファイルとその内容を一覧表示できるツールを別途作成。ツール上から、ファイルごとにリセットもできるように ・リセットしていい変更ならリセットしてデプロイ、そうでなければ差分をGitに取り込んでからリセットしてデプロイ のように、ツールの改良と運用によって対応している ■バージョンの確認 管理画面では「設定 → システム設定 → システム情報」で確認できる ソースコードでは、Ver3系と同じく、src/Eccube/Common/Constant.php で定義されている EC-CUBE バージョン確認方法 - PukiWiki https://yassu.jp/pukiwiki/index.php?EC-CUBE%20%A5%D0%A1%BC%A5%B8%A5%E7%A5%F3%B3%CE%C7%A7%CA%FD%CB%A1 なお、ECCube4.0.4からPHP7.4に対応しているとのこと 最新版「EC-CUBE 4.0.4」をリリース。HTTP クッキーをより安全にする SameSite 属性やPHP7.4への対応と多数の機能改善。|ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/news/detail.php?news_id=356
■運用中のトラブル対応
■入金済みデータの入金キャンセル 管理画面で登録した「入金済み」をキャンセルする方法の考察 以下は銀行振込で検証したが、結論から書くとキャンセルには対応していないみたい 受注管理で「対応状況」が「新規受付」の受注は、「入金日」が空欄になっている この受注で「対応状況」を「入金済み」にすると「入金日」が記録される。一覧にも日付が表示されるようになる この状態から入金をキャンセルしようとしても、手段が見あたらない EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=8384&forum=11 ECCubeの仕様なのか不備なのかは不明だが、上記によると標準機能では対応していないみたい 売上集計や在庫数など他機能との兼ね合いもあっての仕様かもしれないので、安易なカスタマイズはリスクがあるかもしれないので注意 カスタマイズなしで対応するなら、対応状況を「注文取り消し」にし、管理画面から再度受注登録をするくらいか カスタマイズするなら、「新規受付」に戻せるようにしたうえで、入金日を編集できるように(空欄にも戻せるように)するか 対応状況をカスタマイズする方法自体は、このファイル内の「カスタマイズ: 対応状況の調整」を参照 ■入金済みデータの支払い方法変更 ユーザ側からの注文にて、 1. クレジットカード決済を選択し、クレジットカード番号入力画面まで進んだ 2. クレジットカード決済を完了せず、そのまま離脱した 3. 銀行振込を行った という操作をされた この受注に対して、管理画面から誤って「入金済み」に変更する操作をしてしまった 対応状況が「入金済み」となり、支払い方法が「クレジットカード決済」のまま変更できなくなったが、この支払い方法を「銀行振込」にしたい …という場合の作業内容 対象の注文番号が「6017」だったので、以下のSQLで対応した
UPDATE dtb_order SET payment_id = 1, payment_method = '銀行振込' WHERE id = 6017;
なお変更できないのは、クレジットカード決済は決済会社のシステム連動もあるため、手軽に変更することが望ましくないため…だと思われる 作業によって決済状況に矛盾が発生しないかなど、作業前に確認する ■発送後のキャンセル 対応状況が「発送済み」になると、「注文取り消し」にすることはできない 対応するなら「返品」にする必要がある 出荷した後なので自然な仕様ではあるが、操作ミスで「発送済み」にしてしまった場合はどうするか 暫定対策としては「対応状況を変更しようとしたら、データ登録前に確認ダイアログを表示する」などは考えられるかもしれない 「発送済み」から「注文取り消し」ができるように改造する場合、 「すでに出荷日が記録されているのでどうするか」など、関連するデータも巻き戻せるかなどの調査は必要そう 売上集計や在庫数など他機能との兼ね合いもあっての仕様かもしれないので、安易なカスタマイズはリスクがあるかもしれないので注意 対応状況をカスタマイズする方法自体は、このファイル内の「カスタマイズ: 対応状況の調整」を参照 ■クレジットカード決済のキャンセル このファイル内の「プラグインの導入: ECCube VeriTrans4G決済プラグイン(4.0系)」の「決済キャンセルへの対応」を参照
■メールの送信先
管理画面の「設定 → 店舗設定 → 基本設定」にて、以下のアドレスを登録できる ・送信元メールアドレス(From) ・問い合わせ受付メールアドレス(From, ReplyTo) ・返信受付メールアドレス(ReplyTo) ・送信エラー受付メールアドレス(ReturnPath) 「注文受付メールアドレス」が無いので注文されても管理者にメールは送信されず、注文内容は管理画面から確認するしかないのだろうか …と思ったが、src/Eccube/Service/MailService.php の350行目あたりを確認する限り、「送信元メールアドレス(From)」にBccで送られる
/** * Send order mail. * * @param \Eccube\Entity\Order $Order 受注情報 * * @return \Swift_Message */ public function sendOrderMail(\Eccube\Entity\Order $Order) { log_info('受注メール送信開始'); $MailTemplate = $this->mailTemplateRepository->find($this->eccubeConfig['eccube_order_mail_template_id']); $body = $this->twig->render($MailTemplate->getFileName(), [ 'Order' => $Order, ]); $message = (new \Swift_Message()) ->setSubject('['.$this->BaseInfo->getShopName().'] '.$MailTemplate->getMailSubject()) ->setFrom([$this->BaseInfo->getEmail01() => $this->BaseInfo->getShopName()]) ->setTo([$Order->getEmail()]) ->setBcc($this->BaseInfo->getEmail01()) ->setReplyTo($this->BaseInfo->getEmail03()) ->setReturnPath($this->BaseInfo->getEmail04());
また、注文ではなく問い合わせフォームから送信された場合は「問い合わせ受付メールアドレス(From, ReplyTo)」がBCCにセットされる まとめると ・送信元メールアドレスには、注文を受け付けたいメールアドレスを入力する ・問い合わせ受付メールアドレス、問い合わせフォームからの問い合わせ受け付けたいメールアドレスを入力する ・メールは、顧客に送られるものと同じものがBccで送られる という挙動となっている なおReturnPathなどは迷惑メール判定などにも使われるため、 「最終的には shop@example.com にメールが届くようにし、送信元もこのアドレスにする ただしテストのために一時的に test@refirio.net に注文メールなどを送信したい」 という場合、以下のように設定すると良さそう 送信元メールアドレス(From) : test@refirio.net 問い合わせ受付メールアドレス(From, ReplyTo) : test@refirio.net 返信受付メールアドレス(ReplyTo) : shop@example.com 送信エラー受付メールアドレス(ReturnPath) : shop@example.com ※Fromのアドレスが変わるので、問題があるか。要検証 ※テストが完了したら、「問い合わせ受付メールアドレス」も shop@example.com に変更する
■バックグラウンドで処理する
■Cronによる定期実行 ※未検証 ※Symfonyの作法に則ってCronを使うことはできるが、Supervisorを使う方がいいみたい? [EC-CUBE4]cronによる定期実行処理 - Qiita https://qiita.com/me-654393/items/f0bc9dbcc02d61684578 【EC-CUBE4系】Cronで定期実行するCommand処理を実装する | クマひよ工房 https://kumahiyo.com/cron-command/ 【EC-CUBE4】cronで定期実行するバッチ処理を実装 | Like Cloud https://like-cloud.com/504/ ■Supervisorによる定期実行 ※未検証 SymfonyのMessengerコンポーネントを使ってメール送信の非同期処理をEC-CUBE4.1で実装する方法 - あずみ.net http://a-zumi.net/symfony-messenger-sendmail-async-eccube41/ Laravelでも似たような実行方法はあるみたい 【Laravel5.5】EC2にSupervisorを入れてQueueのリスナーを監視する|Laravel|PHP|開発ブログ|株式会社Nextat(ネクスタット) https://nextat.co.jp/staff/archives/218
■インボイスに対応する
カスタマイズで対応する場合、以下が参考になりそう 【2023年10月1日施行】インボイス制度とは?EC-CUBEで対応すべき内容を解説 | ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/know_how/invoice.php
■カスタマイズ
WebデザイナーがEC-CUBE4を触る為に知っておくと良い事 | EC-CUBE CORPORATE BLOG https://www.wantedly.com/companies/ec-cube/post_articles/199543 EC-CUBE4 Template for Adobe XD on Behance https://www.behance.net/gallery/94593359/EC-CUBE4-Template-for-Adobe-XD EC-CUBE 4.0 開発者向けドキュメント - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/ ■ファイル構成 ディレクトリ・ファイル構成 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/spec_directory-structure src内にECCubeのプログラムが格納されているが、原則この中は編集しない app内にファイルをコピーし、そのコピーしたファイルを編集する ただしPHPプログラムの場合、以下のように namespace の調整が必要になる可能性があるので注意 (以下は src\Eccube\Service\OrderStateMachine.php を複製して app\Customize\Service\OrderStateMachine.php を作成した場合)
namespace Eccube\Service; ↓ namespace Customize\Service;
コントローラやビューは上記のとおりapp内にコピーを作成すればいいが、 ファイルによってはsrc内のファイルを直接編集する必要があるかもしれない 都度要確認 ■ルート定義の確認 Symfonyのルート定義は、yamlで定義する方法とアノテーションで定義する方法の2種類がある ECCubeではアノテーション方式が採用されている アノテーションは便利ではあるが、全体を把握しにくいデメリットがある 新しいページを作成しようとした際にルート名が既にあるかなど確認したければ、 以下のコマンドでルート定義一覧を確認できる
$ php bin/console debug:router ---------------------------------------------- -------- ------------ ------ ----------------------------------------------------------------- Name Method Scheme Host Path ---------------------------------------------- -------- ------------ ------ ----------------------------------------------------------------- admin_login ANY https|http ANY /ecadmin/login admin_homepage ANY https|http ANY /ecadmin/ admin_homepage_sale ANY https|http ANY /ecadmin/sale_chart admin_change_password ANY https|http ANY /ecadmin/change_password admin_homepage_nonstock ANY https|http ANY /ecadmin/search_nonstock 〜中略〜 vt4g_plugin_shopping_payment_recv ANY https|http ANY /shopping/vt4g_payment_recv mypage_vt4g_account_id ANY https|http ANY /mypage/vt4g_account_id vt4g_account_id_card_delete ANY https|http ANY /vt4g_account_id/{cardId}/card_delete news_detail GET https|http ANY /news/{id} ---------------------------------------------- -------- ------------ ------ -----------------------------------------------------------------
■キャッシュの扱い コントローラやテンプレートは、ECCube設置ディレクトリ内の html/var/cache 内にキャッシュが作成される 具体的には html/var/cache/prod/twig/b3/b3e156cd992b8f915fb07ae6291c4614787a7b3146f1c86e8ffb6aa78ddcd6f9.php のような場所に作成される ECCubeのキャッシュはなかなか強力で、これが原因で正しく動作しないことが多々ある 意図したとおりに反映&動作しなければ、まずはキャッシュの全削除を試すといい。以下のコマンドでキャッシュを削除できる 次回アクセス時にキャッシュが再生成される(初回アクセス時は非常に重いので注意)
$ cd /var/www/main/html $ php bin/console cache:clear --no-warmup
もしくは管理画面の「コンテンツ管理 → キャッシュ管理」からキャッシュを削除することもできる ただし上記のようにコマンドで削除するほうが確実かもしれない 管理画面からキャッシュを削除しても挙動がおかしければ、コマンドでの削除を試す キャッシュの生成は
$ php bin/console cache:warmup
を実行することでも対応できる …が、ローカルのVagrant環境で試すと 「Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 524288 bytes)」 のエラーになった。ブラウザからアクセスしてキャッシュを再生成させると大丈夫だった なお、部分的な表示変更のための編集なら html/var/cache/prod/twig/b3/b3e156cd992b8f915fb07ae6291c4614787a7b3146f1c86e8ffb6aa78ddcd6f9.php などをピンポイントで手動削除すれば高速に表示確認できる 上記だと対象ファイルを探すのが面倒だが html/var/cache/prod/twig をフォルダごと削除する方法なら、対象は明確だし次回の表示もそれほど時間はかからない この方法が一番無難かもしれない また、.env で以下のように設定すると、キャッシュが作成されないようになり、デバッグバーが表示される(開発用) ただし一部キャッシュなしで動作するので、キャッシュ再生成時ほどではないがそれなりに重い
APP_ENV=prod APP_DEBUG=0 ↓ APP_ENV=dev APP_DEBUG=1
なおVagrantを使用している場合、共有フォルダをNFSにすると大幅に改善する可能性がある Vagrant(VirtualBox)でディスクアクセスが遅い問題の対処法 https://masshiro.blog/vagrant-laravel-slow/ EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=20574&forum=2 ■テンプレートの編集 以下の場所にテンプレートがある html\src\Eccube\Resource\template 例えば以下のファイルを編集すると、ログイン画面に反映される html\src\Eccube\Resource\template\default\Mypage\login.twig ただし元のファイルは直接編集せずに、以下に複製配置して編集することが推奨される(コントローラなど他ファイルも同様) html\app\template\default\Mypage\login.twig 反映されない場合はキャッシュを削除する テンプレートは管理画面の「オーナーズストア → テンプレート → テンプレート一覧」から確認できる ダウンロードすると、CSSファイルや画像ファイルに加え、上の手順で複製配置した差分ファイルを取得できる デザインテンプレートの基礎 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/design_template EC-CUBE4:デザインテンプレートのインストール | ITOBEN STYLE Blog https://itoben.com/blog/3751.html ■CSSファイルの編集 ※GulpでSassをビルドする手順があるが、エラーで進められず 詳細は「メモ」の「CSSファイルの編集」を参照 ■プレースホルダの変更 フォームの入力例は、以下のファイルで変更することができる プレースホルダ内にある文字列「5300001」などで検索すると、該当箇所を見つけることができる src\Eccube\Resource\locale\messages.ja.yaml ■メールテンプレートの追加 「受注編集」画面の最下部に「メールを作成」ボタンがあり、ここから送信ができる テンプレートを選択できるが、現状本番環境では「注文受付メール」のみになっている 以下によると、テンプレートの作成とデータベースの調整で対応できそう EC-CUBE4でメールテンプレートを追加する方法 | コトダマウェブ https://cotodama.co/ec-cube4_mail_template/ また、以下のプラグインを導入すれば管理画面から新規追加できるみたい(有料&未検証) 4.0系|メールテンプレート登録プラグイン for EC-CUBE4|トエビス株式会社 https://www.ec-cube.net/products/detail.php?product_id=1941 ■納品書ロゴの変更 注文管理で納品書を出力したとき、ECCubeのロゴが表示される。これを差し替える方法 管理画面「コンテンツ管理 → ファイル管理」から「asset → pdf」内の「logo.png」に上書きアップロードする EC-CUBE4で納品書ロゴを変更する(4.0.3〜) - Qiita https://qiita.com/kazumiiiiiiiiiii/items/f2e5bdfb902b1b94c00a ■404ページの変更 初期デザインは素っ気ないものなので、サイトに合わせてデザインされたページにしておくといい src\Eccube\Resource\template\default\error.twig を複製して app\template\default\error.twig を作成し、任意の内容に変更する ■メンテナンスモード キャッシュクリア時やプラグインのインストール途中など、 ユーザ側にアクセスしてほしくない場合はメンテナンスモードを利用できる 以下のようにすると、ユーザ側の表示がメンテナンス画面に変更される
$ cd /var/www/main/html $ touch .maintenance
以下のようにすると、メンテナンス画面が解除される
$ cd /var/www/main/html $ rm .maintenance
なお、この機能は管理画面からも利用できる 管理画面の「コンテンツ管理 → メンテナンス管理」から、メンテナンスモードの有効・無効を切り替えることができる メンテナンス機能を追加 by okazy - Pull Request #3998 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/pull/3998 ■税抜価格(税別価格)で表示 プラグインがあるくらいなので、標準機能では対応していないみたい 4.0系|割引率表示、税抜表示プラグイン(EC-CUBE4.0系対応)|株式会社YMK https://www.ec-cube.net/products/detail.php?product_id=2074 ただし、ユーザ側の表示に限定すれば大変な改造にはならないかもしれない 「カートに入れたときの表示」「注文メールの内容」「マイページでの表示」などはあるので、あちこち調整が必要になるかもしれないが EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?viewmode=flat&topic_id=22003&forum=10 ※以下「法律.txt」にも同じ内容を記載している 消費税転嫁対策特別措置法により、税抜表示が問題になる可能性があるらしい? 要勉強 消費税転嫁対策特別措置法 | 消費者庁 https://www.caa.go.jp/policies/policy/representation/consumption_tax/ 10%増税に備えよう!消費税転嫁対策特別措置法まとめ https://biz.moneyforward.com/accounting/basic/13868/ 消費増税前に再確認! 消費税転嫁対策特別措置法って? - マネーイズム https://www.all-senmonka.jp/moneyizm/1088/ ■商品ごとに税率を設定する バージョン4.0.3からは対応している ただし4.0.3時点では商品別税率設定の税率が正しく反映されないケースがあるとのこと(以下ページ参照) またプラグインや表示カスタマイズ部分も含めて対応できているか、は何とも言えないので、設定する前にひととおりの検証は必要 EC-CUBE 4.0.3 で改善された軽減税率制度関連機能と設定方法を見てみよう! - ネットショップの壺 https://tsubo.ec-cube.net/2019/09/09/10674 ■商品登録時の価格を内税にする 税率設定を0%に設定し、商品登録時の商品価格を税込み価格で登録することで、全ての価格を内税(税込)運用とすることはできるみたい ただし表示部分のテンプレートは編集する必要はあるみたい EC-CUBE4設定>店舗設定>税率設定 | EC-CUBE4 管理・運用マニュアル | shiro8 https://www.shiro8.net/manual4/v40x/setting/shop_tax.html 税率設定 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/spec_tax ■タグによる検索 商品情報として「タグ」を登録できる ただし標準機能では、このタグをもとに絞り込み検索する機能は無いみたい EC-CUBE4のフロントの検索ボックスで商品タグ検索できるようにする方法 - あずみ.net https://a-zumi.net/eccube4-tag-search/ [EC-CUBE4]タグIDで商品一覧を絞り込めるようにするよー | Hiroki's toy box https://blugrit.com/2020/04/05/ec-cube4-product-list-tag-id/ 4.0系|商品タグ検索プラグイン for EC-CUBE4|あずみ.net https://www.ec-cube.net/products/detail.php?product_id=1862 4.0系|商品タグ機能拡張(4.0対応版)|systemkd https://www.ec-cube.net/products/detail.php?product_id=1709 ■商品一覧の表示件数を変更する 管理画面の「設定 → システム設定 → マスターデータ管理」で mtb_product_list_max をいじることで、ユーザ側での商品一覧の表示件数を変更できる データの並びを変更すれば、デフォルトの指定も変更できる(一番上のデータがデフォルトの指定となる) EC-CUBE4で商品一覧のソート順を「新着順」を初期表示にする - Qiita https://qiita.com/chihiro-adachi/items/279a05d833e87bc84076 ■商品一覧の並び順を「新着順」を初期表示にする 管理画面の「設定 → システム設定 → マスターデータ管理」で mtb_product_list_order_by をいじることで、ユーザ側での商品一覧の並び順を追加できる データの並びを変更すれば、デフォルトの指定も変更できる(一番上のデータがデフォルトの指定となる) ■商品一覧の並び順を任意のものに変更する 管理画面の商品一覧で、簡単に並び順を変更する機能は無いみたい プログラムを改造するか、プラグインを探すなどする必要がある [EC-CUBE 4] 商品一覧ページのソート順をカスタマイズ - スプレッドワークス - Web制作会社/システム開発 - 東京都豊島区南池袋 https://www.spreadworks.co.jp/product-list-sort-order-change-for-ec-cube4/ EC-CUBE4の管理画面で「商品」「受注」「会員」一覧の並び順を変更する方法 | TechMemo https://techmemo.biz/ec-cube/eccube4-admin-list-orderby/ 4.0系|商品並び替えプラグイン(CSV一括更新対応) for EC-CUBE4|あずみ.net https://www.ec-cube.net/products/detail.php?product_id=1890 「売上順」もしくは「売れ筋順」でよければ、以下のプラグインによって対応できる 4.0系|売上順・売れ筋順並び替えプラグイン for EC-CUBE4|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=2006 ■受注一覧の「対応状況」の色 「システム設定 → マスターデータ管理」の mtb_order_status_color で色を変更できる ステータスが違っても色が同じになっているものがあるので、判りづらいようなら必要に応じて変更する ■受注編集時に「注文者」が折りたたまれないように 「受注管理」で受注を編集する際、「注文者」がデフォルトで折りたたまれている これをデフォルトで折りたたまれないようにする src/Eccube/Resource/template/admin/Order/edit.twig を複製して app/template/admin/Order/edit.twig を作成し、 360行目あたりあたりにある以下の部分をそれぞれ調整する
<i class="fa {{ id ? 'fa-angle-down' : 'fa-angle-up' }} fa-lg"></i></a></div> ↓ <i class="fa fa-angle-up fa-lg"></i></a></div>
<div class="collapse {{ id ? '' : 'show' }} ec-cardCollapse h-adr" id="ordererInfo"> ↓ <div class="collapse show ec-cardCollapse h-adr" id="ordererInfo">
■受注CSVに送料と手数料を出力しない EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=21851&forum=11 src\Eccube\Controller\Admin\Order\OrderController.php の415行目あたり、
// データ行の出力. $this->csvExportService->setExportQueryBuilder($qb); $this->csvExportService->exportData(function ($entity, $csvService) use ($request) { $Csvs = $csvService->getCsvs(); $Order = $entity; $OrderItems = $Order->getOrderItems(); foreach ($OrderItems as $OrderItem) {
この直後に以下を追加して対応できた ロジックとしては「送料と手数料を省く」というより「商品コードがなければ省く」となっている
//商品コードがなければループをスキップする if (!$OrderItem->getProductCode()) { continue; }
■購入時に画像を添付 ※未検証 標準機能では対応できないようだが、以下で改造方法が紹介されている 【EC-CUBE4】購入手続き時にファイルをアップロードできる機能を実装する方法 - あずみ.net https://a-zumi.net/eccube4-shopping-file-upload/ ■カートの内容を参照 カートの情報はデータベースの dtb_cart テーブルに記録されている。 カート内商品の情報は dtb_cart_item テーブルに記録されている。 この情報をもとに ・どの会員のカートに何が格納されているか ・商品と合計金額は何か ・最後にカートが操作されたのはいつか などは確認できそう。 上記の内容を管理ページで一覧表示できれば十分、ならプラグインを使うほどでは無いと思われる。 なお、ログインせずにカートに入れると dtb_cart テーブルの customer_id はNULLになっている ■代理キーでのデータ判定について考察 例えば「クレジットカード決済を選択したら○○を表示する」のような分岐をしたい場合、データの特定には ・代理キー ・名称 のいずれかを使うことになる(code や key のような値を持っていない) 「代理キー」で分岐を作ると「5なら○○を表示する」のような代理キーに依存した作りになってしまうし、「名称」で分岐を作ると名前の変更が難しくなる ECCubeの仕様と妥協するしか無いかもしれないが、以下に管理方法の例を記載する 「代理キー5はクレジットカード決済」のような情報はyamlファイルで設定として持っておく また app/config/eccube/packages/eccube.yaml を参考にして、 「ECCUBE_COOKIE_NAME などは初期値があるが、.env から変更もできる」 という仕組みにしておくと、環境によって代理キーが変わる場合でも柔軟に対応できる eccube.yaml の扱いは、このファイル内にある「運用のためのメモ」の「環境による分岐」も参照 app/config/eccube/packages/eccube.yaml ではなく app/Customize/Resource/config/services.yaml などを作って設定する方がいいかもしれないが、詳細は引き続き要検証 ■その他 このファイル内の「仕事で使う場合の注意」を参考に、必要に応じてカスタマイズを行う
■カスタマイズ: バリデーションの調整
FormTypeのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_formtype フォームの入力項目に独自のバリデーションルールを追加する - 320321.net https://320321.net/2020/11/ec-cube4-add-validation-rule/ バリデーションは、FormExtension拡張してカスタマイズするのがお作法となっている 具定例として、電話番号のバリデーションを改良してみる 標準の入力チェックでは、電話番号は「数字でなければエラー」としかチェックされない これを「ゼロからはじまる10桁もしくは11桁の数字で入力されなければエラー」としてみる 以下のファイルを確認すると、電話番号のバリデーションは PhoneNumberType が使用されている src\Eccube\Form\Type\Front\EntryType.php PhoneNumberTypeは以下にある src\Eccube\Form\Type\PhoneNumberType.php 上記ファイルをもとに、以下のファイルを作成する app\Customize\Form\Extension\PhoneNumberTypeExtension.php
<?php namespace Customize\Form\Extension; use Eccube\Common\EccubeConfig; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints as Assert; use Eccube\Form\Type\PhoneNumberType; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\DependencyInjection\ContainerInterface; class PhoneNumberTypeExtension extends AbstractTypeExtension { /** * @var EccubeConfig */ private $eccubeConfig; /** * @var ValidatorInterface */ protected $validator; /** * @var ContainerInterface */ protected $container; public function __construct(ContainerInterface $container, ValidatorInterface $validator, EccubeConfig $eccubeConfig) { $this->eccubeConfig = $eccubeConfig; $this->validator = $validator; $this->container = $container; } /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { // 全角英数を事前に半角にする $builder->addEventSubscriber(new \Eccube\Form\EventListener\ConvertKanaListener()); $builder->addEventSubscriber(new \Eccube\Form\EventListener\TruncateHyphenListener()); // 電話番号 バリデーション $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { $form = $event->getForm(); $data = $form->getData(); if (!empty($data)) { if (!preg_match('/^0\d{9,10}$/', $data)) { $form->addError(new FormError('電話番号はゼロからはじまる10桁もしくは11桁の数字で入力してください')); } } }); } /** * {@inheritdoc} */ public function getExtendedType() { return PhoneNumberType::class; } }
これで注文時のお客さま情報入力、お問い合わせ入力、管理画面など、 電話番号の入力部分でバリデーションルールが変化する なお、上記バリデーションの正規表現は以下を参考にしている まれによく使う正規表現 - Qiita https://qiita.com/ken97/items/9276f59f3148bb8fbe67 ただし、今後は14桁の電話番号も使われる予定があるらしい 今の時点から、桁数制限は「最大14桁」としておく方が無難そう 総務省、M2M用の「020」番号を14桁化へ - ケータイ Watch https://k-tai.watch.impress.co.jp/docs/news/1219218.html
■カスタマイズ: 送料の調整
※以下が参考になるかもしれない、というメモ ※詳細は要検証 EC-CUBE アーカイブ - あずみ.net https://a-zumi.net/category/ec-cube/ EC-CUBE4で商品送料が設定されている場合に一番高い送料を適用させる方法 - あずみ.net https://a-zumi.net/eccube4-highest-delivery-fee-preprocessor/ C:\Users\Yamano\Vagrant\eccube4\code\main\html\src\Eccube\Service\PurchaseFlow\Processor\DeliveryFeePreprocessor.php 120行目あたりに $deliveryFeeProduct を操作する処理があるが、ここに加算すると送料が増える 135行目に
$deliveryFeeProduct = 999;
を追加すると、送料は1999円になった 1000円は配送業者によるもので、999円が各商品にかかった合計の送料だと思われる よって、ここを編集すれば送料の計算をある程度自由に変更できるか もちろん、実際に改造する場合は src 内ではなく app 内で行う …が、以下にファイルを配置しても src 側のプログラムが反映される? C:\Users\Yamano\Vagrant\eccube4\code\main\html\app\Customize\Service\PurchaseFlow\Processor\DeliveryFeePreprocessor.php キャッシュなどの問題?それとも、Serviceは単純な上書きができない?また確認する もしくは以下を参考に、src を複製する以外の方法をとれるか EC-CUBE4で商品送料が設定されている場合に一番高い送料を適用させる方法 - あずみ.net https://a-zumi.net/eccube4-highest-delivery-fee-preprocessor/ 以下も参考になりそう ショッピングカートの計算や受注処理、商品購入の流れ等を制御する仕組みは、購入フロー(PurchaseFlow)と呼ばれるもので管理されている 購入フローに関しては、後述の「カスタマイズ: 購入フローの調整」も参照
■カスタマイズ: 対応状況の調整
■対応状況の追加 設定 → システム設定 → マスタデータ管理 → mtb_order_status でステータスを管理できる ただしステータス遷移なども定義が必要 詳細はこの後の「対応状況の遷移を変更」を参照 ■対応状況の遷移を変更 EC-CUBE4で受注ステータスを追加する - Qiita https://qiita.com/chihiro-adachi/items/a7c518f49c0182f297fa 例えば「後払い」という決済方法があったとして、ステータスを 「新規受付 → 対応中 → 発送済み」 のように変更したあとに「入金済み」に変更したくてもできない EC-CUBE4受注管理>受注一覧 | EC-CUBE4 管理・運用マニュアル | shiro8 https://www.shiro8.net/manual4/v40x/order/index.html 「※EC-CUBE4のステータスには「入金待ち」のステータスはありません。 受注データの処理中であっても、一度ステータスを「対応中」で登録してしまうと、ステータスを「入金済み」に切り替える事は出来ません。 入金が確認出来るまでの受注データは「新規受付」のステータスのまま保持しておく必要がありますが、 日々新規オーダーは入りますので、入金の突合せの際は新規受付の中から探し出す必要がある為、やや使い辛いかもしれません。」 これはECCube4の仕様らしいが、これを変更可能にしてみる まず前提として、ECCubeのステータスにはデフォルトで以下がある (「購入処理中」は、「ご注文手続き」画面まで進んだが注文はまだ完了していない、というもの)
Status::NEW ... 新規受付 Status::CANCEL ... 注文取消し Status::IN_PROGRESS ... 対応中 Status::DELIVERED ... 発送済み Status::PAID ... 入金済み Status::PENDING ... 決済処理中 Status::PROCESSING ... 購入処理中 Status::RETURNED ... 返品
処理の際にこれらの値を使用する また対応状況は、以下のように遷移する 受注関連 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/spec_order 以降は具体的な実装方法 まず、app\config\eccube\packages\order_state_machine.php の68行目あたりに以下を追加する
'back_to_paid' => [ 'from' => [(string) Status::IN_PROGRESS, (string) Status::DELIVERED], 'to' => (string) Status::PAID, ],
これで「対応中」もしくは「発送済み」から「入金済み」に遷移できる ただしこの時点では、「入金済み」に遷移しても入金日時が記録されない src\Eccube\Service\OrderStateMachine.php を複製して app\Customize\Service\OrderStateMachine.php を作成し、14行目あたりにある namespace を変更する
namespace Eccube\Service; ↓ namespace Customize\Service;
さらに110行目あたり、getSubscribedEvents() 関数内の戻り値に以下を追加する
'workflow.order.transition.back_to_paid' => ['updatePaymentDate'],
これで「対応中」もしくは「発送済み」から「入金済み」に遷移したとき、updatePaymentDate() が実行される これにより、入金日時が記録される データベースでの扱いについては、このファイル内の「受注データのステータス」を参照 ■対応状況を追加する際の注意 独自のステータスを追加すると、ECCubeが想定する運用の流れから外れる可能性があるので注意する それによって、プラグインが対応できなくならないか、など またあまり色々追加すると、独自の仕様によって運用がややこしくなりそうな懸念がある
■カスタマイズ: 購入フローの調整
※要調査 【EC-CUBE4】合計金額3000円未満の場合は送料に300円加算するプラグインを作る方法 - あずみ.net https://a-zumi.net/eccube4-delivery-fee/ EC-CUBE4 送料計算をカスタマイズする - PukiWiki https://yassu.jp/pukiwiki/index.php?EC-CUBE4%20%C1%F7%CE%C1%B7%D7%BB%BB%A4%F2%A5%AB%A5%B9%A5%BF%A5%D... EC-CUBE4のPurchaseFlowをざっくり理解するたった1つの冴えたやり方 - Qiita https://qiita.com/junpeko5/items/3518ac18a6f8c3fc33c2 EC-CUBE4で特定の都道府県のみ送料無料条件の適用を外す方法 - あずみ.net https://a-zumi.net/eccube4-no-free-shipping-pref/ EC-CUBE4で商品送料が設定されている場合に一番高い送料を適用させる方法 - あずみ.net https://a-zumi.net/eccube4-highest-delivery-fee-preprocessor/ EC-CUBE アーカイブ - あずみ.net https://a-zumi.net/category/ec-cube/ EC-CUBE/ec-cube: EC-CUBE is the most popular e-commerce solution in Japan https://github.com/EC-CUBE/ec-cube ec-cube/DeliveryFeePreprocessor.php at 4.0 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/blob/4.0/src/Eccube/Service/PurchaseFlow/Processor/DeliveryFeePre... 購入フローのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_service
■カスタマイズ: ページの作成
■下層ページの作成(管理画面から作成) 管理画面の「コンテンツ管理 → ページ管理 → 新規作成」から作成できる ヘッダなどが表示されたページにしたければ、「レイアウト設定」で「下層ページ用レイアウト」などを選択する EC-CUBE4で新しくページを追加する方法 | ホムペディア https://homupedia.com/eccube4-create-pages.html レイアウトの利用 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/design_layout URLには「user_data」という文字が含まれるが、これは設定で他のものに変更できる EC-CUBE4でuser_dataを変更する - Qiita https://qiita.com/chihiro-adachi/items/3218275ea4fc6d485fe5 ■下層ページの作成(コントローラを用意して作成) Controllerのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_controller コントローラを作成 html\app\Customize\Controller\TestPageController.php
<?php namespace Customize\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Component\HttpFoundation\Response; class TestPageController { /** * @Route("/test") */ public function testMethod() { return new Response('Hello test page !'); } }
以下にアクセスすると「Hello test page !」と表示される 反映されない場合はキャッシュを削除する http://eccube4.local/test ■下層ページの作成(コントローラを用意して作成 / テンプレートに対応) 引き続き、テンプレートファイルに対応させてみる html\app\Customize\Controller\TestPageController.php
<?php namespace Customize\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Response; class TestPageController { /** * @Route("/test") * @Template("Test/index.twig") */ public function testMethod() { return ['message' => '下層ページの作成']; } }
html\app\template\default\Test\index.twig
<h2>Hello !</h2> <p>message={{ message }}</p>
以下にアクセスすると「Hello ! message=下層ページの作成」と表示される http://eccube4.local/test テンプレートファイルを以下のようにすると、bodyタグなどが反映される ただしこの時点では、共通のヘッダやフッタは表示されない html\app\template\default\Test\index.twig
{% extends 'default_frame.twig' %} {% block main %} <h2>Hello !</h2> <p>message={{ message }}</p> {% endblock %}
Controllerのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_controller ■下層ページの作成(コントローラを用意して作成 / データベースの参照) 引き続き、データベースを参照してみる html\app\Customize\Controller\TestPageController.php
<?php namespace Customize\Controller; use Eccube\Repository\ProductRepository; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Response; class TestPageController { /** * @var ProductRepository */ protected $productRepository; /** * ProductController constructor. * * @param ProductRepository $productRepository */ public function __construct( ProductRepository $productRepository ) { $this->productRepository = $productRepository; } /** * @Route("/test/{id}", requirements={"id" = "\d+"}, name="test") * @Template("Test/index.twig") */ public function testMethod($id) { $Product = $this->productRepository->find($id); if (!$Product) { throw new NotFoundHttpException(); } return [ 'message' => '下層ページの作成', 'Product' => $Product, ]; } }
html\app\template\default\Test\index.twig
{% extends 'default_frame.twig' %} {% block main %} <h2>Hello !</h2> <p>message={{ message }}</p> <p>{{ Product.id }}: {{ Product.name }}</p> {% endblock %}
以下にアクセスすると、URLに応じて「1: 彩のジェラートCUBE」のように商品情報が表示される http://eccube4.local/test/1 http://eccube4.local/test/2 ■下層ページの作成(コントローラを用意して作成 / ヘッダとフッタを表示) 引き続き、ヘッダとフッタを表示してみる レイアウトは管理画面から設定を行うが、何もしなければ管理画面には認識されない まずは以下の情報をデータベースに登録する(ただし実際は、データはマイグレーションで追加する方がいい)
INSERT INTO `dtb_page` (`master_page_id`, `page_name`, `url`, `file_name`, `edit_type`, `author`, `description`, `keyword`, `create_date`, `update_date`, `meta_robots`, `meta_tags`, `discriminator_type`) VALUES ( NULL, 'テストページ', /* ページ名 */ 'test', /* url */ 'Test/index', /* テンプレートファイル */ 2, NULL, NULL, NULL, NOW(), NOW(), NULL, NULL, 'page' );
マイグレーションの詳細は、このファイル内の「マイグレーションでのデータ管理」を参照 以下はマイグレーションで追加する場合の例(コマンドで作成したマイグレーションファイルを編集している)
$ php bin/console doctrine:migrations:generate
app\DoctrineMigrations\Version20200820032003.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200820032003 extends AbstractMigration { public function up(Schema $schema) : void { $sql =<<<EOL INSERT INTO `dtb_page` (`master_page_id`, `page_name`, `url`, `file_name`, `edit_type`, `author`, `description`, `keyword`, `create_date`, `update_date`, `meta_robots`, `meta_tags`, `discriminator_type`) VALUES ( NULL, 'テストページ', /* ページ名 */ 'test', /* url */ 'Test/index', /* テンプレートファイル */ 2, NULL, NULL, NULL, NOW(), NOW(), NULL, NULL, 'page' ); EOL; $this->addSql($sql); } public function down(Schema $schema) : void { $sql =<<<EOL DELETE FROM dtb_page WHERE url = 'test'; EOL; $this->addSql($sql); } }
$ php bin/console doctrine:migrations:migrate
これで管理画面「コンテンツ管理 → ページ管理」に「テストページ」が表示される このページに対して設定を行う 「レイアウト設定」で「PC」を「下層ページ用レイアウト」にして「登録」ボタンを押す この作業により、今回は以下のデータが登録された
INSERT INTO `dtb_page_layout` VALUES (46, 2, 41, 'pagelayout');
これで以下にアクセスすると、ヘッダやフッタ付きでページが表示される http://eccube4.local/test/1 dtb_pageテーブルへのデータ追加はどうしても発生するようなので、 本番運営の際はこのデータはマイグレーションで追加する方が無難か dtb_page_layoutのデータは管理画面からの操作で作成されるので、マイグレーションに含めない方が無難か 要検証 [EC-CUBE 4]コントローラを利用するページを追加してみる | creatorlab https://www.creatorlab.jp/2019/07/10/eccube4%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E... ■下層ページの作成(コントローラを用意して作成 / 商品一覧と詳細を表示) html\app\Customize\Controller\TestPageController.php
<?php namespace Customize\Controller; use Eccube\Repository\ProductRepository; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Response; class TestPageController { /** * @var ProductRepository */ protected $productRepository; /** * ProductController constructor. * * @param ProductRepository $productRepository */ public function __construct( ProductRepository $productRepository ) { $this->productRepository = $productRepository; } /** * @Route("/test", name="test_index") * @Template("Test/index.twig") */ public function testIndex() { $Products = $this->productRepository->findAll(); return [ 'message' => '商品一覧の作成', 'Products' => $Products, ]; } /** * @Route("/test/{id}", requirements={"id" = "\d+"}, name="test_detail") * @Template("Test/detail.twig") */ public function testDetail($id) { $Product = $this->productRepository->find($id); if (!$Product) { throw new NotFoundHttpException(); } return [ 'message' => '商品詳細の作成', 'Product' => $Product, ]; } }
html\app\template\default\Test\index.twig
{% extends 'default_frame.twig' %} {% block main %} <h2>商品一覧</h2> <p>message={{ message }}</p> <ul> {% for Product in Products %} <li> <a href="/test/{{ Product.id }}"> {{ Product.id }} / {{ Product.name }} / {{ Product.create_date|date('Y-m-d H:i:s') }} </a> </li> {% endfor %} </ul> {% endblock %}
html\app\template\default\Test\detail.twig
{% extends 'default_frame.twig' %} {% block main %} <h2>商品詳細</h2> <p>message={{ message }}</p> <dl> <dt>id</dt> <dd>{{ Product.id }}</dd> <dt>name</dt> <dd>{{ Product.name }}</dd> <dt>create_date</dt> <dd>{{ Product.create_date|date('Y-m-d H:i:s') }}</dd> </dl> {% endblock %}
■管理画面内ページの作成 html\app\Customize\Controller\Admin\Product\TestController.php
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Controller\Admin\Product; use Eccube\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class TestController extends AbstractController { /** * @Route("/%eccube_admin_route%/test", name="admin_test") * @Template("@admin/Product/test.twig") */ public function index(Request $request) { } }
html\app\template\admin\Product\test.twig
{% extends '@admin/default_frame.twig' %} {% set menus = ['product'] %} {% block title %}タイトル{% endblock %} {% block sub_title %}サブタイトル{% endblock %} {% block main %} <p>テストです。</p> {% endblock %}
これでキャッシュを削除してから http://eccube4.local/system/test のように管理画面にアクセスすると、作成したページが表示される ヘッダやサイドバーは自動的に表示された EC-CUBE4カスタマイズ - 4系で管理画面に2ステップで新規ページを作る方法 https://umebius.com/eccube/eccube4_create_new_admin_page/ ■管理画面内ページの作成(メニューに表示) app\config\eccube\packages\eccube_nav.yaml
category_csv_import: name: admin.product.category_csv_upload url: admin_product_category_csv_import の直下に以下を追加 product_test: name: "Test Page" url: admin_test
これで管理画面の「商品管理 → カテゴリCSV登録」の下に「Test Page」が表示され、 クリックすると上記で作成したページに遷移する 項目名を日本語化したい場合、外部リソースにする必要があるみたい(未検証) EC-CUBE4カスタマイズ - 4系で管理画面に新規メニュー項目を追加する方法 https://umebius.com/eccube/ecceube4_insert_new_admin_menu/ ■管理画面内ページの作成(データベースを参照) html\app\Customize\Controller\Admin\Product\TestController.php
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Controller\Admin\Product; use Eccube\Controller\AbstractController; use Eccube\Repository\ProductRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class TestController extends AbstractController { /** * @var ProductRepository */ protected $productRepository; /** * ProductController constructor. * * @param ProductRepository $productRepository */ public function __construct( ProductRepository $productRepository ) { $this->productRepository = $productRepository; } /** * @Route("/%eccube_admin_route%/test/{id}", requirements={"id" = "\d+"}, name="admin_test") * @Template("@admin/Product/test.twig") */ public function index(Request $request, $id) { $Product = $this->productRepository->find($id); if (!$Product) { throw new NotFoundHttpException(); } return [ 'Product' => $Product, ]; } }
html\app\template\admin\Product\test.twig
{% extends '@admin/default_frame.twig' %} {% set menus = ['product'] %} {% block title %}タイトル{% endblock %} {% block sub_title %}サブタイトル{% endblock %} {% block main %} <p>テストです。</p> <p>{{ Product.id }}: {{ Product.name }}</p> {% endblock %}
■カスタマイズ: ブロックの追加
静的なブロックの追加は、管理画面からの操作で行える ただし、各環境に反映するためにはマイグレーションが必要 ブロックの利用 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/design_block EC-CUBE4で新しくブロックを追加する方法 | ホムペディア https://homupedia.com/eccube4-create-blocks.html 【EC-CUBE4】新着商品ブロックの商品一覧を動的に取得する方法 - あずみ.net https://a-zumi.net/eccube4-dynamic-new-item-block/ 動的なブロックの追加は、専用にExtensionを作成する流れになるみたい? 要検証 ■ブロックの追加例 以下は実際の案件でブロックを追加したときのコード html\app\template\default\Block\left_column.twig
{# トップページや下層ページの左カラムに表示するコンテンツ #} <div class="column_side"> <dl class="column_side_cate"> <dt>商品カテゴリ</dt> {% set Categories = repository('Eccube\\Entity\\Category').getList() %} {% set RecommendCategories = (eccube_recommend_category_id|split(',')) %} {% for Category in Categories %} <dd><a href="{{ url('product_list') }}?category_id={{ Category.id }}"><img src="{{ asset('assets/img/common/category/' ~ Category.id ~'.jpg', 'user_data') }}" alt="">{{ Category.name }}{% if Category.id in RecommendCategories %}<em>オススメ</em>{% endif %}</a></dd> {% endfor %} </dl> <!-- <ul class="column_side_bnr"> <li><a href="{{ url('product_list') }}?category_id=1"><img src="{{ asset('assets/img/common/banner/1.png', 'user_data') }}" alt=""></a></li> <li><a href="{{ url('product_list') }}?category_id=2"><img src="{{ asset('assets/img/common/banner/2.png', 'user_data') }}" alt=""></a></li> </ul> --> <!-- <ul class="column_side_nav"> <li><a href="#">よくあるご質問</a></li> <li><a href="#">各種ダウンロード</a></li> <li><a href="{{ url('help_tradelaw') }}">特定商取引法に基づく表記</a></li> <li><a href="{{ url('help_privacy') }}">個人情報保護方針</a></li> </ul> --> </div> <!-- /column_side -->
上記テンプレートは、各テンプレートから以下のように呼び出す想定
{{ include('Block/left_column.twig') }}
ただし、ただテンプレートを追加しただけだと管理画面の「コンテンツ管理 → ブロック管理」には表示されない(管理画面から編集できない) ここに表示させたい場合、以下のようにマイグレーションを作成する html\app\DoctrineMigrations\Version20210604041526.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20210604041526 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs // dtb_block にレコード追加 $this->addSql("INSERT INTO dtb_block (device_type_id,block_name, file_name, use_controller,deletable,create_date,update_date,discriminator_type) VALUES (10, '左カラム(カテゴリ、バナー)','left_column',0, 1, NOW(), NOW(), 'block');"); } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs } }
■カスタマイズ: マイグレーションでのデータ管理
EC-CUBE4カスタマイズ - マイグレーションの作成・実行方法 https://umebius.com/eccube/eccube4-howto-migration/ EC-CUBE4でmigrationを使いこなす - Qiita https://qiita.com/yusukeito58/items/a33e2fd6d26c4fc29d9f マイグレーションでデータベースを管理できるが、データの編集に使うのが主な目的らしい ECCube3まではカラムの追加などをマイグレーションで行っていたが、ECCube4では考え方を変えたらしい 以下はマイグレーションを試したときのメモ テーブルの追加を試しているが、上記のようにテーブルの追加はEntityから行うのが正攻法みたい Entityでのテーブル作成&列追加は、このファイル内の「ORMでテーブルを作成」を参照 ■個別実行 以下のコマンドでマイグレーションを作成できる バージョン名は、コマンドを実行したときの日時が使われる
$ php bin/console doctrine:migrations:generate Generated new migration class to "/var/www/main/html/app/DoctrineMigrations/Version20200728034801.php"
以下のファイルが作成された 作成直後は、マイグレーションの中身はカラ html\app\DoctrineMigrations\Version20200728034801.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200728034801 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs } }
upとdownの内容を以下のように編集してみる つまり今回は、table_testテーブルの作成を行う
public function up(Schema $schema) : void { $sql =<<<EOL CREATE TABLE table_test( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', text VARCHAR(255) NOT NULL COMMENT 'テキスト', PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'テーブル操作テスト'; EOL; $this->addSql($sql); } public function down(Schema $schema) : void { $sql =<<<EOL DROP TABLE table_test; EOL; $this->addSql($sql); }
以下のコマンドでマイグレーションを実行できる
$ php bin/console doctrine:migrations:execute 20200728034801 --up WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y ++ migrating 20200728034801 -> CREATE TABLE table_test( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', text VARCHAR(255) NOT NULL COMMENT 'テキスト', PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'テーブル操作テスト'; ++ migrated (0.4s)
データベースを確認すると、table_test テーブルが作成されていた また、既存テーブル migration_versions の version 列に「20200728034801」が登録されていた これをもとに、どのマイグレーションが実行済みなのかを管理しているみたい 以下のコマンドでマイグレーションの巻き戻しを実行できる
$ php bin/console doctrine:migrations:execute 20200728034801 --down WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y -- reverting 20200728034801 -> DROP TABLE table_test; -- reverted (0.22s)
データベースを確認すると、table_test テーブルが削除されていた また、既存テーブル migration_versions の version 列に登録されていた「20200728034801」が削除されていた ■一括実行 以下のコマンドでマイグレーションを一括実行できる html\app\DoctrineMigrations には元からいくつかマイグレーションが入っているので、それらが実行される
$ php bin/console doctrine:migrations:migrate Application Migrations WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)y Migrating up to 20200303053716 from 0 ++ migrating 20181017090225 Migration 20181017090225 was executed but did not result in any SQL statements. ++ migrated (0.27s) ++ migrating 20181109101907 -> UPDATE dtb_page SET page_name = '商品購入/遷移', url = 'shopping_redirect_to' WHERE id = 42 ++ migrated (0.02s) ++ migrating 20190821081036 Migration 20190821081036 was executed but did not result in any SQL statements. ++ migrated (0.21s) ++ migrating 20200303053716 -> UPDATE dtb_delivery_duration SET duration = -1 WHERE id = 9 and duration = 0 ++ migrated (0.02s) ------------------------ ++ finished in 0.52s ++ 4 migrations executed ++ 2 sql queries
もう一度実行すると、実行すべきマイグレーションは無い旨が表示される
$ php bin/console doctrine:migrations:migrate Application Migrations No migrations to execute.
Version20200728034801.php を追加してみる ファイルの内容は何でもいいが、「個別実行」で作成したものを利用してみる 追加したら、マイグレーションを一括実行する
$ php bin/console doctrine:migrations:migrate Application Migrations WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)y Migrating up to 20200728034801 from 20200303053716 ++ migrating 20200728034801 -> CREATE TABLE table_test( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', text VARCHAR(255) NOT NULL COMMENT 'テキスト', PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'テーブル操作テスト'; ++ migrated (0.23s) ------------------------ ++ finished in 0.23s ++ 1 migrations executed ++ 1 sql queries
追加したマイグレーションのみが実行された なお「php bin/console doctrine:migrations:execute --down」を実行すると「Not enough arguments (missing: "version").」というエラーになった マイグレーションの巻き戻しは、バージョンの指定が必要みたい よってマイグレーションをやりなおしたければ、
$ php bin/console doctrine:migrations:execute 20200728034801 --down $ php bin/console doctrine:migrations:migrate
のように必要がある マイグレーションの実行順に影響はありそうなので注意が必要そう ■確認なしに実行
$ php bin/console doctrine:migrations:migrate
でマイグレーションを実行すると、以下のように実行確認が表示される 「y」を入力して初めて実行されるが、スクリプトで自動化したい場合などは邪魔な処理となる
WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)n
この場合、以下のように「--no-interaction」を付けて実行すると、確認なしにマイグレーションを実行できる
$ php bin/console doctrine:migrations:migrate --no-interaction
【Symfony4】Doctrineマイグレーションコマンドまとめ - Qiita https://qiita.com/tomcky/items/e30a08861fd2e7530a0d
■カスタマイズ: 会員情報に項目を追加
※プラグインで対応する、という手段も検討する むしろ独自に項目を追加すると、CSVエクスポート他機能と連携するための処理も考慮する必要があってややこしい 原則独自に追加しようとせずに、素直にプラグインに頼るのが良さそう EC-CUBE4カスタマイズ - [4系] 新規会員登録画面に項目を追加する方法 https://umebius.com/eccube/eccube4-add-new-form-item-customer/ ■Entityを作成 Entityのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_entity html\app\Customize\Entity\CustomerTrait.php を作成
<?php namespace Customize\Entity; use Doctrine\ORM\Mapping as ORM; use Eccube\Annotation\EntityExtension; /** * @EntityExtension("Eccube\Entity\Customer") */ trait CustomerTrait { /** * @var string|null * @ORM\Column(type="string", length=14, nullable=true) * @Eccube\Annotation\FormAppend( * auto_render=false, * type="\Symfony\Component\Form\Extension\Core\Type\TextType", * options={ * "required": false, * "label": "携帯電話番号", * "attr": {"placeholder": "例:09000000000"} * }) */ private $mobile_number; /** * @return string|null */ public function getMobileNumber() { return $this->mobile_number; } /** * @param string|null $mobile_number * @return CustomerTrait */ public function setMobileNumber($mobile_number) { $this->mobile_number = $mobile_number; return $this; } }
作業したらキャッシュを削除する
$ php bin/console cache:clear --no-warmup
プロキシファイルを作成する
$ php bin/console eccube:generate:proxies
以下に、トレイトを認識させるためのプロキシファイルが作成される ファイル内の「use \Customize\Entity\CustomerTrait;」で、上記ファイルが読み込まれる html\app\proxy\entity\src\Eccube\Entity\Customer.php ■テーブル定義を変更(マイグレーションを使用する場合)
$ php bin/console cache:clear --no-warmup $ php bin/console doctrine:migrations:diff Generated new migration class to "/var/www/main/html/app/DoctrineMigrations/Version20200826083254.php" from schema differences.
以下のファイルが作成された 必要に応じて、SQLの内容を変更する(「AFTER column_name」を追加したりなど) html\app\DoctrineMigrations\Version20200826083254.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200826083254 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('ALTER TABLE dtb_customer ADD mobile_number VARCHAR(14) DEFAULT NULL'); } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('ALTER TABLE dtb_customer DROP mobile_number'); } }
実行されていない新しいバージョンのmigrationがあるかを確認 (ECCubeデフォルトのマイグレーションが未実行だと、その分も表示される)
$ php bin/console doctrine:migrations:status
マイグレーションを実行
$ php bin/console doctrine:migrations:migrate
テーブルに項目が追加される ■テーブル定義を変更(ORMを使用する場合)
$ php bin/console cache:clear --no-warmup $ php bin/console eccube:schema:update --force --dump-sql [OK] Database schema updated successfully!
dtb_customerテーブルの最後に「mobile_number」が追加される これは「ALTER TABLE dtb_customer ADD mobile_number VARCHAR(14) DEFAULT NULL;」と同じ効果 (ORMを使用する場合、「AFTER column_name」を追加したりなどはできないみたい) --force を付けないと、「[CAUTION] This operation should not be executed in a production environment!」となって本番環境では実行できないので注意 また「[OK] Nothing to update - your database is already in sync with the current entity metadata.」と表示された場合、先にキャッシュの削除を行っておく ■画面表示を調整 html\src\Eccube\Resource\template\default\Entry\index.twig を複製して html\app\template\default\Entry\index.twig を作成し、以下を追加する
<dl> <dt> {{ form_label(form.mobile_number, 'common.mobile_number', { 'label_attr': { 'class': 'ec-label' }}) }} </dt> <dd> <div class="ec-telInput{{ has_errors(form.mobile_number) ? ' error' }}"> {{ form_widget(form.mobile_number) }} {{ form_errors(form.mobile_number) }} </div> </dd> </dl>
html\src\Eccube\Resource\template\default\Entry\confirm.twig を複製して html\app\template\default\Entry\confirm.twig を作成し、以下を追加する
<dl> <dt> {{ form_label(form.mobile_number, 'common.mobile_number', { 'label_attr': { 'class': 'ec-label' }}) }} </dt> <dd>{{ form.mobile_number.vars.data }} {{ form_widget(form.mobile_number, { type : 'hidden' }) }} </dd> </dl>
html\src\Eccube\Resource\template\default\Mypage\change.twig を複製して html\app\template\default\Mypage\change.twig を作成し、以下を追加する
<dl> <dt> {{ form_label(form.mobile_number, 'common.mobile_number', { 'label_attr': { 'class': 'ec-label' }}) }} </dt> <dd> <div class="ec-telInput{{ has_errors(form.mobile_number) ? ' error' }}"> {{ form_widget(form.mobile_number) }} {{ form_errors(form.mobile_number) }} </div> </dd> </dl>
ラベルを正しく表示させるには、以下のリソースファイルに文言を追加する (リソースファイルが無ければ作成する) html\app\Customize\Resource\locale\messages.ja.yaml
common.mobile_number: 携帯電話番号
html\app\Customize\Resource\locale\messages.en.yaml
common.mobile_number: Mobile Phone
これでユーザ側に携帯電話番号欄が追加される 引き続き、管理側も調整を行う html\src\Eccube\Resource\template\admin\Customer\edit.twig を複製して html\app\template\admin\Customer\edit.twig を作成し、以下を追加する
<div class="row mb-2"> <div class="col-3"> <span>{{ 'admin.common.mobile_number'|trans }}</span> </div> <div class="col"> {{ form_widget(form.mobile_number) }} {{ form_errors(form.mobile_number) }} </div> </div>
管理画面のラベルを正しく表示させるには、以下のリソースファイルに文言を追加する (リソースファイルが無ければ作成する) html\app\Customize\Resource\locale\messages.ja.yaml
admin.common.mobile_number: 携帯電話番号
html\app\Customize\Resource\locale\messages.en.yaml
admin.common.phone_number: Mobile Phone
最後に、キャッシュをクリアしておく
$ php bin/console cache:clear --no-warmup
これで管理側に携帯電話番号欄が追加される 引き続き、PDFなどの掲載内容も必要に応じて調整が必要かもしれない
■カスタマイズ: 商品情報に項目を追加
※未検証 ※プラグインで対応する、という手段も検討する むしろ独自に項目を追加すると、CSVエクスポート他機能と連携するための処理も考慮する必要があってややこしい 原則独自に追加しようとせずに、素直にプラグインに頼るのが良さそう 【EC-CUBE4】商品規格に項目を追加し、バリデーションを入れる | クマひよ工房 https://kumahiyo.com/add-product-class-validation/
■カスタマイズ: ご注文手続きに項目を追加
※未検証 EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=22487&forum=4
■カスタマイズ: テーブルを作成
Entityのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_entity 以下を参考に、お問い合わせ(/contact)の内容をデータベースに保存してみる 【EC-CUBE4】お問い合わせ内容をデータベースに保存する方法 - あずみ.net https://a-zumi.net/eccube4-save-contact/ ■データベース操作の概要 マイグレーション用のSQL作成について、大きく分けて ・マイグレーションで作成する ・ORMの機能で作成する と2つの方法がある どちらでも同じ結果を得られるが、デフォルトでは列を追加するとテーブルの最後に追加される マイグレーションならマイグレーション内容を調整することで任意の場所に追加できるので、 特に理由が無ければ「マイグレーションで作成する」の方が無難そう また、ORMとマイグレーションが混在すると混乱の元なので、常に「マイグレーションで作成する」を使うように統一するほうが良さそう ただしECCube4のお作法としては「マイグレーションはデータの編集に使う」という思想があるようなので、引き続き検証したい EC-CUBE4カスタマイズ - マイグレーションの作成・実行方法 https://umebius.com/eccube/eccube4-howto-migration/ ■Entityを作成 html\app\Customize\Entity\Contact.php を作成する
<?php namespace Customize\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="dtb_contact") * @ORM\Entity(repositoryClass="Customize\Repository\ContactRepository") */ class Contact extends \Eccube\Entity\AbstractEntity { /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string", length=255) */ private $name01; /** * @ORM\Column(type="string", length=255) */ private $name02; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $kana01; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $kana02; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $postal_code; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $pref; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $addr01; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $addr02; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $phone_number; /** * @ORM\Column(type="string", length=255) */ private $email; /** * @ORM\Column(type="text") */ private $contents; public function getId(): ?int { return $this->id; } public function getName01(): ?string { return $this->name01; } public function setName01(string $name01): self { $this->name01 = $name01; return $this; } public function getName02(): ?string { return $this->name02; } public function setName02(string $name02): self { $this->name02 = $name02; return $this; } public function getKana01(): ?string { return $this->kana01; } public function setKana01(?string $kana01): self { $this->kana01 = $kana01; return $this; } public function getKana02(): ?string { return $this->kana02; } public function setKana02(?string $kana02): self { $this->kana02 = $kana02; return $this; } public function getPostalCode(): ?string { return $this->postal_code; } public function setPostalCode(?string $postal_code): self { $this->postal_code = $postal_code; return $this; } public function getPref(): ?string { return $this->pref; } public function setPref(?string $pref): self { $this->pref = $pref; return $this; } public function getAddr01(): ?string { return $this->addr01; } public function setAddr01(?string $addr01): self { $this->addr01 = $addr01; return $this; } public function getAddr02(): ?string { return $this->addr02; } public function setAddr02(?string $addr02): self { $this->addr02 = $addr02; return $this; } public function getPhoneNumber(): ?string { return $this->phone_number; } public function setPhoneNumber(?string $phone_number): self { $this->phone_number = $phone_number; return $this; } public function getEmail(): ?string { return $this->email; } public function setEmail(string $email): self { $this->email = $email; return $this; } public function getContents(): ?string { return $this->contents; } public function setContents(string $contents): self { $this->contents = $contents; return $this; } }
■テーブルを作成(マイグレーションを使用する場合)
$ php bin/console cache:clear --no-warmup $ php bin/console doctrine:migrations:diff Generated new migration class to "/var/www/main/html/app/DoctrineMigrations/Version20200826091332.php" from schema differences.
以下のファイルが作成された 必要に応じて、SQLの内容を変更する(「COMMENT」を追加したりなど) html\app\DoctrineMigrations\Version20200826091332.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200826091332 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('CREATE TABLE dtb_contact (id INT AUTO_INCREMENT NOT NULL, name01 VARCHAR(255) NOT NULL, name02 VARCHAR(255) NOT NULL, kana01 VARCHAR(255) DEFAULT NULL, kana02 VARCHAR(255) DEFAULT NULL, postal_code VARCHAR(255) DEFAULT NULL, pref VARCHAR(255) DEFAULT NULL, addr01 VARCHAR(255) DEFAULT NULL, addr02 VARCHAR(255) DEFAULT NULL, phone_number VARCHAR(255) DEFAULT NULL, email VARCHAR(255) NOT NULL, contents LONGTEXT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB'); } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('DROP TABLE dtb_contact'); } }
実行されていない新しいバージョンのmigrationがあるかを確認 (ECCubeデフォルトのマイグレーションが未実行だと、その分も表示される)
$ php bin/console doctrine:migrations:status
マイグレーションを実行
$ php bin/console doctrine:migrations:migrate
テーブルが作成される ■テーブルを作成(ORMを使用する場合) 以下のようにして、作成される予定のSQLを確認する (実際は CREATE TABLE は1行で表示される。インデントも調整されていない。またORMを使用する場合、「COMMENT」を追加したりなどはできないみたい)
$ php bin/console eccube:schema:update --dump-sql gen -> /tmp/proxy_9gCzBn5rLdAt/src/Eccube/Entity/Customer.php The following SQL statements will be executed: CREATE TABLE dtb_contact ( id INT AUTO_INCREMENT NOT NULL, name01 VARCHAR(255) NOT NULL, name02 VARCHAR(255) NOT NULL, kana01 VARCHAR(255) DEFAULT NULL, kana02 VARCHAR(255) DEFAULT NULL, postal_code VARCHAR(255) DEFAULT NULL, pref VARCHAR(255) DEFAULT NULL, addr01 VARCHAR(255) DEFAULT NULL, addr02 VARCHAR(255) DEFAULT NULL, phone_number VARCHAR(255) DEFAULT NULL, email VARCHAR(255) NOT NULL, contents LONGTEXT NOT NULL, PRIMARY KEY(id) ) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB;
以下のようにして、実際にテーブルを作成する
$ php bin/console eccube:schema:update --force gen -> /tmp/proxy_WQKomAybHkLO/src/Eccube/Entity/Customer.php gen -> /tmp/proxy_WQKomAybHkLO/src/Eccube/Entity/Customer.php Updating database schema... 1 query was executed [OK] Database schema updated successfully!
dtb_contact テーブルが作成されているので確認する ■イベントを作成 html\app\Customize\Event.php を作成する
<?php namespace Customize; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Eccube\Event\EccubeEvents; use Eccube\Event\EventArgs; use Customize\Entity\Contact; use Doctrine\ORM\EntityManagerInterface; class Event implements EventSubscriberInterface { /** * @var EntityManagerInterface */ private $entityManager; public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } /** * @return array */ public static function getSubscribedEvents() { return [ EccubeEvents::FRONT_CONTACT_INDEX_COMPLETE => 'onFrontContactIndexComplete' ]; } public function onFrontContactIndexComplete(EventArgs $event) { $data = $event->getArgument('data'); $Contact = new Contact(); $Contact->setName01($data['name01']); $Contact->setName02($data['name02']); $Contact->setKana01($data['kana01']); $Contact->setKana02($data['kana02']); $Contact->setPostalCode($data['postal_code']); $Contact->setPref($data['pref']); $Contact->setAddr01($data['addr01']); $Contact->setAddr02($data['addr02']); $Contact->setPhoneNumber($data['phone_number']); $Contact->setEmail($data['email']); $Contact->setContents($data['contents']); $this->entityManager->persist($Contact); $this->entityManager->flush(); } }
以下でキャッシュを削除する
$ php bin/console cache:clear --no-warmup
お問い合わせからメールを送信すると、データベースの dtb_contact テーブルに記録される 「EccubeEvents::FRONT_CONTACT_INDEX_COMPLETE」は処理を行うタイミングの指定みたい 大した説明は無いが以下が参考になりそう EC-CUBE4 APIドキュメント https://www.xross-cube.com/EC-CUBE_doc/classes/Eccube-Event-EccubeEvents.html
■カスタマイズ: テーブルに項目を追加
■ORMの機能で作成する例 ※実際の開発では常に「マイグレーションで作成する」を使うように統一するほうが良さそう ※ECCubeデフォルトのマイグレーションがあるので、先にマイグレーションを実行しておくといい 上記「ORMでテーブルを作成」の「テーブルの追加」で追加したテーブルに、 お問い合わせの送信日時(データの登録日時)を追加してみる (Entityのどの部分に追加しても、データベース上は最後に追加される) html\app\Customize\Entity\Contact.php に以下を追加
/** * @ORM\Column(type="datetimetz") */ private $create_date; public function setCreateDate($createDate) { $this->create_date = $createDate; return $this; } public function getCreateDate() { return $this->create_date; }
以下のようにして、作成される予定のSQLを確認する (あらかじめキャッシュをクリアしておかないと、実行しても 「[OK] Nothing to update - your database is already in sync with the current entity metadata.」 と表示される)
$ php bin/console cache:clear --no-warmup $ php bin/console eccube:schema:update --dump-sql gen -> /tmp/proxy_KTZb3ampuVeY/src/Eccube/Entity/Customer.php The following SQL statements will be executed: ALTER TABLE contact ADD create_date DATETIME NOT NULL COMMENT '(DC2Type:datetimetz)';
以下のようにして、実際にテーブルを更新する
$ php bin/console eccube:schema:update --force
html\app\Customize\Event.php を修正する
$Contact->setCreateDate(date('Y-m-d H:i:s'));
以下のコマンドでキャッシュを削除し、動作を確認する
$ php bin/console cache:clear --no-warmup
■マイグレーションで作成する例 ※ECCubeデフォルトのマイグレーションがあるので、先にマイグレーションを実行しておくといい ※上記 create_date をマイグレーションで追加するようにしたら、update_date の解説は削除しておくか 代わりに、ニュース管理を参考にCRUDを別途作ってみるか 上記「ORMでテーブルを作成」の「テーブルの追加」で追加したテーブルに、 さらにお問い合わせの編集日時(データの編集日時)を追加してみる (Entityとは異なり、作成されたSQLを調整することで任意の位置に追加できるみたい) html\app\Customize\Entity\Contact.php に以下を追加
/** * @ORM\Column(type="datetimetz") */ private $update_date; public function setUpdateDate($updateDate) { $this->update_date = $updateDate; return $this; } public function getUpdateDate() { return $this->update_date; }
Entityに基づいてmigrationファイルを自動生成 (あらかじめキャッシュをクリアしておかないと、実行しても「No changes detected in your mapping information.」と表示される)
$ php bin/console cache:clear --no-warmup $ php bin/console doctrine:migrations:diff Generated new migration class to "/var/www/main/html/app/DoctrineMigrations/Version20200817055148.php" from schema differences.
以下のファイルが作成された 必要に応じて、SQLの内容を変更する(「AFTER column_name」を追加したりなど) html\app\DoctrineMigrations\Version20200817055148.php
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200817055148 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('ALTER TABLE contact ADD update_date DATETIME NOT NULL COMMENT \'(DC2Type:datetimetz)\''); } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('ALTER TABLE contact DROP update_date'); } }
実行されていない新しいバージョンのmigrationがあるかを確認 (ECCubeデフォルトのマイグレーションが未実行だと、その分も表示される)
$ php bin/console doctrine:migrations:status
マイグレーションを実行
$ php bin/console doctrine:migrations:migrate
テーブルに項目が追加される html\app\Customize\Event.php を修正する
$Contact->setUpdateDate(date('Y-m-d H:i:s'));
以下のコマンドでキャッシュを削除し、動作を確認する
$ php bin/console cache:clear --no-warmup
■カスタマイズ: 独自に追加したテーブルからデータを取得
上記「ORMでテーブルを作成」の「テーブルの追加」で追加したテーブルから、 管理画面にデータを表示してみる Symfony2で利用されているDoctrineに入門する(後編) - OTOBANK Engineering Blog https://engineering.otobank.co.jp/entry/2017/05/09/190056 ※上記にある保存処理も、Repositoryにする方がいいか html\src\Customize\Repository\ContactRepository.php
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Repository; use Customize\Entity\Contact; use Eccube\Repository\AbstractRepository; use Symfony\Bridge\Doctrine\RegistryInterface; /** * ContactRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class ContactRepository extends AbstractRepository { public function __construct(RegistryInterface $registry) { parent::__construct($registry, Contact::class); } }
html\app\Customize\Controller\Admin\Content\ContactController.php
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Controller\Admin\Content; use Eccube\Controller\AbstractController; use Customize\Repository\ContactRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class ContactController extends AbstractController { /** * @var ContactRepository */ protected $contactRepository; /** * ContactController constructor. * * @param ContactRepository $contactRepository */ public function __construct( ContactRepository $contactRepository ) { $this->contactRepository = $contactRepository; } /** * @Route("/%eccube_admin_route%/content/contact", name="admin_contact") * @Template("@admin/Content/contact.twig") */ public function index(Request $request) { $Contacts = $this->contactRepository->findAll(); return [ 'Contacts' => $Contacts, ]; } }
html\app\template\admin\Content\contact.twig
{% extends '@admin/default_frame.twig' %} {% set menus = ['content'] %} {% block title %}お問い合わせ{% endblock %} {% block sub_title %}コンテンツ管理{% endblock %} {% block main %} <div class="c-contentsArea__cols"> <div class="c-contentsArea__primaryCol"> <div class="c-primaryCol"> <div class="d-block mb-3"> <p>お問い合わせ一覧。</p> </div> <div class="card rounded border-0 mb-4"> <div class="card-body p-0"> <ul class="list-group list-group-flush mb-4 sortable-container"> <li class="list-group-item"> <div class="row justify-content-around"> <div class="col-1"><strong>ID</strong></div> <div class="col"><strong>Name</strong></div> <div class="col"><strong>Kana</strong></div> <div class="col"><strong>Email</strong></div> <div class="col"><strong>Created</strong></div> </div> </li> {% for Contact in Contacts %} <li class="list-group-item sortable-item" data-id="{{ Contact.id }}"> <div class="row justify-content-around"> <div class="col-1">{{ Contact.id }}</div> <div class="col">{{ Contact.name01 }} {{ Contact.name02 }}</div> <div class="col">{{ Contact.kana01 }} {{ Contact.kana02 }}</div> <div class="col">{{ Contact.email }}</div> <div class="col">{{ Contact.create_date|date('Y-m-d H:i:s') }}</div> </div> </li> {% endfor %} </ul> </div> </div> </div> </div> </div> {% endblock %}
管理画面にログインして以下にアクセスすると、お問い合わせ一覧が表示される /system/content/contact 表示されない場合、以下のコマンドでキャッシュを削除してから再度アクセスする
$ php bin/console cache:clear --no-warmup
データベースに「2020-08-17 06:27:51」と格納されていても、 上記のコードで一覧に「2020-08-17 15:27:51」と表示される つまり、表示の際に時差は調整されるみたい
■カスタマイズ: テーブル間のリレーション
Symfonyの仕様で、Entityに対してアノテーションを設定して「OneToOne」「OneToMany」などを指定する 1対多リレーションについて|ジャムブログ https://jum11.com/2020/10/26/ec-cube4-manytoone/ ステップ 8: データ構造の説明 (Symfony Docs) https://symfony.com/doc/current/the-fast-track/ja/8-doctrine.html 既存テーブルに紐付けを追加する場合、 app\Customize\Entity\ProductTrait.php などEntityのtraitを作成してそこでアノテーションを設定するといいみたい(オリジナルのEntityを複製して編集、ではない) Entityのカスタマイズ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/customize_entity ただしマスターデータのテーブルは、traitで拡張できない 仕様なのか、今後調整されるのかは不明 MasterテーブルがTraitで拡張できない - Issue #4325 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4325
■カスタマイズ: お問い合わせフォームの複製
未検証だが、以下が参考になりそう [EC-CUBE 4]問い合わせフォームの複製 | creatorlab https://www.creatorlab.jp/2019/08/17/ec-cube-4%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B%E3%83%95...
■カスタマイズ: 新着情報管理をもとに独自の記事管理を作成
既存機能の「新着情報管理」をもとに、独自の記事管理を作成してみる 独自に作成したテーブルには専用のプレフィックスを付けておく方が管理しやすいか。contactも含めて考える ■調査メモ
@ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
EC-CUBE4のDBのそれぞれのテーブルにあるdiscriminator_typeとは何なのか - Qiita https://qiita.com/okazy/items/974d8e07b0dbd50b9c6a Entityを継承して拡張するときに利用されるらしい ECCubeでは以下のアノテーションを指定すると、discriminator_type列にクラス名が小文字になった文字列が入る 現状それ以上は気にしなくていいか
@ORM\InheritanceType("SINGLE_TABLE")
Doctrine2のInheritanceMapping | QUARTETCOM TECH BLOG https://tech.quartetcom.co.jp/2015/08/24/doctorine2-inheritance-mapping/ Single Table Inheritance は、全ての階層構造を一つのテーブルで表現したもの Class Table Inheritance は、複数テーブルで継承関係を表現したもの ECCubeではすべてのテーブルで SINGLE_TABLE が指定されいているみたい
@ORM\HasLifecycleCallbacks()
データーベースと Doctrine | SymDoc - PHP フレームワーク Symfony3 日本語ドキュメント Wiki http://symdoc.kwalk.jp/doc/book/doctrine エンティティが INSERT や UPDATE、DELETE される直前または、直後に、アクションを実行することができる 例えば「データを登録する際に、自動で特定のメソッドが実行されるようにする」のような処理ができる ECCubeではすべてのテーブルに指定されいているみたい ■Entityとテーブルの作成 src\Eccube\Entity\News.php をもとに app\Customize\Entity\Article.php を作成
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Entity; use Doctrine\ORM\Mapping as ORM; /** * Article * * @ORM\Table(name="dtb_article") * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255) * @ORM\HasLifecycleCallbacks() * @ORM\Entity(repositoryClass="Customize\Repository\ArticleRepository") * @ORM\Cache(usage="NONSTRICT_READ_WRITE") */ class Article extends \Eccube\Entity\AbstractEntity { /** * @return string */ public function __toString() { return (string) $this->getTitle(); } /** * @var int * * @ORM\Column(name="id", type="integer", options={"unsigned":true}) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var \DateTime|null * * @ORM\Column(name="publish_date", type="datetimetz", nullable=true) */ private $publish_date; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ private $title; /** * @var string|null * * @ORM\Column(name="description", type="text", nullable=true) */ private $description; /** * @var string|null * * @ORM\Column(name="url", type="string", length=4000, nullable=true) */ private $url; /** * @var boolean * * @ORM\Column(name="link_method", type="boolean", options={"default":false}) */ private $link_method = false; /** * @var \DateTime * * @ORM\Column(name="create_date", type="datetimetz") */ private $create_date; /** * @var \DateTime * * @ORM\Column(name="update_date", type="datetimetz") */ private $update_date; /** * @var boolean * * @ORM\Column(name="visible", type="boolean", options={"default":true}) */ private $visible; /** * @var \Eccube\Entity\Member * * @ORM\ManyToOne(targetEntity="Eccube\Entity\Member") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="creator_id", referencedColumnName="id") * }) */ private $Creator; /** * Get id. * * @return int */ public function getId() { return $this->id; } /** * Set publishDate. * * @param \DateTime|null $publishDate * * @return Article */ public function setPublishDate($publishDate = null) { $this->publish_date = $publishDate; return $this; } /** * Get publishDate. * * @return \DateTime|null */ public function getPublishDate() { return $this->publish_date; } /** * Set title. * * @param string $title * * @return Article */ public function setTitle($title) { $this->title = $title; return $this; } /** * Get title. * * @return string */ public function getTitle() { return $this->title; } /** * Set description. * * @param string|null $description * * @return Article */ public function setDescription($description = null) { $this->description = $description; return $this; } /** * Get description. * * @return string|null */ public function getDescription() { return $this->description; } /** * Set url. * * @param string|null $url * * @return Article */ public function setUrl($url = null) { $this->url = $url; return $this; } /** * Get url. * * @return string|null */ public function getUrl() { return $this->url; } /** * Set linkMethod. * * @param boolean $linkMethod * * @return Article */ public function setLinkMethod($linkMethod) { $this->link_method = $linkMethod; return $this; } /** * Get linkMethod. * * @return boolean */ public function isLinkMethod() { return $this->link_method; } /** * Set createDate. * * @param \DateTime $createDate * * @return Article */ public function setCreateDate($createDate) { $this->create_date = $createDate; return $this; } /** * Get createDate. * * @return \DateTime */ public function getCreateDate() { return $this->create_date; } /** * Set updateDate. * * @param \DateTime $updateDate * * @return Article */ public function setUpdateDate($updateDate) { $this->update_date = $updateDate; return $this; } /** * Get updateDate. * * @return \DateTime */ public function getUpdateDate() { return $this->update_date; } /** * @return integer */ public function isVisible() { return $this->visible; } /** * @param boolean $visible * * @return Article */ public function setVisible($visible) { $this->visible = $visible; return $this; } /** * Set creator. * * @param \Eccube\Entity\Member|null $creator * * @return Article */ public function setCreator(\Eccube\Entity\Member $creator = null) { $this->Creator = $creator; return $this; } /** * Get creator. * * @return \Eccube\Entity\Member|null */ public function getCreator() { return $this->Creator; } }
以下でマイグレーションファイルを作成
$ cd /var/www/main/html $ php bin/console cache:clear --no-warmup $ php bin/console doctrine:migrations:diff Generated new migration class to "/var/www/main/html/app/DoctrineMigrations/Version20200820110258.php" from schema differences.
app\DoctrineMigrations\Version20200820110258.php を修正(自動作成されたマイグレーションファイル)
<?php declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20200820110258 extends AbstractMigration { public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('CREATE TABLE dtb_article (id INT UNSIGNED AUTO_INCREMENT NOT NULL, creator_id INT UNSIGNED DEFAULT NULL, publish_date DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetimetz)\', title VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, url VARCHAR(4000) DEFAULT NULL, link_method TINYINT(1) DEFAULT \'0\' NOT NULL, create_date DATETIME NOT NULL COMMENT \'(DC2Type:datetimetz)\', update_date DATETIME NOT NULL COMMENT \'(DC2Type:datetimetz)\', visible TINYINT(1) DEFAULT \'1\' NOT NULL, discriminator_type VARCHAR(255) NOT NULL, INDEX IDX_23A0E6661220EA6 (creator_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB'); $this->addSql('ALTER TABLE dtb_article ADD CONSTRAINT FK_23A0E6661220EA6 FOREIGN KEY (creator_id) REFERENCES dtb_member (id)'); } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); $this->addSql('DROP TABLE dtb_article'); } }
以下でマイグレーションを実行
$ php bin/console doctrine:migrations:status == Configuration >> Name: Application Migrations >> Database Driver: pdo_mysql >> Database Name: main >> Configuration Source: manually configured >> Version Table Name: migration_versions >> Version Column Name: version >> Migrations Namespace: DoctrineMigrations >> Migrations Directory: /var/www/main/html/app/DoctrineMigrations >> Previous Version: 2020-08-17 05:51:48 (20200817055148) >> Current Version: 2020-08-20 03:20:03 (20200820032003) >> Next Version: 2020-08-20 11:02:58 (20200820110258) >> Latest Version: 2020-08-20 11:02:58 (20200820110258) >> Executed Migrations: 6 >> Executed Unavailable Migrations: 0 >> Available Migrations: 7 >> New Migrations: 1 $ php bin/console doctrine:migrations:migrate Application Migrations WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)y Migrating up to 20200820110258 from 20200820032003 ++ migrating 20200820110258 -> CREATE TABLE article (id INT UNSIGNED AUTO_INCREMENT NOT NULL, creator_id INT UNSIGNED DEFAULT NULL, publish_date DATETIME DEFAULT NULL COMMENT '(DC2Type:datetimetz)', title VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, url VARCHAR(4000) DEFAULT NULL, link_method TINYINT(1) DEFAULT '0' NOT NULL, create_date DATETIME NOT NULL COMMENT '(DC2Type:datetimetz)', update_date DATETIME NOT NULL COMMENT '(DC2Type:datetimetz)', visible TINYINT(1) DEFAULT '1' NOT NULL, discriminator_type VARCHAR(255) NOT NULL, INDEX IDX_23A0E6661220EA6 (creator_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB -> ALTER TABLE article ADD CONSTRAINT FK_23A0E6661220EA6 FOREIGN KEY (creator_id) REFERENCES dtb_member (id) ++ migrated (0.28s) ------------------------ ++ finished in 0.28s ++ 1 migrations executed ++ 2 sql queries
実行できたら、テーブルが作成されていることを確認する ■プログラムの作成 src\Eccube\Repository\NewsRepository.php をもとに app\Customize\Repository\ArticleRepository.php を作成
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Repository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Symfony\Bridge\Doctrine\RegistryInterface; use Customize\Entity\Article; /** * ArticleRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class ArticleRepository extends \Eccube\Repository\AbstractRepository { public function __construct(RegistryInterface $registry) { parent::__construct($registry, Article::class); } /** * 記事を登録します. * * @param $Article */ public function save($Article) { $em = $this->getEntityManager(); $em->persist($Article); $em->flush($Article); } /** * 記事を削除します. * * @param Article $Article * * @throws ForeignKeyConstraintViolationException 外部キー制約違反の場合 * @throws DriverException SQLiteの場合, 外部キー制約違反が発生すると, DriverExceptionをthrowします. */ public function delete($Article) { $em = $this->getEntityManager(); $em->remove($Article); $em->flush($Article); } /** * @return \Doctrine\ORM\QueryBuilder */ public function getQueryBuilderAll() { $qb = $this->createQueryBuilder('n'); $qb->orderBy('n.publish_date', 'DESC') ->addOrderBy('n.id', 'DESC'); return $qb; } /** * @return Article[]|ArrayCollection */ public function getList() { // second level cacheを効かせるためfindByで取得 $Results = $this->findBy(['visible' => true], ['publish_date' => 'DESC', 'id' => 'DESC']); // 公開日時前のArticleをフィルター $criteria = Criteria::create(); $criteria->where(Criteria::expr()->lte('publish_date', new \DateTime())); $Article = new ArrayCollection($Results); return $Article->matching($criteria); } }
src\Eccube\Controller\Admin\Content\NewsController.php をもとに app\Customize\Controller\Admin\Content\ArticleController.php を作成
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Controller\Admin\Content; use Eccube\Controller\AbstractController; //use Eccube\Event\EccubeEvents; //use Eccube\Event\EventArgs; use Eccube\Util\CacheUtil; use Knp\Component\Pager\Paginator; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Annotation\Route; use Customize\Entity\Article; use Customize\Form\Type\Admin\ArticleType; use Customize\Repository\ArticleRepository; class ArticleController extends AbstractController { /** * @var ArticleRepository */ protected $articleRepository; /** * ArticleController constructor. * * @param ArticleRepository $articleRepository */ public function __construct(ArticleRepository $articleRepository) { $this->articleRepository = $articleRepository; } /** * 記事一覧を表示する。 * * @Route("/%eccube_admin_route%/content/article", name="admin_content_article") * @Route("/%eccube_admin_route%/content/article/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_content_article_page") * @Template("@admin/Content/article.twig") * * @param Request $request * @param int $page_no * @param Paginator $paginator * * @return array */ public function index(Request $request, $page_no = 1, Paginator $paginator) { $qb = $this->articleRepository->getQueryBuilderAll(); /* $event = new EventArgs( [ 'qb' => $qb, ], $request ); $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_INDEX_INITIALIZE, $event); */ $pagination = $paginator->paginate( $qb, $page_no, $this->eccubeConfig->get('eccube_default_page_count') ); return [ 'pagination' => $pagination, ]; } /** * 記事を登録・編集する。 * * @Route("/%eccube_admin_route%/content/article/new", name="admin_content_article_new") * @Route("/%eccube_admin_route%/content/article/{id}/edit", requirements={"id" = "\d+"}, name="admin_content_article_edit") * @Template("@admin/Content/article_edit.twig") * * @param Request $request * @param null $id * * @return array|\Symfony\Component\HttpFoundation\RedirectResponse */ public function edit(Request $request, $id = null, CacheUtil $cacheUtil) { if ($id) { $Article = $this->articleRepository->find($id); if (!$Article) { throw new NotFoundHttpException(); } } else { $Article = new \Customize\Entity\Article(); $Article->setPublishDate(new \DateTime()); } $builder = $this->formFactory ->createBuilder(ArticleType::class, $Article); /* $event = new EventArgs( [ 'builder' => $builder, 'Article' => $Article, ], $request ); $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_EDIT_INITIALIZE, $event); */ $form = $builder->getForm(); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { if (!$Article->getUrl()) { $Article->setLinkMethod(false); } $this->articleRepository->save($Article); /* $event = new EventArgs( [ 'form' => $form, 'Article' => $Article, ], $request ); $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_EDIT_COMPLETE, $event); */ $this->addSuccess('admin.common.save_complete', 'admin'); // キャッシュの削除 $cacheUtil->clearDoctrineCache(); return $this->redirectToRoute('admin_content_article_edit', ['id' => $Article->getId()]); } return [ 'form' => $form->createView(), 'Article' => $Article, ]; } /** * 指定した記事を削除する。 * * @Route("/%eccube_admin_route%/content/article/{id}/delete", requirements={"id" = "\d+"}, name="admin_content_article_delete", methods={"DELETE"}) * * @param Request $request * @param Article $Article * * @return \Symfony\Component\HttpFoundation\RedirectResponse */ public function delete(Request $request, Article $Article, CacheUtil $cacheUtil) { $this->isTokenValid(); log_info('記事削除開始', [$Article->getId()]); try { $this->articleRepository->delete($Article); /* $event = new EventArgs(['Article' => $Article], $request); $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_DELETE_COMPLETE, $event); */ $this->addSuccess('admin.common.delete_complete', 'admin'); log_info('記事削除完了', [$Article->getId()]); // キャッシュの削除 $cacheUtil->clearDoctrineCache(); } catch (\Exception $e) { $message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Article->getTitle()]); $this->addError($message, 'admin'); log_error('記事削除エラー', [$Article->getId(), $e]); } return $this->redirectToRoute('admin_content_article'); } }
src\Eccube\Form\Type\Admin\NewsType.php をもとに app\Customize\Form\Type\Admin\ArticleType.php を作成
<?php /* * This file is part of EC-CUBE * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * * http://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Customize\Form\Type\Admin; use Eccube\Common\EccubeConfig; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints as Assert; use Customize\Entity\Article; class ArticleType extends AbstractType { /** * @var EccubeConfig */ protected $eccubeConfig; public function __construct(EccubeConfig $eccubeConfig) { $this->eccubeConfig = $eccubeConfig; } /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('publish_date', DateTimeType::class, [ 'date_widget' => 'choice', 'input' => 'datetime', 'format' => 'yyyy-MM-dd hh:mm', 'years' => range($this->eccubeConfig['eccube_article_start_year'], date('Y') + 3), 'constraints' => [ new Assert\NotBlank(), ], ]) ->add('title', TextType::class, [ 'required' => true, 'constraints' => [ new Assert\NotBlank(), new Assert\Length(['max' => $this->eccubeConfig['eccube_mtext_len']]), ], ]) ->add('url', TextType::class, [ 'required' => false, 'constraints' => [ new Assert\Url(), new Assert\Length(['max' => $this->eccubeConfig['eccube_mtext_len']]), ], ]) ->add('link_method', CheckboxType::class, [ 'required' => false, 'label' => 'admin.content.article.new_window', 'value' => '1', ]) ->add('description', TextareaType::class, [ 'required' => false, 'attr' => [ 'rows' => 8, ], 'constraints' => [ new Assert\Length(['max' => $this->eccubeConfig['eccube_ltext_len']]), ], ]) ->add('visible', ChoiceType::class, [ 'label' => false, 'choices' => ['admin.content.article.display_status__show' => true, 'admin.content.article.display_status__hide' => false], 'required' => true, 'expanded' => false, ]); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Article::class, ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'admin_article'; } }
■テンプレートの作成 src\Eccube\Resource\template\admin\Content\news.twig をもとに app\template\admin\Content\article.twig を作成
{# This file is part of EC-CUBE Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} {% extends '@admin/default_frame.twig' %} {% set menus = ['content', 'article'] %} {% block title %}{{ 'admin.content.article_management'|trans }}{% endblock %} {% block sub_title %}{{ 'admin.content.contents_management'|trans }}{% endblock %} {% block stylesheet %} <style type="text/css"> li.list-group-item { z-index: inherit !important; } </style> {% endblock %} {% block main %} <div class="c-contentsArea__cols"> <div class="c-contentsArea__primaryCol"> <div class="c-primaryCol"> <div class="d-block mb-3"> <a id="addNew" class="btn btn-ec-regular" href="{{ url('admin_content_article_new') }}">{{ 'admin.common.create__new'|trans }}</a> </div> <div class="card rounded border-0 mb-4"> <div class="card-body p-0"> <ul class="list-group list-group-flush mb-4 sortable-container"> <li class="list-group-item"> <div class="row justify-content-around"> <div class="col-2"><strong>{{ 'admin.content.article.publish_date'|trans }}</strong> </div> <div class="col-1"><strong>{{ 'admin.content.article.display_status'|trans }}</strong> </div> <div class="col"><strong>{{ 'admin.content.article.title'|trans }}</strong></div> </div> </li> {% for Article in pagination %} <li class="list-group-item sortable-item" data-id="{{ Article.id }}"> <div class="row justify-content-around"> <div class="col-2 d-flex align-items-center"> <span>{{ Article.publishDate|date_min }}</span></div> <div class="col-1 d-flex align-items-center">{{ Article.visible ? 'admin.content.article.display_status__show'|trans : 'admin.content.article.display_status__hide'|trans }}</div> <div class="col d-flex align-items-center"><a href="{{ url('admin_content_article_edit', {id: Article.id}) }}">{{ Article.title }}</a> </div> <div class="col-2"> <div class="row"> <div class="col px-0 text-center"> <a class="btn btn-ec-actionIcon" href="{{ url('admin_content_article_edit', {id: Article.id}) }}" data-toggle="tooltip" data-placement="top" title="{{ 'admin.common.edit'|trans }}"> <i class="fa fa-pencil fa-lg text-secondary"></i> </a> </div> <div class="col pl-0 text-center" data-toggle="tooltip" data-placement="top" title="{{ 'admin.common.delete'|trans }}"> <a class="btn btn-ec-actionIcon" data-toggle="modal" data-target="#delete_{{ Article.id }}"> <i class="fa fa-close fa-lg text-secondary" aria-hidden="true"></i> </a> <div class="modal fade" id="delete_{{ Article.id }}" tabindex="-1" role="dialog" aria-labelledby="delete_{{ Article.id }}" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title font-weight-bold">{{ 'admin.common.delete_modal__title'|trans }}</h5> <button class="close" type="button" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span> </button> </div> <div class="modal-body text-left"> <p class="text-left">{{ 'admin.common.delete_modal__message'|trans({'%name%': Article.title}) }}</p> </div> <div class="modal-footer"> <button class="btn btn-ec-sub" type="button" data-dismiss="modal">{{ 'admin.common.cancel'|trans }}</button> <a class="btn btn-ec-delete" href="{{ url('admin_content_article_delete', {id: Article.id}) }}" {{ csrf_token_for_anchor() }} data-method="delete" data-confirm="false">{{ 'admin.common.delete'|trans }}</a> </div> </div> </div> </div> </div> </div> </div> </div> </li> {% endfor %} </ul> {% if pagination.paginationData.pageCount > 1 %} <div class="row justify-content-md-center mb-4"> {% include "@admin/pager.twig" with { 'pages' : pagination.paginationData, 'routes' : 'admin_content_article_page' } %} </div> {% endif %} </div> </div> </div> </div> </div> {% endblock %}
src\Eccube\Resource\template\admin\Content\news_edit.twig をもとに app\template\admin\Content\article_edit.twig を作成
{# This file is part of EC-CUBE Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} {% extends '@admin/default_frame.twig' %} {% set menus = ['content', 'article'] %} {% block title %}{{ 'admin.content.article_management'|trans }}{% endblock %} {% block sub_title %}{{ 'admin.content.contents_management'|trans }}{% endblock %} {% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %} {% block main %} <form role="form" class="form-horizontal" name="form1" id="form1" method="post" action="?"> {{ form_widget(form._token) }} <div class="c-contentsArea__cols"> <div class="c-contentsArea__primaryCol"> <div class="c-primaryCol"> <div class="card rounded border-0 mb-4"> <div class="card-header"> <div class="row"> <div class="col-8"> <span class="card-title">{{ 'admin.content.article.article_registration'|trans }}</span></div> <div class="col-4 text-right"> <a data-toggle="collapse" href="#articleForm" aria-expanded="true" aria-controls="articleForm"><i class="fa fa-lg fa-angle-up"></i></a></div> </div> </div> <div class="ec-cardCollapse collapse show" id="articleForm" style=""> <div class="card-body"> <div class="row"> <div class="col-3"><span>{{ 'admin.content.article.publish_date'|trans }}</span></div> <div class="col mb-2"> {{ form_widget(form.publish_date) }} {{ form_errors(form.publish_date) }} </div> </div> <div class="row"> <div class="col-3"><span>{{ 'admin.content.article.title'|trans }}</span></div> <div class="col mb-2"> {{ form_widget(form.title) }} {{ form_errors(form.title) }} </div> </div> <div class="row"> <div class="col-3"> <div class="d-inline-block" data-tooltip="true" data-placement="top" title="{{ 'tooltip.content.article.url'|trans }}"> <span>{{ 'admin.content.article.url'|trans }}</span><i class="fa fa-question-circle fa-lg ml-1"></i> </div> </div> <div class="col mb-2"> {{ form_widget(form.url) }} {{ form_errors(form.url) }} </div> </div> <div class="row"> <div class="col-3">&nbsp;</div> <div class="col mb-2"> {{ form_widget(form.link_method) }} {{ form_errors(form.link_method) }} </div> </div> <div class="row"> <div class="col-3"> <div class="d-inline-block" data-tooltip="true" data-placement="top" title="{{ 'tooltip.content.article.body'|trans }}"> <span>{{ 'admin.content.article.body'|trans }}</span><i class="fa fa-question-circle fa-lg ml-1"></i> </div> </div> <div class="col mb-2"> {{ form_widget(form.description) }} {{ form_errors(form.description) }} </div> </div> {# エンティティ拡張の自動出力 #} {% for f in form if f.vars.eccube_form_options.auto_render %} {% if f.vars.eccube_form_options.form_theme %} {% form_theme f f.vars.eccube_form_options.form_theme %} {{ form_row(f) }} {% else %} <div class="row mb-2"> <div class="col-3"> <span>{{ f.vars.label|trans }}</span> </div> <div class="col"> {{ form_widget(f) }} {{ form_errors(f) }} </div> </div> {% endif %} {% endfor %} </div> </div> </div> </div> <div class="c-conversionArea"> <div class="c-conversionArea__container"> <div class="row justify-content-between align-items-center"> <div class="col-6"> <div class="c-conversionArea__leftBlockItem"> <a class="c-baseLink" href="{{ url('admin_content_article') }}"> <i class="fa fa-backward" aria-hidden="true"></i> <span>{{ 'admin.content.article_management'|trans }}</span></a> </div> </div> <div class="col-6"> <div id="ex-conversion-action" class="row align-items-center justify-content-end"> <div class="col-auto"> {{ form_widget(form.visible) }} {{ form_errors(form.visible) }} </div> <div class="col-auto"> <button class="btn btn-ec-conversion px-5" type="submit">{{ 'admin.common.registration'|trans }}</button> </div> </div> </div> </div> </div> </div> </div> </div> </form> {% endblock %}
■文字列定義の作成 app\config\eccube\packages\eccube_nav.yaml に追記(news の直下)
article: name: admin.content.article_management url : admin_content_article
src\Eccube\Resource\locale\messages.en.yaml をもとに app\Customize\Resource\locale\messages.en.yaml を作成
front.block.article.title__en: ARTICLE front.block.article.title__ja: Article front.block.article.read_more: More front.block.article.see_details: Read details admin.home.article_title: Article admin.content.article_management: Article admin.content.article.publish_date: Published on admin.content.article.display_status: Display Status admin.content.article.display_status__show: Displayed admin.content.article.display_status__hide: Hidden admin.content.article.title: Title admin.content.article.url: URL admin.content.article.body: Body admin.content.article.article_registration: Create News admin.content.article.new_window: Open in a New Window tooltip.content.article.url: If there is a page which provides the details of this article, enter its URL. You can also enter external URLs. tooltip.content.article.body: You can use HTML tags.
src\Eccube\Resource\locale\messages.ja.yaml をもとに app\Customize\Resource\locale\messages.ja.yaml を作成
front.block.article.title__en: Article front.block.article.title__ja: 記事 front.block.article.read_more: more front.block.article.see_details: 詳しくはこちら admin.home.article_title: 記事 admin.content.article_management: 記事管理 admin.content.article.publish_date: 公開日時 admin.content.article.display_status: 公開状態 admin.content.article.display_status__show: 公開 admin.content.article.display_status__hide: 非公開 admin.content.article.title: タイトル admin.content.article.url: URL admin.content.article.body: 本文 admin.content.article.article_registration: 記事登録 admin.content.article.new_window: 別ウィンドウで開く tooltip.content.article.url: この記事の詳細な内容を記したウェブページある場合、URLを入力します。外部サイトのURLなどを利用することもできます。 tooltip.content.article.body: HTMLタグが利用可能です。
app\config\eccube\packages\eccube.yaml に追記(news の直下)
eccube_article_start_year: 2000
■動作確認 管理画面の「コンテンツ管理 → 記事管理」から、データの登録編集削除を行える 挙動は基本的に「新着情報管理」と同じ
■プラグインの導入
プラグイン → プラグインを探す からプラグインをインストール インストール完了のダイアログが表示されたら「完了」ボタンを押す プラグイン一覧画面に遷移するので、プラグインを有効化する …というのが、プラグイン導入の基本的な流れ ■有用そうなプラグイン 有用そうなプラグインをメモ ただしプラグインは有料のものが多く、動作を試したいだけの場合でも購入する必要がある。ECCubeの導入には結局結構なお金がかかるかもしれない また https://www.ec-cube.net/owners/ にある「ダウンロードランキング(有料)」「ダウンロードランキング(無料)」「公式プラグイン」あたりは一度目を通しておくと良さそう プラグインは競合のリスクが常にあるようなので、可能ならできる限り同じ制作元のプラグインで統一する方が無難そう また部分的に自作すると「商品情報に項目を追加したが、CSV登録には対応していない」などの状態になりそう 公式のプラグインは安心感があるし、無料のものが多いので試す価値はある また、とある案件で「株式会社ブラテックのプラグインをメインに使おうか」を検討中 4.0系|EC-CUBE 4.0 Web API プラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=2121 4.0系|クーポンプラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=1923 4.0系|おすすめ商品管理プラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=1757 4.0系|関連商品プラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=1761 4.0系|商品レビュー管理プラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=1756 4.0系|売上集計プラグイン|株式会社イーシーキューブ(EC-CUBE開発元) https://www.ec-cube.net/products/detail.php?product_id=1759 4.0系|会員項目追加プラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1791 4.0系|会員項目追加プラグイン:検索アドオン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1834 4.0系|商品項目追加プラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1801 4.0系|商品CSV登録拡張プラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1802 4.0系|商品オプションプラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1787 4.0系|会員ランクプラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1710 4.0系|販売制限機能プラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1818 4.0系|セット商品販売(在庫管理)プラグイン for EC-CUBE4|あずみ.net https://www.ec-cube.net/products/detail.php?product_id=1833 4.0系|特定会員価格表示プラグイン for EC-CUBE4|あずみ.net https://www.ec-cube.net/products/detail.php?product_id=1929 4.0系|セット商品販売プラグイン(4.0系)|株式会社Diezon https://www.ec-cube.net/products/detail.php?product_id=1910 4.0系|会員機能無効化|ONE HAND RED https://www.ec-cube.net/products/detail.php?product_id=1734 4.0系|ランディングページ作成プラグイン|ITOBEN STYLE https://www.ec-cube.net/products/detail.php?product_id=1912 4.0系|新着情報をページ化するプラグイン(EC-CUBE4.0系対応)|seiyaan.com https://www.ec-cube.net/products/detail.php?product_id=1898 4.0系|カテゴリスライドショープラグイン for EC-CUBE4|株式会社アイデア工房 https://www.ec-cube.net/products/detail.php?product_id=1947 4.0系|カートクリアリンク追加プラグイン EC-CUBE4系|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=1730 4.0系|お問い合わせ管理 for EC-CUBE4|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=2004 4.0系|商品問い合わせプラグイン for EC-CUBE4|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=2005 4.0系|カテゴリー並び替えプラグインEC-CUBE4系 (レベル・階層も再設定)|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=1995 4.0系|キービジュアル管理 for EC-CUBE4|株式会社U-Mebius https://www.ec-cube.net/products/detail.php?product_id=2000 4.0系|[Ver.4]価格帯で選ぶブロック追加プラグイン|lecast system https://www.ec-cube.net/products/detail.php?product_id=2049 4.0系|VeriTrans4G決済プラグイン(4.0系)|ベリトランス株式会社 https://www.ec-cube.net/products/detail.php?product_id=1835 定期購入プラグイン「リピートキューブ」4系用|アイピーロジック株式会社 https://www.ec-cube.net/products/detail.php?product_id=1918 ■インストールできない1 インストールできない場合、以下などを参考にする また管理画面からのインストールよりも、コマンドラインからのインストールの方が格段に安定しているらしい 複数回エラーになった場合は、コマンドラインからのインストールを試すといい EC-CUBE4系でのプラグインエラーの原因と予防対策 - Qiita https://qiita.com/nanasess/items/791c9ec98f69ada93ea0 ■インストールできない2 ECCubeの管理画面から「ベリトランス4Gプラグイン」を導入すると、 html\composer.json に以下のコードが追加された(「ec-cube/VeriTrans4G」部分)
"doctrine/lexer": "^1.0", "doctrine/migrations": "^1.8", "doctrine/orm": "^2.6", + "ec-cube/VeriTrans4G": "^1.2", "ec-cube/plugin-installer": "~0.0.6", "egulias/email-validator": "^2.1", "friendsofphp/php-cs-fixer": "^2.10",
この状態で、別環境に環境を構築するために「composer install」を行おうとするとエラーになる いったんデフォルトの composer.json と composer.lock を配置し直し、 ECCubeをインストールしてからプラグインの導入を行えば対応できる 詳細は後述の「プラグインの導入: プラグインを導入済みのECCubeをセットアップする場合」を参照
■プラグインの導入: プラグインを導入済みのECCubeをセットアップする場合
■概要 composer.json には、追加プラグインの情報が含まれている(プラグインをインストールすると、composer.json と composer.lock が更新される) そのため何らかのプラグインを追加した後は、通常の composer install では新規にインストールできなくなる 具体的には、以下のようなエラーになる
[RuntimeException] You can not install the EC-CUBE plugin via `composer` command. Please use the `bin/console eccube:composer:require ec-cube/veritrans4g` instead.
bin/console で上記指定のコマンドを実行すれば良さそうだが、 初期状態はそもそも vendor が無いので以下のエラーになる
PHP Warning: require(/var/www/main/html/bin/../vendor/autoload.php): failed to open stream: No such file or directory in /var/www/main/html/bin/console on line 13 Warning: require(/var/www/main/html/bin/../vendor/autoload.php): failed to open stream: No such file or directory in /var/www/main/html/bin/console on line 13 PHP Fatal error: require(): Failed opening required '/var/www/main/html/bin/../vendor/autoload.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/main/html/bin/console on line 13 Fatal error: require(): Failed opening required '/var/www/main/html/bin/../vendor/autoload.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/main/html/bin/console on line 13
以下、ついに公式に対応される可能性が出たか プラグインの初期インストール機構 - Issue #4733 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4733 ■対応手順 [PHP] composerでocramius/package-versionsのインストールに失敗する - 端くれプログラマの備忘録 https://www.84kure.com/blog/2020/02/23/php-composer%E3%81%A7ocramius-package-versions%E3%81%AE%E3%82... 「--no-plugins --no-scripts」を付けてComposerでのインストールを行う これにより、上記プラグイン関連のエラーを回避できる 作業ディレクトリに移動する
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html
「--no-plugins --no-scripts」を付けてComposerをインストール
$ composer install --no-plugins --no-scripts Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error. Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 165 installs, 0 updates, 0 removals - Installing ocramius/package-versions (1.4.2): Loading from cache - Installing ec-cube/plugin-installer (0.0.8): Loading from cache - Installing kylekatarnls/update-helper (1.2.0): Loading from cache - Installing symfony/flex (v1.8.4): Loading from cache - Installing symfony/process (v3.4.42): Loading from cache - Installing symfony/finder (v3.4.42): Loading from cache - Installing symfony/polyfill-ctype (v1.17.1): Loading from cache - Installing symfony/filesystem (v3.4.42): Loading from cache - Installing symfony/polyfill-mbstring (v1.17.1): Loading from cache - Installing psr/log (1.1.2): Loading from cache 〜略〜 codeception/codeception suggests installing league/factory-muffin-faker (For Faker support in DataFactory module) codeception/codeception suggests installing phpseclib/phpseclib (for SFTP option in FTP Module) codeception/codeception suggests installing stecman/symfony-console-completion (For BASH autocompletion) Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead. Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead. Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead. Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested. Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead. Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested. Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested. Generating optimized autoload files
完了すると、プラグイン無しの状態でインストールされている この状態で引き続き「php bin/console eccube:composer:install」を実行しても、データベースに接続できないのでエラーになる(データベースへの接続情報を登録していないため) いったんECCube自体のセットアップを行う。ブラウザから以下にアクセスしてインストールする(後述の「ECCubeをインストール」と同じ手順でインストールできる) http://eccube4.local/ インストールできたらマイグレーションを実行する
$ php bin/console doctrine:migrations:migrate
続いて、管理画面から認証キーを設定する 以前の環境で設定された認証キーは、composer.json の最後の方にある「X-ECCUBE-KEY」という箇所に記録されている。よってこの値を設定する 改めて認証キーが composer.json に書き込まれるが、同じ認証キーを設定しているのでファイルの差分が発生しないことを確認する 以下のコマンドでプラグインをダウンロードする。これで管理画面からもプラグインを確認できる
$ php bin/console eccube:composer:install
最後に、以下でオートローダーを生成する
$ composer install
あとは、必要に応じて管理画面からプラグインを有効化&設定する ■試行錯誤メモ1 ※上記の「対応手順」なら、以下のようにややこしいことはしなくていいかも 以下を参考に、この現象を回避しようとしたもの EC-CUBE4 初回デプロイ - Qiita https://qiita.com/applexco/items/00831ba61936139e8b26 準備として、ECCubeデフォルトの composer.json と composer.lock を、それぞれ以下の名前で配置しておく(コミットもしておいて良さそう) composer-default.json composer-default.lock 以降はインストール時の手順 まずは作業ディレクトリに移動する
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html
以下を実行する代わりに
$ composer install
以下を実行する この時点では、独自に追加したライブラリやプラグインを含まない、ECCubeデフォルトのライブラリがインストールされる (composer-default.json をもとにインストールされる)
$ COMPOSER=composer-default.json composer install --no-scripts
実行できたら、ブラウザからアクセスしてインストールする(後述の「ECCubeをインストール」と同じ手順でインストールできる) インストールできたら、マイグレーションを実行する
$ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console doctrine:migrations:migrate
管理画面から認証キーを設定する 以下のコマンドでプラグインをダウンロードする。これで管理画面からもプラグインを確認できる
$ php bin/console eccube:composer:install
最後に、以下でオートローダーを生成する
$ composer install
あとは、必要に応じて管理画面からプラグインを有効化&設定する ■試行錯誤メモ2 ※上記の「対応手順」なら、独自プラグインのためにインストールしたパッケージにも対応できるかも 公式プラグインは上記で対応できそうだが、独自プラグインの場合はさらに問題があるみたい 独自プラグインのためにComposerで依存パッケージをインストールすると、依存解決ができなくなるらしい 現状問題は放置されている。ECCubeはComposerと相性が悪いみたい EC-CUBE4 独自プラグイン開発 ?独自プラグイン開発Tips - Qiita https://qiita.com/haruna-nagayoshi/items/27108c75eaf9511f3524 なかなかややこしいことになるようなので、どうしても対応するなら ・プラグインとvendorの内容を丸ごとGit管理対象にする ・データベースは「プラグイン導入済みの状態のdump」を作成し、初期データベースとする くらいか ただし強引さは否めない ■要調査メモ プラグインが作成するデータベースのテーブルに手を加える場合、どこで指定すべきか 通常のマイグレーションに含めると、初回はプラグインが存在しない問題がある 対象プラグインをGit管理に含めて、プラグインのインストール時に実行されるSQLを調整すべきか ただしその場合、運用中にテーブル定義を変更する場合はどうするか。「プラグインをいったん削除して再度インストール」は、データの保持を考えれば問題がありそう
■プラグインの導入: プラグインを追加削除したときの挙動を検証
■composerの実行 「composer install」の代わりに「php bin/console eccube:composer:install」を実行する (ただし初回はこのコマンドもエラーになる。詳細は「プラグインを導入済みのECCubeをセットアップする場合」を参照)
$ php bin/console eccube:composer:install <warning>Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error.</warning> [28.5MiB/0.03s] Loading composer repositories with package information [28.9MiB/0.04s] Installing dependencies (including require-dev) from lock file [29.8MiB/0.12s] Nothing to install or update [29.0MiB/0.14s] <warning>Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.0MiB/0.14s] <warning>Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead.</warning> [29.0MiB/0.14s] <warning>Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.0MiB/0.14s] <warning>Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead.</warning> [29.0MiB/0.14s] <warning>Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.0MiB/0.14s] <warning>Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.</warning> [29.0MiB/0.14s] <warning>Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.</warning> [29.0MiB/0.14s] Generating optimized autoload files [33.1MiB/14.84s] <warning>Warning: Ambiguous class resolution, "Eccube\Entity\Customer" was found in both "$baseDir . '/app/proxy/entity/src/Eccube/Entity/Customer.php" and "/var/www/main/html/src/Eccube/Entity/Customer.php", the first will be used.</warning> [32.4MiB/19.45s] Memory usage: 32.43MiB (peak: 40.82MiB), time: 19.45s
実行できたら、以下でオートローダーを生成する
$ composer install Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error. Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Nothing to install or update Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested. Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead. Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested. Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead. Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested. Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead. Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead. Generating optimized autoload files Deprecation Notice: Class Eccube\Entity\Customer located in ./app/proxy/entity/src/Eccube/Entity/Customer.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///usr/local/bin/composer/src/Composer/Autoload/ClassMapGenerator.php:201 Warning: Ambiguous class resolution, "Eccube\Entity\Customer" was found 2x: in "/var/www/main/html/app/proxy/entity/src/Eccube/Entity/Customer.php" and "/var/www/main/html/src/Eccube/Entity/Customer.php", the first will be used. Carbon 1 is deprecated, see how to migrate to Carbon 2. https://carbon.nesbot.com/docs/#api-carbon-2 You can run './vendor/bin/upgrade-carbon' to get help in updating carbon and other frameworks and libraries that depend on it. ocramius/package-versions: Generating version class... ocramius/package-versions: ...done generating version class Executing script cache:clear --no-warmup [OK] Executing script cache:warmup --no-optional-warmers [OK] Executing script assets:install --symlink --relative html [OK]
■composer install の警告 上記のとおり、composerを実行する際にwarningがいくつも表示される …が、これは気にしなくてもいいみたい プラグインインストール時にcomposerのwarningが出力される - Issue #4106 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4106 「composer 2.0 より、パッケージ名に大文字の記述が利用できなくなるため。 ec-cube 4では、composerは1.xを利用するため、動作に支障はありません。 また、プラグインのコードも変更する必要はありません。」 ■プラグインの追加 プラグイン一覧画面で「オーナーズストアから新規追加」ボタンを押してプラグインのインストール画面に進むことができる プラグインをインストールすると、その情報が composer.json と composer.lock にも書き込まれる この状態ならプラグインの情報が残っているので、次回は以下のコマンドでプラグインをインストールできる
$ php bin/console eccube:composer:install
■プラグインの削除 ※VeriTrans4G を削除したときの挙動 プラグイン一覧画面で「削除」ボタンを押すと app/Plugin/VeriTrans4G が削除され、 dtb_plugin テーブルからもデータが削除された 以下は削除されたときのログ
[32.1MiB/0.00s] <warning>ec-cube/veritrans4g is not required in your composer.json and has not been removed</warning> [33.7MiB/0.68s] Loading composer repositories with package information [35.0MiB/4.23s] Updating dependencies (including require-dev) [311.2MiB/13.72s] <warning>Writing /var/www/main/html/app/Plugin/.composer/cache/repo/https---repo.packagist.org/provider-symfony$polyfill-ctype.json into cache failed after 3842048 of 4103821 bytes written, only 291899080704 bytes of free space available</warning> [433.8MiB/27.69s] Package operations: 0 installs, 0 updates, 1 removal [454.4MiB/32.67s] - Removing ec-cube/veritrans4g (1.2.0) [451.3MiB/32.72s] <warning>Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested.</warning> [451.3MiB/32.72s] <warning>Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead.</warning> [451.3MiB/32.72s] <warning>Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.</warning> [451.3MiB/32.72s] <warning>Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.</warning> [451.3MiB/32.72s] <warning>Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead.</warning> [451.3MiB/32.72s] <warning>Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested.</warning> [451.3MiB/32.72s] <warning>Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.</warning> [451.3MiB/32.72s] <warning>Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.</warning> [451.5MiB/32.75s] Writing lock file [451.5MiB/32.75s] Generating optimized autoload files [454.2MiB/48.16s] <warning>Warning: Ambiguous class resolution, "Eccube\Entity\Customer" was found in both "$baseDir . '/app/proxy/entity/src/Eccube/Entity/Customer.php" and "/var/www/main/html/src/Eccube/Entity/Customer.php", the first will be used.</warning> [456.0MiB/53.29s] Memory usage: 456.01MiB (peak: 497.08MiB), time: 53.29s
※app/Plugin/VeriTrans4G/Resource/tgMdkPHP/tgMdk/log4php.properties を作成して、 そのファイル内で「log4php.appender.R1.File」の設定も行った状態でないと削除できなかった 削除すると composer.json と composer.lock からも該当プラグインの情報が削除された よって次回は php bin/console eccube:composer:install でのインストールではなく、管理画面からのインストールが必要 ■プラグインを直接削除したときの挙動 VeriTrans4G フォルダを直接削除してから以下を実行すると、新たに VeriTrans4G が作られた
$ php bin/console eccube:composer:install <warning>Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error.</warning> [28.4MiB/0.02s] Loading composer repositories with package information [28.8MiB/0.03s] Installing dependencies (including require-dev) from lock file [29.7MiB/0.10s] Package operations: 1 install, 0 updates, 0 removals [29.9MiB/0.12s] - Installing ec-cube/veritrans4g (1.2.0): [29.9MiB/0.14s] Loading from cache[29.9MiB/0.14s] [29.5MiB/5.64s] <warning>Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.5MiB/5.64s] <warning>Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead.</warning> [29.5MiB/5.64s] <warning>Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.5MiB/5.64s] <warning>Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead.</warning> [29.5MiB/5.64s] <warning>Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested.</warning> [29.5MiB/5.64s] <warning>Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.</warning> [29.5MiB/5.64s] <warning>Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.</warning> [29.5MiB/5.64s] Generating optimized autoload files [33.6MiB/14.81s] <warning>Warning: Ambiguous class resolution, "Eccube\Entity\Customer" was found in both "$baseDir . '/app/proxy/entity/src/Eccube/Entity/Customer.php" and "/var/www/main/html/src/Eccube/Entity/Customer.php", the first will be used.</warning> [32.9MiB/19.15s] Memory usage: 32.89MiB (peak: 41.27MiB), time: 19.15s
すでにプラグインが存在する場合は何も行われなかった カラの VeriTrans4G がある場合も何も行われなかった
$ php bin/console eccube:composer:install <warning>Deprecation warning: require.ec-cube/VeriTrans4G is invalid, it should not contain uppercase characters. Please use ec-cube/veritrans4g instead. Make sure you fix this as Composer 2.0 will error.</warning> [28.4MiB/0.02s] Loading composer repositories with package information [28.8MiB/0.03s] Installing dependencies (including require-dev) from lock file [29.7MiB/0.10s] Nothing to install or update [28.9MiB/0.12s] <warning>Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested.</warning> [28.9MiB/0.12s] <warning>Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead.</warning> [28.9MiB/0.12s] <warning>Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.</warning> [28.9MiB/0.12s] <warning>Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead.</warning> [28.9MiB/0.12s] <warning>Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested.</warning> [28.9MiB/0.12s] <warning>Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.</warning> [28.9MiB/0.12s] <warning>Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.</warning> [28.9MiB/0.12s] Generating optimized autoload files [33.0MiB/13.62s] <warning>Warning: Ambiguous class resolution, "Eccube\Entity\Customer" was found in both "$baseDir . '/app/proxy/entity/src/Eccube/Entity/Customer.php" and "/var/www/main/html/src/Eccube/Entity/Customer.php", the first will be used.</warning> [32.3MiB/17.58s] Memory usage: 32.34MiB (peak: 40.73MiB), time: 17.58s
よってプラグインを改造して使用する場合、 ・通常の手順でプラグインをインストールする(app/Plugin/VeriTrans4G にインストールされるとする) ・app/Plugin/VeriTrans4G をGit管理対象にし、ファイルをリポジトリに含める とすれば、あとは通常の手順でプラグインの改造も新規環境構築もできる はず ■プラグインを無効化したときの挙動 プラグイン一覧画面で「無効化」ボタンを押すと dtb_plugin テーブルで該当データの enabled が 1 から 0 になる 以下は参考までにデータの変化とテーブル定義
INSERT INTO `dtb_plugin` VALUES (1,'ベリトランス4G','VeriTrans4G',1,'1.2.0','1835',1,'2020-08-31 09:35:35','2020-08-31 09:51:07','plugin'); ↓ INSERT INTO `dtb_plugin` VALUES (1,'ベリトランス4G','VeriTrans4G',0,'1.2.0','1835',1,'2020-08-31 09:35:35','2020-09-24 05:53:57','plugin'); MariaDB [main]> select * from dtb_plugin; +----+----------------------+-------------+---------+---------+--------+-------------+---------------------+---------------------+--------------------+ | id | name | code | enabled | version | source | initialized | create_date | update_date | discriminator_type | +----+----------------------+-------------+---------+---------+--------+-------------+---------------------+---------------------+--------------------+ | 1 | ベリトランス4G | VeriTrans4G | 0 | 1.2.0 | 1835 | 1 | 2020-08-31 09:35:35 | 2020-09-24 05:53:57 | plugin | +----+----------------------+-------------+---------+---------+--------+-------------+---------------------+---------------------+--------------------+ 1 row in set (0.00 sec) MariaDB [main]> show columns from dtb_plugin; +--------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | code | varchar(255) | NO | | NULL | | | enabled | tinyint(1) | NO | | 0 | | | version | varchar(255) | NO | | NULL | | | source | varchar(255) | NO | | NULL | | | initialized | tinyint(1) | NO | | 0 | | | create_date | datetime | NO | | NULL | | | update_date | datetime | NO | | NULL | | | discriminator_type | varchar(255) | NO | | NULL | | +--------------------+------------------+------+-----+---------+----------------+ 10 rows in set (0.00 sec)
■プラグインの導入: ECCube VeriTrans4G決済プラグイン(4.0系)
■概要 4.0系|VeriTrans4G決済プラグイン(4.0系)|ベリトランス株式会社 https://www.ec-cube.net/products/detail.php?product_id=1835 ※2020年7月に確認すると、「EC-CUBE対応バージョン」は「4.0.0, 4.0.1, 4.0.2, 4.0.3」となっている 現状4.0.4はサポートしていないとのこと このプラグインを使うなら、ECCube本体は4.0.3を使う方が無難か。もしくは実質4.0.4でも問題ないか ※2021年2月に確認すると、「EC-CUBE対応バージョン」は「4.0.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.0.5」となっていた 以下にマニュアルがあるので、これをもとに進める https://www.ec-cube.net/upload/manual_file/04151400_5e9694fe5c61f.pdf ■API設定情報を確認 ベリトランス管理画面で、あらかじめAPI設定情報を確認しておく ダッシュボード下部で「API設定情報」の「API設定情報はこちら」をクリック 「API設定情報」として表示される内容を控えておく ■インストール(P.11) ECCube管理画面「プラグイン → プラグインを探す」からプラグインを入手 「購入する」をクリックする(無料で購入できる) 公式サイトで購入処理を行うと、「プラグイン → プラグインを探す」に「VeriTrans4G決済プラグイン(4.0系)」が表示される 「インストール」をクリックする インストール確認画面が表示されるので、再度「インストール」をクリックする インストールが完了したら、ダイアログに表示されている「完了」をクリックする プラグイン一覧で、プラグインを有効化する ステータスが「無効」から「有効」になれば成功 プラグインは以下に保存された html\app\Plugin\VeriTrans4G html\html\plugin\vt4g 以下にログファイルが作成された(必要に応じて書き込み権限を与えておく) html\var\log\mdk.log データベースに、以下のテーブルが作成された(説明はマニュアルより) plg_vt4g_order_log ... 決済ログ保持テーブル plg_vt4g_order_payment ... 決済情報保持テーブル plg_vt4g_payment_method ... 各決済方法の設定情報保持テーブル plg_vt4g_plugin ... プラグイン設定情報保持テーブル また、dtb_customerテーブルに以下の列が追加された `vt4g_account_id` varchar(100) DEFAULT NULL ■プラグインの設定(P.20) プラグイン一覧の設定アイコンをクリックする API設定情報でメモした内容をもとに、以下のように設定する マーチャントCCID: A100000000000001234567xx マーチャント認証鍵: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx マーチャントID: (空欄のまま) ハッシュシード: (空欄のまま) トークンAPI キー: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 取引IDプレフィックス: TEST_ 有効にする支払方法: クレジットカード決済 ダミーモード: ダミーモードで稼働 注文完了メール送信タイミング: 注文完了画面表示時 「登録」ボタンをクリックする ■支払い方法の設定(P.22) ECCube管理画面「設定 → 店舗設定 → 支払い方法設定」から設定を行う 今回は一覧から「クレジットカード決済」をクリックする 今回は以下を変更してみる 支払い種別: 「一括払い」のみにチェックを入れる ベリトランス会員IDプレフィックス: TEST_CARD_ 「登録」ボタンをクリックする ■配送方法の設定(P.26) ECCube管理画面「設定 → 店舗設定 → 配送方法設定」から設定を行う 今回は「サンプル業者」の詳細画面で「取り扱う支払方法」から「クレジットカード決済」にチェックを入れる 「登録」ボタンをクリックする ■動作確認(P.28) 通常の手順でカートに商品を入れ、レジに進む 配送方法で、上記で設定した「サンプル業者」を選択すると、お支払い方法で「クレジットカード決済」を選択できる 注文確認画面に進むと、 お支払方法: クレジットカード決済(¥0) と表示されている(「(¥0)」は手数料が表示されており、「設定 → 店舗設定 → 支払方法設定」から設定できる) そのまま「注文する」ボタンをクリックする 「クレジットカード決済」という画面が表示される ※この時点でECCube管理画面の「受注管理 → 受注一覧」を確認すると、以下のデータが確認できる 対応状況: 新規受付 支払方法: クレジットカード決済 決済状況: (空欄) クレジットカード番号: 4111111111111111 カード有効期限: 12/20 カード名義人名: TARO YAMADA セキュリティコード: 123 お支払い方法: 一括払い 「入力したクレジットカード情報でお支払い」ボタンをクリックする ご注文完了画面に遷移する 通常の完了メッセージとご注文番号に加えて、「クレジットカード決済情報」として以下が表示された 決済取引ID: TEST_363534656663653700000000002 ■決済の確認 ベリトランス管理画面の取引検索画面で 決済品目: クレジットカード決済 取引ID: TEST_363534656663653700000000002 テスト取引: 「除外する」チェックを外す として検索 先程テストしたデータが表示されていることを確認する この時点では、「ステータス」は「与信」となっている ECCube管理画面「受注管理 → 受注一覧」から確認すると、以下のデータが確認できる 対応状況: 新規受付 支払方法: クレジットカード決済 決済状況: 与信 また、詳細画面の下部に「ベリトランス4G決済情報」が表示されている 「売上確定(実売上)実行」をクリックすると売上が確定されるみたい 対応状況: 新規受付 支払方法: クレジットカード決済 決済状況: 売上 なお「対応状況」は自動で変わらなかった(「新規受付」のまま) 手動で「入金済み」に変更することはできた ベリトランス管理画面の取引検索画面で再度確認すると、 「ステータス」が「売上」となっているデータが追加されていた ■決済時の「与信」を「与信+売上」に変更 クレジットカード決済のオーソリとは?安全性をさらに高める対策も紹介|クレジットカード決済代行のベリトランス株式会社 https://www.veritrans.co.jp/tips/column/authorization.html クレジットカード決済では売上確定などの処理は必要ですか? - イプシロンよくある質問 https://www.epsilon.ne.jp/support/faq/ufaqs/credit002/ ECCube管理画面「設定 → 店舗設定 → 支払い方法設定 → クレジットカード決済」から設定を行う 処理区分: 与信のみ ↓ 処理区分: 与信+売上 「登録」ボタンをクリックする この状態でクレジット決済すると、購入完了直後で以下の状態になった 対応状況: 入金済み 支払方法: クレジットカード決済 決済状況: 売上 なお、与信から売上確定に変更できる期間は60日間と定められている クレジットカードのオーソリゼーションのお話。 - メモ代わりのブログ https://murabit.hatenablog.com/entry/2020/09/01/182838 また、デビットカードの場合はその性質上与信の時点で引き落とされる デビットカードの正しい基礎知識と使い方 | JCBデビット https://www.jcb.jp/products/jcbdebit/article1/ ■決済キャンセルへの対応 ・ECCubeの注文詳細画面で「[売上済]再売上(実売上)実行」をクリックすると、ベリトランス管理画面で「ステータス」が「キャンセル(売上)」のデータが追加された 前回の売上をキャンセルして、再度売上にしたということだと思われる ・同画面で「取消(返品)実行」をクリックすると、決済が取り消された 取り消し後、詳細画面に「お支払い合計と決済の金額が異なります。」と警告が表示されるようになった 恐らく「入金済みで売上金額も記録したが、その後取り消されたのでカード決済状況とデータベースの内容に差異がある」という警告だと思われる (この警告は、再オーソリを行なうことで表示されなくなるみたい。後述の「金額変更への対応」「与信切れへの対応」「「お支払い合計と決済の金額が異なります。」の表示が残るパターン」も参照) また、「対応状況」は「新規受付」になっている ■決済金額減額への対応 ※以下で正しいはず…というもの 一例だが、以下の手順で決済金額の減額ができる 1. 「商品A」と「商品B」の商品を普通に購入し、クレジットカードで決済完了 2. 管理画面から受注情報を編集して「商品B」を削除し、内容を確定させる 3. 「お支払い合計と異なります」のエラーになる 4. 「再売上(実売上)実行」ボタンを押す このとき、決済ログには以下が記録された。(下の2行は、決済金額減額によって追加されたもの)
2023-07-10 18:09:59 決済取引ID TEST_386534616533616300000001456, クレジットカード決済 [与信]成功 2023-07-10 18:11:03 決済取引ID TEST_386534616533616300000001456, クレジットカード決済 [売上]成功, 売上確定金額 11,000 2023-07-10 18:13:14 決済取引ID TEST_653930663739316100000001456, クレジットカード決済 [売上]成功, 再取引金額 11,000 2023-07-10 18:13:16 決済取引ID TEST_386534616533616300000001456, クレジットカード決済 [取消]成功, 取消金額 19,800
決済取引IDが変更されているが、決済情報は以下のとおり変化が無かった
決済取引ID TEST_386534616533616300000001456
「決済取引ID」は変わらず古い方が表示されているが、 ログを確認する限り減額は行われている(3865から始まる決済はキャンセルされ、6539から始まる決済が登録されている) 「決済取引ID」は新しい方が表示される方が自然に思うが、このプラグインの仕様みたい (意図的に「最初の取引」の情報を残しているのか。もしくは単に不具合の可能性もあるが詳細不明) なお決済金額を変更する場合、与信の金額よりも小さければ(つまり減額なら)そのまま処理できる 決済金額の増額については、後述の「決済金額増額への対応」を参照 決済金額を減額する方法が知りたい|クレジットカード決済代行の株式会社DGフィナンシャルテクノロジー(DGFT,旧:ベリトランス株式会社) https://www.veritrans.co.jp/trial/4g/faq/3g_qno_64.html ■決済金額増額への対応 ※以下で正しいはず…というもの ユーザ側から9,800円の商品を購入。 合計15,180円。 決済ログには以下が記録された。
2021-05-07 18:34:15 決済取引ID TEST_383533616661663500000000370, クレジットカード決済 [与信]成功
金額が変更になる例として、管理画面で72,000円の商品を追加。 合計94,380円。 いったん「保存する」をクリック。 保存すると「お支払い合計と決済の金額が異なります。」と表示される。 この状態で「再オーソリ実行」ボタンをクリック。 決済ログには以下が追加で記録された。
2021-05-07 18:37:02 決済取引ID TEST_346461343463396600000000370, クレジットカード決済 [与信]成功, 再取引金額 94,380 2021-05-07 18:37:04 決済取引ID TEST_383533616661663500000000370, クレジットカード決済 [取消]成功, 取消金額 15,180
https://pay.veritrans.co.jp/maps/search の取引検索画面で 決済品目: クレジットカード決済 取引ID: (上記でメモした値) テスト取引: 「除外する」チェックを外す として検索 先程テストしたデータが表示されていることを確認する (「TEST_383533616661663500000000370」と「TEST_346461343463396600000000370」でそれぞれ検索する) なお決済金額を増額する場合、再度与信が行われる もし再度の与信に失敗した場合、元の受注をキャンセルして、新しい受注を登録する 与信については、後述の「与信切れへの対応」も参照 決済金額を増額する方法が知りたい|クレジットカード決済代行の株式会社DGフィナンシャルテクノロジー(DGFT,旧:ベリトランス株式会社) https://www.veritrans.co.jp/trial/4g/faq/3g_qno_65.html ■与信切れへの対応 ※以下で正しいはず…というもの ドキュメントに明示されているわけでは無いので注意 ECCubeがどうこうではなく一般的なクレジットカードの仕様として、 購入時にオーソリ(仮売上処理)を行うことで、後から購入確定と引き落としを行うことができる ただしこれには期限が定められていて、最大60日と決められている クレジットカードのオーソリゼーションのお話。 - メモ代わりのブログ https://murabit.hatenablog.com/entry/2020/09/01/182838 それを踏まえてベリトランスの仕様として、オーソリの期限が切れると「与信有効期限切れ」というステータスになる これをECCube側で自動感知する仕組みは用意されていない そしてECCubeの挙動として、オーソリ期限が切れたものを決済したければ「再オーソリ実行」ボタンを押して再度オーソリを行うことができ、 これによりベリトランス側でのステータスも変更される (新たなオーソリ(与信)を取得後、元の取引のキャンセルが実行される) あとは通常の手順で売上確定処理を行うことができる なお、ここで元の取引が与信有効期限切れになっていると、取引のキャンセルに失敗するため、エラーが表示される が、あらたな与信の取得に成功していれば、売上確定処理は可能 …となっている模様 「再オーソリ実行」に「最大60日」の期限が関係あるかどうかまでは調べられていない ■「お支払い合計と決済の金額が異なります。」の表示が残るパターン ※クライアントからの依頼で他の方に調査してもらった内容を、調整して転載したもの 「お支払い合計と決済の金額が異なります。」の表示は、 ベリトランスプラグイン用のテーブルデータと、ECCube標準の受注テーブルデータの金額とが異なっている場合に表示されている模様 前者は plg_vt4g_order_payment.memo10 に(PHP配列の内容がシリアライズされて)格納されており、後者は dtb_order.payment_total に格納されている
MySQL [recole]> select memo10 from plg_vt4g_order_payment where order_id = 26989; +---------------------------------------------------------------+ | memo10 | +---------------------------------------------------------------+ | a:2:{s:11:"card_amount";d:84260;s:9:"card_type";s:5:"61C06";} | +---------------------------------------------------------------+ 1 row in set (0.00 sec) MySQL [recole]> select payment_total from dtb_order where id = 26989; +---------------+ | payment_total | +---------------+ | 75460.00 | +---------------+ 1 row in set (0.00 sec)
何のために表示しているかはベリトランスに聞くのが確実だが、おそらく再オーソリ・再売上を促すために表示していると思われる この警告は、残るパターンとそうでないパターンがある 警告が残るパターン: 1. 受注登録(商品は2つ) 2. クレジット決済実施 3. 商品を1つ削除し、保存ボタンを押下する前に再オーソリボタン押下(減額前の金額で再オーソリされる) 4. 商品が消えていないので、もう一度商品を削除し、保存ボタン押下 警告が残らないパターン: 1. 受注登録(商品は2つ) 2. クレジット決済実施 3. 商品を1つ削除し、保存ボタンを押下後、再オーソリボタン押下(減額後の金額で再オーソリされる) 前者であっても「オーソリの金額は減額前だが、実際に売上として記録する際は(タイムラグがあるので)減額後の金額が使われるみたい」 ただしどちらにせよ、恐らく「警告が残らないパターン」の手順で対応するのが正しい流れだと思われる ■「本人認証(3Dセキュア)」を使用 クレジットカード決済の安全性を高める「3Dセキュア」とは | 企業のお金とテクノロジーをつなぐメディア「Finance&Robotic」 https://www.robotpayment.co.jp/blog/creditcard/4046/ ECCube管理画面「設定 → 店舗設定 → 支払い方法設定 → クレジットカード決済」から設定を行う 本人認証(3Dセキュア): 利用しない ↓ 本人認証(3Dセキュア): 利用する 「登録」ボタンをクリックする この状態でクレジット決済すると、直後に以下の画面が表示された(何のデザインも無い簡素なページだった)
3D-Secure Authentication dummy page 消費者はこのタイミングで本人認証のための情報(パスワード等)を入力します。 これはダミーサイトのため、認証情報の入力は省略します。 ボタンを押して次のページに進んで下さい。 In this sequence, consumers will enter the password for authentication. Please press the button. Password is omitted Because this is a dummy site. [ OK ]
「OK」をクリックすると「決済結果にリダイレクト中です」と表示され、しばらく待つと注文完了画面が表示された 恐らくプラグインの設定で「ダミーモード」を「本番モードで稼働」にすればいいとは思うが、テストサイトで有効にして大丈夫かは要確認 ■ベリトランスのログをダウンロード 「設定 → システム設定 → ベリトランス4G ログダウンロード」 ログをダウンロードできない場合、以下ファイルの「log4php.appender.R1.File」項目で適切なパスが指定されているか確認する app/Plugin/VeriTrans4G/Resource/tgMdkPHP/tgMdk/log4php.properties ■本番アカウントでの決済 「オーナーズストア → プラグイン → プラグイン一覧 → ベリトランス4G → 設定(歯車アイコン)」 ベリトランスと契約すると、本番環境用の設定が送られてくるので設定する 具体的には「マーチャントCCID」「マーチャント認証鍵」「トークンAPI キー」を本番環境用の値に設定する 「取引IDプレフィックス」は設定すると決済取引IDの先頭に付加されるので、その案件用に判りやすい値にしておくといい 本番用のマーチャントCCIDなどを設定しても、ダミーモードだとテストカードでの決済になった(本物のカードだとエラーになった) これは、テストカードを使ってベリトランスとの疎通確認を行うためのものだと思われる ベリトランスの本番アカウントの管理画面には、決済履歴は記録されない 本番用のマーチャントCCIDなどを設定した状態で、本番モードにすると本物のカードで決済完了できた (本番モードにするには同画面で「メール送信に同意する」にチェックを入れる必要があった) これは実際に運用するためのもの。運用開始前に、このモードにした上で本物のカードで決済を試しておく ベリトランスの本番アカウントの管理画面に、決済履歴が記録される ■管理画面で受注登録する際のクレジットカード決済 プラグインを導入すると、管理画面で受注登録する際「支払方法」に「クレジットカード決済」が現れる ただしこれを選択して受注登録しようとしても 「クレジットカード決済に必要な情報がありません。他の支払方法を選択してください。」 というエラーになって登録できない ユーザのカード情報を入力することは実質不可能なので、ある意味当然の仕様ではある ■クレジットカード決済を中断した際の注文日時の記録 注文時に「クレジットカード払い」を選択した場合、正常に注文が進めば以下の遷移が行われる /shopping (注文画面) /shopping/confirm (注文確認画面) /shopping/vt4g_payment (クレジット決済画面) /shopping/complete (完了画面) 「/shopping/vt4g_payment」の画面で止まっていた場合、決済処理は完了されない 購入時にサーバが重いなどの原因で、例えば 1. ご注文内容確認画面に遷移 2. クレジット決済画面に遷移 3. クレジット決済ボタンを押下した後、なかなか画面が変わらないので離脱 という操作をされると、つまり決済が中断されると、直感的でない挙動があった 具体的には、クレジットカード決済を完了せずに離脱すると ・「注文日」が空欄の状態になる ・その受注情報を編集すると、最初に編集ボタンを押した日時が「注文日」として記録される となる これ自体はプラグインの標準の振る舞いではあるが、知らないと「何故こんな注文日なんだ」となるので注意 本来の注文日を調べたい場合、注文番号をもとに
SELECT DATE_FORMAT(DATE_ADD(create_date, INTERVAL 9 HOUR),'%Y-%m-%d %H:%i:%s') AS create_date, DATE_FORMAT(DATE_ADD(update_date, INTERVAL 9 HOUR),'%Y-%m-%d %H:%i:%s') AS update_date, DATE_FORMAT(DATE_ADD(order_date, INTERVAL 9 HOUR),'%Y-%m-%d %H:%i:%s') AS order_date FROM dtb_order WHERE id = 74088 ;
このように検索すれば、以下のような値を得ることができる
create_date: 2023-07-15 03:43:25 update_date: 2023-07-15 09:47:01 order_date: 2023-07-15 09:47:01
create_dateがデータベースに記録された日時なので、この日時の前後数分を調べることで対象のログを見つけられるはず また「ご注文内容のご確認」画面で「注文する」ボタンを押した際に
[注文処理] 注文処理を開始します. [
の文言がログに出力される これをもとに注文処理を開始した厳密な日時を得ることができる (初めからこの文言でログを検索するのも有効) 以下、実際に操作を試した内容 お客様情報を入力後、ご注文手続き画面へ進んだ時点で、以下の受注情報が作成される この受注情報は、受注管理の「購入処理中」に現れる(絞り込まないと表示されない) 対応状況: 購入処理中 支払方法: クレジットカード決済 注文日: (空欄) 更新日: (ご注文手続き画面へ進んだ日時) ユーザ側からクレジット決済で注文を行う クレジットカード情報入力画面へ進んだ時点で、以下のように受注情報が更新される この受注情報は、受注管理の「新規受付」に現れる(絞り込まなくても表示される) 対応状況: 新規受付 支払方法: クレジットカード決済 注文日: (空欄) 更新日: (注文した日時) 決済を完了せずに離脱すると、上記のデータがそのまま残る この状態で管理画面の受注編集で「保存する」ボタンを押すと、以下のようにその時点の注文日と更新日が記録される 対応状況: 新規受付 支払方法: クレジットカード決済 注文日: (ボタンを押した日時) 更新日: (ボタンを押した日時) さらにボタンを押すと、以降は注文日はそのままに、更新日が記録される 例えばこの後すぐにキャンセル処理を行うと、「ユーザ側から注文された数秒後に、管理画面から決済がキャンセルされた」ように見えるデータができてしまう 実際に上記のデータができてしまったとき、ベリトランス側でのクレジットカード決済の与信は成功となっていた (成功するかどうかは、そのときの状況による可能性はある) 決済が完全に完了されていた場合、該当の注文は「対応状況」を「入金済み」にしておくのが混乱が無いかと思われる 決済が行われていなかった場合、該当の注文は「対応状況」を「注文取り消し」にするなどし、注文者に「改めて注文作業をしていただきたい」旨を伝えるのがいいかと思われる もしベリトランス管理画面に「完了ではないが、おかしなデータが残っている」という場合、ベリトランス側に問い合わせるのがいいと思われる ■クレジットカード決済の後に完了画面へ遷移できなかった際の記録 購入時にサーバが重いなどの原因で、例えば 1. ご注文内容確認画面に遷移 2. クレジット決済画面に遷移 3. クレジット決済ボタンを押下した後、なかなか画面が変わらないのでブラウザバックを実施 という操作をされると、 対応状況: 新規受付 支払方法: クレジットカード決済 注文日: (空欄) 更新日: (注文した日時) のままのデータが残ることがある このとき、クレジットカード決済の与信は成功となっていた 操作のタイミングによってはまた異なる結果になるかもしれないが、そのようなことがあったメモとして残しておく 決済の中断については「クレジットカード決済を中断した際の注文日時の記録」も参照 ■決済時の警告ログ 決済時のログを確認すると、以下のようになっていた
07/29 14:38:18 GET /shopping 200 07/29 14:38:28 POST /shopping/confirm 200 07/29 14:38:38 POST /shopping/checkout 302 07/29 14:38:38 GET /shopping/vt4g_payment 200
その際のECCubeログは以下のとおり
07/29 14:38:38 front.INFO [N/A] [b79945c] [N/A] [Eccube\Log\Logger:log:66] - INIT [] [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:38 request.INFO [N/A] [b79945c] [N/A] [Symfony\Component\HttpKernel\EventListener\RouterListener:onKernelRequest:123] - Matched route "vt4g_shopping_payment". {"route":"vt4g_shopping_payment","route_parameters":{"_controller":"Plugin\\VeriTrans4G\\Controller\\PaymentController::index","_route":"vt4g_shopping_payment"},"request_uri":"https://example.com/shopping/vt4g_payment","method":"GET"} [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:38 front.INFO [411dfcf9] [b79945c] [983] [Eccube\Log\Logger:log:66] - PROCESS START ["vt4g_shopping_payment"] [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:38 front.INFO [411dfcf9] [b79945c] [983] [Eccube\Log\Logger:log:66] - LOGIC START ["vt4g_shopping_payment"] [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:38 php.INFO [411dfcf9] [b79945c] [983] [Symfony\Component\Debug\ErrorHandler:handleError:532] - User Deprecated: The "Eccube\Security\Core\Encoder\PasswordEncoder" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: The \"Eccube\\Security\\Core\\Encoder\\PasswordEncoder\" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. at /var/www/main/html/vendor/symfony/dependency-injection/Container.php:282)"} [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:38 php.INFO [411dfcf9] [b79945c] [983] [Symfony\Component\Debug\ErrorHandler:handleError:532] - User Deprecated: The "Eccube\Security\Core\Encoder\PasswordEncoder" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: The \"Eccube\\Security\\Core\\Encoder\\PasswordEncoder\" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. at /var/www/main/html/vendor/symfony/dependency-injection/Container.php:282)"} [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:39 request.INFO [411dfcf9] [b79945c] [983] [Symfony\Component\HttpKernel\EventListener\RouterListener:onKernelRequest:123] - Matched route "block_search_product". {"route":"block_search_product","route_parameters":{"_controller":"Eccube\\Controller\\Block\\SearchProductController::index","_route":"block_search_product"},"request_uri":"https://example.com/block/search_product","method":"GET"} [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:39 front.INFO [411dfcf9] [b79945c] [983] [Eccube\Log\Logger:log:66] - LOGIC END ["vt4g_shopping_payment"] [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183] 07/29 14:38:39 app.INFO [N/A] [b79945c] [983] [Eccube\Log\Logger:log:68] - PROCESS END ["vt4g_shopping_payment"] [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183]
以下のエラーらしきログがあるのは気になったが、
User Deprecated: The "Eccube\Security\Core\Encoder\PasswordEncoder" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. { "exception":"[object] (ErrorException(code: 0): User Deprecated: The \"Eccube\\Security\\Core\\Encoder\\PasswordEncoder\" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. at /var/www/main/html/vendor/symfony/dependency-injection/Container.php:282)" } [GET, /shopping/vt4g_payment, 172.22.62.32, https://example.com/shopping/confirm, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183]
このエラーメッセージは、ECCube標準でも出力されるらしい これ自体は特に問題無さそう EC-CUBE 開発コミュニティ - フォーラム https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=23554&forum=10 ■トラブル: プラグインが動作しない プラグイン一覧で「ベリトランス4G」の設定アイコンが表示されないことがあった 設定画面に直接アクセスしてもエラーになる html/var/log/mdk.log が存在しない場合、以下の手順でファイルを作成してからブラウザを再読込すると表示された
$ cd html/var/log $ touch mdk.log $ chmod 0666 mdk.log
なお、上記トラブルが発生したのは複数台構成の環境で、rsyncで双方向動機する環境だった html/var 内はログファイル置き場なので、同期対象外としていた ■トラブル: 特定範囲の日時のみ売上確定できていない ログを確認したところ、7/18の12:53〜13:15において、ECCubeから決済代行会社に対する売上確定の通信処理がエラーとなっていた 障害情報を調べたところ、下記のとおり、決済代行会社側で決済システムの障害が発生していた(DGFTというのはベリトランスのこと。商号を変更らしい) 一部のお客様における弊社決済サービスがご利用できない不具合に関するお詫び https://www.dgft.jp/company/info/2021/apology202107.html ECCubeの決済プラグインの仕様では、先の「与信切れへの対応」に書いた対応をするようになっている ただし今回はベリトランス側の障害なので、「再オーソリ実行」だけで対応できるかは何とも言えない ベリトランスに確認すると、以下の回答があった
EC-CUBEのプラグインでは「再オーソリ実行」によって 新たなオーソリ(与信)を取得後、元の取引のキャンセルを実行します。 ここで、元の取引が与信有効期限切れになっていると、 取引のキャンセルに失敗するため、エラーが表示されるのですが、 あらたな与信の取得に成功していれば、売上確定処理は可能です。 操作をお試しいただき、与信が成功し、売上確定処理が成功したことを 念のためMAPでもご確認いただきながら、ご対応を進めていただきますようお願いいたします。
■トラブル: 特定のクレジットカードが利用できない エラーコード AG39000000000000 が表示され、クレジットカード決済できないという問い合わせがあった 所持している3種類のカードで試したがどれも駄目だったが、対象カードは他社では使えたとのこと 調査すると、以下のログが記録されていた ・カードA: 7回試行 コード: AG39000000000000 エラーメッセージ: 取引判定保留(有人判定)です。 [G30](7回ともすべてこのエラー) ・カードB: 1回試行 コード: AG33000000000000 エラーメッセージ: カード使用不可です。 [G12] ・カードC: 1回試行 コード: AG45000000000000 エラーメッセージ: 1日限度額オーバーです。 [G55] ベリトランスのサイトからダウンロードできる結果コード一覧によると、 それぞれ想定される原因は下記のようになっている AG39: 取引判定保留(有人判定) カード会社のモニタリング上電話確認が必要なカードであり、カードがネットでは使用できない状態です。 AG33: カード使用不可 カードが使用できない状態です。限度額オーバーやカード会社のモニタリング上使用できない場合、脱会済カードの場合等の理由が考えられます。 AG45: 1日限度額オーバー 限度額オーバーの為、カードが使用できない状態です。 「取引判定保留(有人判定)」というのは、 カード会社が「本人以外の使用」「立て続けに商品を購入」「急に高額な商品を買った」などといった理由で使用をストップさせているときに表示されるもの カードの所有者からカード会社に連絡すると解除されるケースが多い ただしカードの所有者に確認すると、今回は「他社ではカードが使えた」とのことだった システムとしては同日に決済できている方もいるため、問題は無いと思われる 「他社では使えた」ということについては、以下のようなことが考えられる ・他社で使ったのち、本サイトで使ったため、限度額に達した ・本サイトで使ったのち、他社で使用する際は日をまたいだため、限度額に達していなかった ・本サイトで使ったのち、他社で使用する際は限度額に達しない金額だった ・クレジット決済代行会社によってセキュリティが働くロジックが違う ・ネットではなく対面で利用したため
■プラグインのカスタマイズ
※未検証 例えばプラグインの以下のファイルは app/Plugin/ProductReview4/Resource/template/default/review.twig 以下に配置することで上書きできるみたい? app/template/default/ProductReview4/Resource/template/default/review.twig 実案件でそういう配置になっているものがあった 独自に改造して配置場所を変えている可能性もあるが、改めて調査したい
■プラグインの作成
■プラグインでページを作成
$ php bin/console eccube:plugin:generate EC-CUBE Plugin Generator Interactive Wizard =========================================== name [EC-CUBE Sample Plugin]: > サンプルページプラグイン code [Sample]: > SamplePage ver [1.0.0]: > 1.0.0 [OK] Plugin was successfully created: サンプルページプラグイン SamplePage 1.0.0
以下の場所にプラグインの雛形が作成される html\app\Plugin\SamplePage ページ表示用にファイルを作成する html\app\Plugin\SamplePage\Controller\SampleController.php
<?php namespace Plugin\SamplePage\Controller; use Eccube\Controller\AbstractController; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\Routing\Annotation\Route; class SampleController extends AbstractController { /** * @Route("sample", name="sample") * @Template("@SamplePage/default/index.twig") */ public function index() { return []; } }
html\app\Plugin\SamplePage\Resource\template\default\index.twig
{% extends 'default_frame.twig' %} {% block main %} <p>サンプルページです</p> {% endblock %}
以下のコマンドでプラグインをインストールできる プラグインファイルをリポジトリに含めていたとしても、インストールコマンドの実行は必要
$ php bin/console eccube:plugin:install --code=SamplePage
管理画面の「プラグイン → プラグイン一覧」の「ユーザー独自プラグイン」に、作成したプラグインが表示される(上記コマンドを実行して初めて表示される) 一覧に表示されている三角の「有効化」ボタンで、プラグインを有効化できる もしくは、以下のコマンドでプラグインを有効化することもできる
$ php bin/console eccube:plugin:enable --code=SamplePage
プラグインを有効化すると、以下でプラグインによって作成されたページを表示できる ただしこの時点では、他ページのようなヘッダとフッタは表示されない http://eccube4.local/sample 一覧に表示されている四角2つの「無効化」ボタンで、プラグインを有効化できる もしくは、以下のコマンドでプラグインを無効化することができる
$ php bin/console eccube:plugin:disable --code=SamplePage
以下のコマンドでプラグインをアンインストールできる アンインストール後、念のためキャッシュを削除しておく
$ php bin/console eccube:plugin:uninstall --code=SamplePage $ php bin/console cache:clear --no-warmup
■プラグインで作成したページにヘッダとフッタを表示 引き続き、プラグインの有効化時にページとレイアウトに関する情報がデータベースに登録されるようにする 以下のファイルを作成する このファイルはプラグインの有効化・無効化時に実行されるので、ここにページレイアウト登録のためのプログラムを記述する html\app\Plugin\SamplePage\PluginManager.php
<?php namespace Plugin\SamplePage; use Eccube\Entity\PageLayout; use Eccube\Plugin\AbstractPluginManager; use Eccube\Repository\LayoutRepository; use Eccube\Repository\PageLayoutRepository; use Eccube\Repository\PageRepository; use Symfony\Component\DependencyInjection\ContainerInterface; class PluginManager extends AbstractPluginManager { // 1: トップページ用レイアウト, 2:下層ページ用レイアウト const ADD_PAGE_LAYOUT_ID = 2; // ページ設定 const ADD_PAGE_NAME = "サンプルページ"; const ADD_PAGE_URL = "sample"; const ADD_PAGE_FILE_NAME = "SamplePage/Resource/template/default/index"; const ADD_PAGE_META_ROBOTS = "noindex"; // or null // 0: Controller不要, 2: Controller必要 const ADD_PAGE_EDIT_TYPE = 2; /** * プラグイン有効化時に走る * @param array $meta * @param ContainerInterface $container */ public function enable(array $meta, ContainerInterface $container) { $this->createPage($container); } /** * プラグイン無効化時・アンインストール時に走る * @param array $meta * @param ContainerInterface $container */ public function disable(array $meta, ContainerInterface $container) { $this->deletePage($container); } /** * ページ情報を挿入する dtb_page, dtb_page_layout * @param ContainerInterface $container */ private function createPage(ContainerInterface $container) { // dtb_page に存在しないことを確認する $pageRepository = $container->get(PageRepository::class); $pageFindResult = $pageRepository->findOneBy(["url" => $this::ADD_PAGE_URL]); if (is_null($pageFindResult) == false) return; // dtb_layout から下層ページ用レイアウトを取得する $layoutRepository = $container->get(LayoutRepository::class); $underLayout = $layoutRepository->findOneBy(["id" => $this::ADD_PAGE_LAYOUT_ID]); // dtb_page_layout の次のSortNoを取得する $pageLayoutRepository = $container->get(PageLayoutRepository::class); $LastPageLayout = $pageLayoutRepository->findOneBy([], ['sort_no' => 'DESC']); $nextSortNo = $LastPageLayout->getSortNo() + 1; // EntityManager準備 $em = $container->get('doctrine.orm.entity_manager'); $em->beginTransaction(); // INSERT INTO dtb_page $page = $pageRepository->newPage(); $page->setName($this::ADD_PAGE_NAME) ->setUrl($this::ADD_PAGE_URL) ->setFileName($this::ADD_PAGE_FILE_NAME) ->setEditType($this::ADD_PAGE_EDIT_TYPE) ->setMetaRobots($this::ADD_PAGE_META_ROBOTS); $em->persist($page); $em->flush($page); // INSERT INTO dtb_page_layout $pageLayout = new PageLayout(); $pageLayout->setLayout($underLayout) ->setLayoutId($underLayout->getId()) ->setPageId($page->getId()) ->setSortNo($nextSortNo) ->setPage($page); $em->persist($pageLayout); $em->flush($pageLayout); $em->commit(); } /** * ページ情報を削除 dtb_page, dtb_page_layout * @param ContainerInterface $container */ private function deletePage(ContainerInterface $container) { // dtb_page に存在することを確認する $pageRepository = $container->get(PageRepository::class); $page = $pageRepository->findOneBy(["url" => $this::ADD_PAGE_URL]); if (is_null($page)) return; // EntityManager準備 $em = $container->get('doctrine.orm.entity_manager'); $em->beginTransaction(); // DELETE FROM dtb_page WHERE インストール時にINSERTしたページ $em->remove($page); $em->flush($page); // DELETE FROM dtb_page_layout WHERE インストール時にINSERTしたページレイアウト $pageLayoutRepository = $container->get(PageLayoutRepository::class); $pageLayout = $pageLayoutRepository->findOneBy(["page_id" => $page->getId()]); if(is_null($pageLayout) === false){ $em->remove($pageLayout); $em->flush($pageLayout); } $em->commit(); } }
プラグインを無効化し、再度有効化する これによって、上記で作成したファイルの内容が実行される
$ php bin/console eccube:plugin:disable --code=SamplePage $ php bin/console eccube:plugin:enable --code=SamplePage
以下を再読込すると、ヘッダやフッタが表示されている http://eccube4.local/sample [EC-CUBE4] ヘッダー・フッター付きの新規ページを作成する方法→プラグイン化 - Qiita https://qiita.com/seiyaan/items/382e3d2107ca859c38ad ■プラグインで作成したページに商品情報を表示 html\app\Plugin\SamplePage\Controller\SampleController.php
<?php namespace Plugin\SamplePage\Controller; use Eccube\Controller\AbstractController; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\Routing\Annotation\Route; use Eccube\Repository\ProductRepository; class SampleController extends AbstractController { /** * @var ProductRepository */ protected $productRepository; /** * ProductController constructor. * * @param ProductRepository $productRepository */ public function __construct( ProductRepository $productRepository ) { $this->productRepository = $productRepository; } /** * @Route("sample", name="sample") * @Template("@SamplePage/default/index.twig") */ public function index() { $Products = $this->productRepository->findAll(); return [ 'Products' => $Products, ]; } }
html\app\Plugin\SamplePage\Resource\template\default\index.twig
{% extends 'default_frame.twig' %} {% block main %} <p>サンプルページです</p> <ul> {% for Product in Products %} <li> {{ Product.id }} / {{ Product.name }} / {{ Product.create_date|date('Y-m-d H:i:s') }} </li> {% endfor %} </ul> {% endblock %}
■プラグインで作成したページに独自に追加したテーブルの内容を表示 html\app\Plugin\SamplePage\Controller\SampleController.php
<?php namespace Plugin\SamplePage\Controller; use Eccube\Controller\AbstractController; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\Routing\Annotation\Route; use Customize\Repository\ContactRepository; class SampleController extends AbstractController { /** * @var ContactRepository */ protected $contactRepository; /** * ContactController constructor. * * @param ContactRepository $contactRepository */ public function __construct( ContactRepository $contactRepository ) { $this->contactRepository = $contactRepository; } /** * @Route("sample", name="sample") * @Template("@SamplePage/default/index.twig") */ public function index() { $Contacts = $this->contactRepository->findAll(); return [ 'Contacts' => $Contacts, ]; } }
html\app\Plugin\SamplePage\Resource\template\default\index.twig
{% extends 'default_frame.twig' %} {% block main %} <p>サンプルページです</p> <ul> {% for Contact in Contacts %} <li> {{ Contact.id }} / {{ Contact.name01 }} {{ Contact.name02 }} / {{ Contact.kana01 }} {{ Contact.kana02 }} / {{ Contact.email }} / {{ Contact.create_date|date('Y-m-d H:i:s') }} </li> {% endfor %} </ul> {% endblock %}
■既存ページに情報を表示 データの表示はEvent経由で行う必要があるみたい 以下が参考になりそう EC-CUBE4系のプラグインを開発してみた - Qiita https://qiita.com/yoshiharu-semachi/items/03817d6dd883b000348f 以下、実際に試したメモ プラグイン一覧 → サンプルページプラグイン → 設定 で「名前」に「テスト」と登録しておく 以下にEntityが作成されていることを確認する app\Plugin\SamplePage\Entity\Config.php 以下にRepositoryが作成されていることを確認する app\Plugin\SamplePage\Repository\ConfigRepository.php 以下にFormが作成されていることを確認する(管理画面のフォームに項目を追加するためのもの?要確認) app\Plugin\SamplePage\Form\Type\Admin\ConfigType.php 以下にControllerが作成されていることを確認する app\Plugin\SamplePage\Controller\Admin\ConfigController.php 以下にEventが作成されていることを確認する app\Plugin\SamplePage\Event.php Event.php の内容を以下のように編集する
<?php namespace Plugin\SamplePage; use Eccube\Event\TemplateEvent; use Plugin\SamplePage\Repository\ConfigRepository; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class Event implements EventSubscriberInterface { /** * @var ConfigRepository */ protected $ConfigRepository; /** * ProductReview constructor. * * @param ConfigRepository $ConfigRepository */ public function __construct(ConfigRepository $ConfigRepository) { $this->ConfigRepository = $ConfigRepository; } /** * 配列のキーはイベント名、値は呼び出すメソッド名です。 * * @return array */ public static function getSubscribedEvents() { return [ 'Product/detail.twig' => 'StockShowTwig', ]; } /** * @param TemplateEvent $event */ public function StockShowTwig(TemplateEvent $event) { $twig = '@SamplePage/default/Product/stock_show.twig'; // addSnippet()関数で指定したテンプレートを<body>タグの下部に追加できます。 $event->addSnippet($twig); $Config = $this->ConfigRepository->get(); $parameters = $event->getParameters(); $parameters['pluginName'] = $Config->getName(); $event->setParameters($parameters); } }
以下のファイルを作成する app\Plugin\SamplePage\Resource\template\default\Product\stock_show.twig
{# Style #} <style type="text/css"> #stock_show_area { padding: 0 0 14px 0; border-bottom: 1px dotted #ccc; } #stock_show_area p { font-size: 18px; } </style> <script> {# JQueryのinsertAfterメソッドを使いProduct/detail.twigの<div class="ec-productRole__tags">の直後に挿入 #} $(function() { $('#stock_show_area').insertAfter($('.ec-productRole__tags')); }); </script> {# 規格あり商品の場合 #} {% if Product.hasProductClass %} {# 規格なし商品の場合 #} {% else %} {% if Product.getStockMin > 0 and Product.getStockMin <= 100 %} <div id="stock_show_area"> <p>残り {{ Product.getStockMin }} 点です。ご注文はお早めに! by {{ pluginName }}</p> </div> {% endif %} {% endif %}
これで完成。商品在庫数が100個以下なら、商品詳細ページに「ご注文はお早めに!!」のメッセージが表示される 表示されなければキャッシュを削除する ※引き続き、登録項目の追加を試したい 「残り○点です」の基準値を管理画面から登録できるようにして、それをもとに画面に表示できるようにしたい ■その他 未検証だが、以下も参考になりそう EC-CUBE 4 でお問い合わせプラグインを作ってみた | freks blog https://blog.freks.jp/eccube4-plugin/ EC-CUBE4 独自プラグイン開発 ?独自プラグイン開発Tips - Qiita https://qiita.com/haruna-nagayoshi/items/27108c75eaf9511f3524
■テスト
※未検証 ディレクトリ・ファイル構成 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/spec_directory-structure tests にテストを置くようになっているが、それ以上の情報はすぐに見つけられず Symfonyの解説を探すべきか もとからかなりの数のテストが書かれているみたい 以下に色々なテストがある tests\Eccube\Tests 以下にHTTPリクエストのテストがある tests\Eccube\Tests\Web 以下にテスト用ダミーデータ生成用のファイルがある? tests\Fixtures 以下はテスト用ダミーデータの生成について EC-CUBE4 会員・商品・注文のダミーデータを作る方法 | seiyaan teck blog https://seiyaan.com/2019/09/ec-cube4-dummy-data/ EC-CUBE4 でテスト用のデータを生成する - Qiita https://qiita.com/nanasess/items/f43adf45c88e0321f0a5 コマンドラインインターフェイス - < for EC-CUBE 4.0 Developers /> https://kiy0taka.github.io/doc4.ec-cube.net/quickstart_cli
■本番環境(AWS想定)での稼働
意外に簡単!?最新EC-CUBEのAWSインストール方法 【ネットショップ構築最前線】 https://regolith.diezon.co.jp/ec11_aws.detail.html EC-CUBE4の立ち上げ〜AWSにデプロイするまで - ひろこま Hack Log https://www.mahirokazuko.com/entry/2020/01/03/235548 ■nginx ※未検証 公式にはApacheしか記載は無いが、動かないわけでは無いみたい [備忘録] EC-CUBE4をnginx上で動かす | planet-green.com https://planet-green.com/eccube4-on-nginx/3235 ■複数台構成の考察 rsyncで双方向同期…なら大丈夫そうだが、 きちんと「画像はS3に置いて」は難しそう EC-CUBEによるECサイトの負荷対策 https://www.slideshare.net/kazunoriinaba/20171010jawsugeccube 「負荷対策(1)」で紹介されているが、S3を使うとしても ・管理用サーバを1台とし、管理画面へはそこへ直接アクセス ・uploadやuser_dataなど、同期が必要なものは管理用サーバからS3に転送。公開用サーバはS3から取得 となる?つまり、S3に対応させる場合でも「ECCubeが直接S3を扱うように」は不可能? 【JAWS-UG関西女子会】#2 ELB+EC2+RDSでECサイトをつくろう 〜EC-CUBEコンテンツ同期編〜 - Qiita https://qiita.com/naotinn74/items/83ccefd08bbea796f359 双方向同期の情報はある EFSは今も激重か?使用実績は無い? 以下は最近の情報みたい Amazon Elastic File System(EFS)を使ううえでの勘所 - Qiita https://qiita.com/taichimachima/items/f7d47b68aac2a11ea7cc 色々と注意があるようなので、いきなりの実戦投入はリスクが高そう EC-CUBE とクラウドは仲良しか? https://www.slideshare.net/nanasess/aw-svs-azure ECCubeをBeanstalkで使う場合について触れられているが、ECCubeがスケーリングには対応してないとある EC-CUBE4のアーキテクチャとこれから https://www.ec-cube.net/user_data/press/R1-3_eccube.pdf いくらか参考になるかもしれない 又聞きだが、ECCube公式に確認して「ECCube自体がスケーリングを想定していないので、正式な対応手順があるわけでは無い」と回答されたとのこと 「不可能ではないが色々と工夫が必要」という感じ 「管理画面用サーバの内容をS3に自動転送して、ユーザ画面用サーバで自動取得する」が一応のベストかもしれない が、プログラム側では複数台構成を意識しなくていい「rsyncで同期方式」が無難か ■複数台構成の実践 ※詳細は要検証 基本的には、rsyncでの双方向同期で対応できそう ただしrsyncで双方向同期する際、var の内容を同期すると正しく動作しなかった いったん lsyncd.conf にて var のみ双方向同期の対象から外して対応した var 内にはエラーログなども格納されるので、ここはむしろ同期すべきでは無いかも
exclude = { "*.swp", "/html/var" },
また html/user_data/ 内にディレクトリを作成すると、グループのパーミッションが除外される(ECCubeがそのように処理しているみたい) FileController.php の181行目あたりにある $fs->mkdir($nowDir.'/'.$filename); の直後に chmod($nowDir.'/'.$filename, 0777); を追加すれば何とかなるかもしれない ただし影響範囲が読みづらいので、rsyncにsudoを許可して所有者も含めて双方向同期する方が無難そう 具体的には lsyncd.conf にて以下のように設定した
rsync = { archive = true, compress = false, owner = true, … パーミッションを保持した状態で同期 group = true, … パーミッションを保持した状態で同期 rsync_path = "sudo /usr/bin/rsync", … 管理者権限で rsync を実行 rsh = "ssh -p 10022" }
この設定を行うには、あらかじめ rsync ユーザに /usr/bin/rsync のsudo許可をしておく必要がある
# visudo -f /etc/sudoers.d/users
Cmnd_Alias RSYNCCOMMANDS = /usr/bin/rsync rsync ALL=PASSWD: ALL, NOPASSWD: RSYNCCOMMANDS
追加後、下記コマンドで追加されているか確認できる
# su -s /bin/bash - rsync … rsyncユーザへ切り替え # cat /etc/sudoers.d/users … usersファイルの所有者はrootの為、権限エラー # sudo cat /etc/sudoers.d/users … sudoに追加されていても、許可されたコマンドではないためパスワードが要求される # sudo rsync --version … sudoに追加されており、且つ許可されたコマンドなのでパスワードが要求されず実行できる
■設定ファイル インストール方法 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/quickstart_install 「インストール完了後、インストールディレクトリにデータベースの接続情報等が設定された .env ファイルが生成されます。 .env ファイルは、開発用途での環境変数を設定するためのものであり、本番環境での使用は推奨されません。 本番環境では、環境変数をサーバ設定ファイルに設定することを推奨します。 サーバ設定ファイルに環境変数を設定することにより、環境変数が外部に暴露される危険性が減り、安全に運用できます。」 自動作成された .env は削除したうえで、httpd.conf や .htaccess で設定することが推奨される 例えばインストール完了後に /var/www/main/html/.env
APP_ENV=prod APP_DEBUG=0 DATABASE_URL=mysql://webmaster:1234@localhost/main DATABASE_SERVER_VERSION=5.5.64-MariaDB MAILER_URL=smtp://smtp.gmail.com:465?username=example@gmail.com&password=xxxxxxxxxxxxxxxx&encryption=ssl&auth_mode=login ECCUBE_AUTH_MAGIC=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ECCUBE_ADMIN_ALLOW_HOSTS=[] ECCUBE_FORCE_SSL=false ECCUBE_ADMIN_ROUTE=system ECCUBE_COOKIE_PATH=/ ECCUBE_TEMPLATE_CODE=default ECCUBE_LOCALE=ja
と設定されている場合、以下のようにする /var/www/main/.htaccess
SetEnv APP_ENV prod SetEnv APP_DEBUG 0 SetEnv DATABASE_URL mysql://webmaster:1234@localhost/main SetEnv DATABASE_SERVER_VERSION 5.5.64-MariaDB SetEnv MAILER_URL smtp://smtp.gmail.com:465?username=example@gmail.com&password=xxxxxxxxxxxxxxxx&encryption=ssl&auth_mode=login SetEnv ECCUBE_AUTH_MAGIC xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SetEnv ECCUBE_ADMIN_ALLOW_HOSTS [] SetEnv ECCUBE_FORCE_SSL false SetEnv ECCUBE_ADMIN_ROUTE system SetEnv ECCUBE_COOKIE_PATH / SetEnv ECCUBE_TEMPLATE_CODE default SetEnv ECCUBE_LOCALE ja
ブラウザでアクセスして、.env があるときと同じように表示されるか確認する インストール画面が表示される場合、設定内容が認識されていないと思われる /var/www/main 内で .htaccess の利用を許可されているかなどを確認する ただしこの設定をした場合、以下の機能を管理画面から設定できなくなる ・管理画面 → オーナーズストア → テンプレート ・管理画面 → 設定 → システム設定 → セキュリティ管理 また、「セキュリティ管理」の画面に 「 .envファイルが見つかりません。.envを利用していない場合はセキュリティ設定を管理画面から変更できません。」 と表示されるようになる この設定をした場合、マイグレーションなど bin/console を使う機能が正しく動作するか確認する (「マイグレーションがエラーになるようになった」と報告をもらったことがあるが、勘違いなのかどうかは不明) ただしそもそも、上記のように「管理画面から設定できなくなる」が発生するのは後々ややこしそう よって ・本番環境では .env の場所を一階層上げる(開発環境ではそのままでもいい) ・index.php で「../../.env」があればそれを読み込むという分岐を設ける ・bin/console で「../../.env」があればそれを読み込むという分岐を設ける ・src/Eccube/Controller/Admin/Setting/System/SecurityController.php をもとに app/Customize/Controller/Admin/Setting/System/SecurityController.php を作成し、「../../.env」があればそれを読み書きするという分岐を設ける という対応の方が良さそう 実際の案件で試して、現状特に問題なく動作している phpdotenvで環境変数を制御する - demolog http://skitn.hateblo.jp/entry/2018/04/22/212210 phpdotenvについて。 - Qiita https://qiita.com/H40831/items/aaf8ffa727928661ad1d ■SSLの強制 管理画面 → 設定 → 店舗設定 → セキュリティ管理 に「SSLを強制」という項目があり、通常はこれにチェックを入れるだけで対応が可能なはず ただし「httpsからの接続でなければSSL制限を設定できません。」と補足が書かれていて、AWSのロードバランサー経由で確認するとチェックができない ロードバランサーからEC2へのアクセスはSSLでは無いため、恐らく自動判別に失敗しているのだと思われる 以下で対応はできそうだが、プログラムを編集する必要はある ただし上の「設定ファイル」でも index.php と bin/console を編集する案があるので、素直に「本番環境ならSSLを強制」のコードをプログラム内に書くか 【EC-CUBE4系】Certificate ManagerでHTTPSを導入 | クマひよ工房 https://kumahiyo.com/certificate-manage/ EC-CUBE4カスタマイズ - ロードバランサを使用するとHTTPS・SSLが認識されない場合の対応 https://umebius.com/eccube/behind-load-balancer-ssl/
■バージョンアップ
■本体のバージョンアップ EC-CUBE本体のバージョンアップ - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/update アップデートのための手順やプラグインは用意されるようだが、なかなかリスクは大きそう また、ECCubeをカスタマイズしている場合はさらに厄介そう Git を使って EC-CUBE を簡単アップデート - Qiita https://qiita.com/nanasess/items/fe2a93ff64833d87eb19 Gitを使って対応する方法が紹介されているが、 これですんなりアップデートできるかは実際にやってみないと判らない ■プラグインのバージョンアップ プラグイン仕様 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/plugin_spec アップデート手段が用意されていそうだが不明 アンインストールしてから再度インストールすればアップデート可能だが、データが消えるので本番運用中は使えない アップデート対象があると、管理画面の「プラグイン一覧」に「アップデート」というボタンが表示された 対象がなければ「アップデート対象プラグインはありません。」と表示されている この「アップデート」ボタンを押すことでアップデートできるとは思うが、データが消えるかどうかなどは要確認 また、プラグインを改造している場合はこのボタンを押すと問題があるので、「アップデート」の列を表示しないなど対策が必要 改造していなくても、うっかりアップデートされて問題が起きないようにする必要はあるかもしれない
■脆弱性
ときどき報告されているので、以下のページなどで確認する 脆弱性リスト | ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/info/weakness/ 最近挙がった大きな問題だと、以下のXSS問題がある EC-CUBE4.0におけるクロスサイトスクリプティングの脆弱性(JVN#97554111) https://www.ec-cube.net/info/weakness/weakness.php?id=77 EC-CUBE4.0におけるクロスサイトスクリプティングの脆弱性(JVN#95292458) https://www.ec-cube.net/info/weakness/weakness.php?id=78 EC-CUBE 4.0系: クロスサイトスクリプティング脆弱性 (JVN#97554111) について https://www.ec-cube.net/info/weakness/20210507/ EC-CUBE4.0におけるクロスサイトスクリプティングの脆弱性(JVN#95292458) https://www.ec-cube.net/info/weakness/20210610/index40.php この脆弱性により、以下のような手順で攻撃を受ける可能性がある ECサイトのクロスサイトスクリプティング脆弱性を悪用した攻撃 - JPCERT/CC Eyes | JPCERTコーディネーションセンター公式ブログ https://blogs.jpcert.or.jp/ja/2021/07/water_pamola.html
■トラブル
■プログラムに異常は見当たらないのにエラーになる 管理画面の「コンテンツ管理 → キャッシュ管理」からキャッシュを削除する 以下のコマンドで削除してもいい(むしろ、コマンドで削除する方が余計なデータが残らなくていいかも)
$ php bin/console cache:clear --no-warmup
■データベースに接続できたりできなかったりする データベースへの接続パスワードに「$」が含まれていたときの挙動が怪しい .env でパスワードをシングルクォートで囲えばいいかと思ったが、それでも接続エラーになることがある Symfonyの仕様かもしれないが詳細は不明 ひとまず、データベースへの接続パスワードを変更して対応した ■正しいログイン情報のはずなのに認証できない 別環境のデータベースを取り込んだ時に発生する .env の ECCUBE_AUTH_MAGIC が暗号化のキーとして使われているので、この値を合わせないと正しく複合化できない よってこの値を、取り込み元と同じ値にしておく必要がある EC-CUBE4データ移行:会員データ・管理者データ(MySQL) | ITOBEN STYLE Blog https://itoben.com/blog/4277.html ■Twigファイルで日時を参照できない 日時を出力しようとすると致命的なエラーになることがある エラー内容はECCube設置ディレクトリ内の var/log/prod/site-2021-01-19.log などに出力されるので、確認すると 「Fatal Error: Object of class DateTime could not be converted to strin」 のようなエラーになっている 日付の値は、文字列型に自動変換されないとのこと 一例だが、以下のようにformatの指定を追加すると表示できるようになる 必要に応じて、Entity内でformatの指定を行っておくこともできる
Product.getProductCst().getTestDate() ↓ Product.getProductCst().getTestDate().format('Y-m-d')
【PHP】DateTime型の値を文字列(String)にする方法 - AppSeedのアプリ開発ブログ https://develop.hateblo.jp/entry/php-datetime-string ■.env の値に「#」を使用できない Symfony3.txt の「トラブル」を参照 上記「データベースに接続できたりできなかったりする」も似たような問題だが、これはECCube独自の問題かもしれない ■受注一覧からCSVをダウンロードできない ECCubeの不具合。最新版では解消されているらしい 商品・受注・顧客などのCSVダウンロードでメモリ不足エラー。 - Issue #4775 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/issues/4775 CSV出力時にメモリを使い切ってしまう問題を修正 by chihiro-adachi - Pull Request #4815 - EC-CUBE/ec-cube https://github.com/EC-CUBE/ec-cube/pull/4815 最終的に、以下の内容が本体に取り込まれているみたい。 ページングして取得するように修正 - EC-CUBE/ec-cube@0da7c07 https://github.com/EC-CUBE/ec-cube/commit/0da7c07755505ce96eaa2e279ec550dd84165c4f
■メモ
■参考:導入の際にECCubeの初期設定も同時に行う場合 create-project でプロジェクト作成時、「--no-scripts」を省略すると初期設定が自動で行われる この場合、完了後以下にアクセスするとインストール画面ではなく、CCubeのユーザ向け画面が表示される セットアップは終わった状態となる http://eccube4.local/ 以下で管理画面にもログインできる ただしログインすると「管理画面URLは、セキュリティのため推測されにくいものを設定してください。」と警告が表示されている http://eccube4.local/admin/ admin / password また、デフォルトではデータベースはSQLiteとなる 動作もなかなか重い
$ php composer.phar create-project ec-cube/ec-cube html "4.0.x-dev" --keep-vcs Cannot create cache directory /usr/share/httpd/.cache/composer/repo/https---repo.packagist.org/, or directory is not writable. Proceeding without cache Creating a "ec-cube/ec-cube" project at "./html" Installing ec-cube/ec-cube (4.0.x-dev 040a47089de8942731d0ba0e7c5328b420c46c6d) Cannot create cache directory /usr/share/httpd/.cache/composer/files/, or directory is not writable. Proceeding without cache - Installing ec-cube/ec-cube (4.0.x-dev 040a470): Cloning 040a47089d Created project in /var/www/main/html Cannot create cache directory /usr/share/httpd/.cache/composer/repo/https---repo.packagist.org/, or directory is not writable. Proceeding without cache Cannot create cache directory /usr/share/httpd/.cache/composer/files/, or directory is not writable. Proceeding without cache Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 164 installs, 0 updates, 0 removals - Installing ocramius/package-versions (1.4.2): Downloading (100%) - Installing ec-cube/plugin-installer (0.0.8): Downloading (100%) 〜中略〜 - Installing php-coveralls/php-coveralls (v2.2.0): Downloading (100%) - Installing symfony/phpunit-bridge (v3.4.42): Downloading (100%) Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead. Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead. Package sensio/generator-bundle is abandoned, you should avoid using it. Use symfony/maker-bundle instead. Package setasign/fpdi-tcpdf is abandoned, you should avoid using it. No replacement was suggested. Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead. Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested. Package easycorp/easy-log-handler is abandoned, you should avoid using it. No replacement was suggested. Generating optimized autoload files Deprecation Notice: Class Plugin\EntityExtension\Entity\CustomerSortNoTrait located in ./app/Plugin/EntityExtension/Entity/CustomerRankTrait.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///var/www/main/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:201 Carbon 1 is deprecated, see how to migrate to Carbon 2. https://carbon.nesbot.com/docs/#api-carbon-2 You can run './vendor/bin/upgrade-carbon' to get help in updating carbon and other frameworks and libraries that depend on it. 55 packages you are using are looking for funding. Use the `composer fund` command to find out more! ocramius/package-versions: Generating version class... ocramius/package-versions: ...done generating version class Executing script cache:clear --no-warmup [OK] Executing script cache:warmup --no-optional-warmers [OK] Executing script assets:install --symlink --relative html [OK] > bin/console doctrine:database:create Created database var/eccube.db for connection named default > bin/console doctrine:schema:create ! [CAUTION] This operation should not be executed in a production environment! Creating database schema... [OK] Database schema created successfully! > bin/console eccube:fixtures:load > Finished Successful! Executing script cache:clear --no-warmup [OK] Executing script cache:warmup --no-optional-warmers [OK] Executing script assets:install --symlink --relative html [OK]
■仮登録と本登録 会員登録をすると、いったん仮登録状態になる 確認の旨のメールが送信され、メール内のURLをクリックすると本会員登録される 本会員登録されると、完了の旨のメールが送信される 仮登録の状態だと、ログインしようとしても 「ログインできませんでした。入力内容に誤りがないかご確認ください。」 のエラーになる ■CSSファイルの編集 ※エラーで進められず GulpでSassをビルドする必要があるみたい まずは以下のようにNode.jsをインストールする
$ curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash - $ sudo yum install -y nodejs $ node --version v12.16.3 $ npm --version 6.14.4
npmコマンドでインストール
$ cd /var/www/main/html $ npm install npm ERR! code EPROTO npm ERR! syscall symlink npm ERR! path ../acorn/bin/acorn npm ERR! dest /var/www/main/html/node_modules/@gulp-sourcemaps/identity-map/node_modules/.bin/acorn npm ERR! errno -71 npm ERR! EPROTO: protocol error, symlink '../acorn/bin/acorn' -> '/var/www/main/html/node_modules/@gulp-sourcemaps/identity-map/node_modules/.bin/acorn' npm ERR! A complete log of this run can be found in: npm ERR! /home/vagrant/.npm/_logs/2020-05-21T06_27_22_323Z-debug.log
エラーになる symlinkが使えない場合、「--no-bin-link」のオプションを付けるといいらしい
$ npm install --no-bin-link npm WARN bootstrap@4.1.3 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself. npm WARN bootstrap@4.1.3 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself. added 7 packages from 115 contributors and audited 735 packages in 7.588s found 131 vulnerabilities (76 low, 8 moderate, 47 high) run `npm audit fix` to fix them, or `npm audit` for details $ npm run build > eccube@4.0.0 build /var/www/main/html > run-s build:moc sh: run-s: コマンドが見つかりません npm ERR! code ELIFECYCLE npm ERR! syscall spawn npm ERR! file sh npm ERR! errno ENOENT npm ERR! eccube@4.0.0 build: `run-s build:moc` npm ERR! spawn ENOENT npm ERR! npm ERR! Failed at the eccube@4.0.0 build script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /home/vagrant/.npm/_logs/2020-05-21T06_30_02_632Z-debug.log
エラーになる node_modules を削除して、始めからやりなおすといいらしい Laravel-Mixコンパイル時のcode ELIFECYCLEエラーに対応する - Qiita https://qiita.com/ishizukih/items/9673e709832dacaa5155
$ rm -rf node_modules $ rm package-lock.json $ npm cache clear --force $ npm install --no-bin-link $ npm run build > eccube@4.0.0 build /var/www/main/html > run-s build:moc sh: run-s: コマンドが見つかりません npm ERR! code ELIFECYCLE npm ERR! syscall spawn npm ERR! file sh npm ERR! errno ENOENT npm ERR! eccube@4.0.0 build: `run-s build:moc` npm ERR! spawn ENOENT npm ERR! npm ERR! Failed at the eccube@4.0.0 build script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /home/vagrant/.npm/_logs/2020-05-21T06_37_31_405Z-debug.log
相変わらず同じエラーになる /home/vagrant/.npm/_logs/2020-05-21T06_37_31_405Z-debug.log の内容は以下のとおり
0 info it worked if it ends with ok 1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'run', 'build' ] 2 info using npm@6.14.4 3 info using node@v12.16.3 4 verbose run-script [ 'prebuild', 'build', 'postbuild' ] 5 info lifecycle eccube@4.0.0~prebuild: eccube@4.0.0 6 info lifecycle eccube@4.0.0~build: eccube@4.0.0 7 verbose lifecycle eccube@4.0.0~build: unsafe-perm in lifecycle true 8 verbose lifecycle eccube@4.0.0~build: PATH: /usr/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/var/www/main/html/node_modules/.bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/puppetlabs/bin:/home/vagrant/.local/bin:/home/vagrant/bin 9 verbose lifecycle eccube@4.0.0~build: CWD: /var/www/main/html 10 silly lifecycle eccube@4.0.0~build: Args: [ '-c', 'run-s build:moc' ] 11 info lifecycle eccube@4.0.0~build: Failed to exec build script 12 verbose stack Error: eccube@4.0.0 build: `run-s build:moc` 12 verbose stack spawn ENOENT 12 verbose stack at ChildProcess.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:48:18) 12 verbose stack at ChildProcess.emit (events.js:310:20) 12 verbose stack at maybeClose (internal/child_process.js:1021:16) 12 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5) 13 verbose pkgid eccube@4.0.0 14 verbose cwd /var/www/main/html 15 verbose Linux 3.10.0-1127.el7.x86_64 16 verbose argv "/usr/bin/node" "/usr/bin/npm" "run" "build" 17 verbose node v12.16.3 18 verbose npm v6.14.4 19 error code ELIFECYCLE 20 error syscall spawn 21 error file sh 22 error errno ENOENT 23 error eccube@4.0.0 build: `run-s build:moc` 23 error spawn ENOENT 24 error Failed at the eccube@4.0.0 build script. 24 error This is probably not a problem with npm. There is likely additional logging output above. 25 verbose exit [ 1, true ]
以下はEC2で作業したときのメモだが、やはりエラーで進められず ec2-user で以下を実行
$ curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash - $ sudo yum install -y nodejs $ node --version v12.17.0 $ npm --version 6.14.4
apache で以下を実行
$ node --version v12.17.0 $ npm --version 6.14.4
npmコマンドでインストール
$ cd /var/www/main/html $ npm install npm ERR! correctMkdir failed to make directory /usr/share/httpd/.npm/_locks npm ERR! code EACCES npm ERR! syscall mkdir npm ERR! path /usr/share/httpd/.npm npm ERR! errno -13 npm ERR! npm ERR! Your cache folder contains root-owned files, due to a bug in npm ERR! previous versions of npm which has since been addressed. npm ERR! npm ERR! To permanently fix this problem, please run: npm ERR! sudo chown -R 48:48 "/usr/share/httpd/.npm"
エラーになったので、ディレクトリを作成して所有者を調整
# mkdir /usr/share/httpd/.npm # chown -R apache. /usr/share/httpd/.npm $ npm install gyp ERR! build error gyp ERR! stack Error: `make` failed with exit code: 2 gyp ERR! stack at ChildProcess.onExit (/var/www/main/html/node_modules/node-gyp/lib/build.js:262:23) gyp ERR! stack at ChildProcess.emit (events.js:315:20) gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12) gyp ERR! System Linux 4.14.173-137.229.amzn2.x86_64 gyp ERR! command "/usr/bin/node" "/var/www/main/html/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library=" gyp ERR! cwd /var/www/main/html/node_modules/node-sass gyp ERR! node -v v12.17.0 gyp ERR! node-gyp -v v3.8.0 gyp ERR! not ok Build failed with error code: 1 npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! node-sass@4.9.3 postinstall: `node scripts/build.js` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the node-sass@4.9.3 postinstall script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /usr/share/httpd/.npm/_logs/2020-05-27T06_00_31_503Z-debug.log
エラーになったのでやりなおしてみる
$ rm -rf node_modules $ rm package-lock.json $ npm cache clear --force $ npm install added 726 packages from 548 contributors and audited 726 packages in 25.499s 18 packages are looking for funding run `npm fund` for details found 14 vulnerabilities (2 low, 4 moderate, 8 high) run `npm audit fix` to fix them, or `npm audit` for details ┌──────────────────────────────────────────────────────────────┐ │ npm update check failed │ │ Try running with sudo or get access │ │ to the local update config store via │ │ sudo chown -R $USER:$(id -gn $USER) /usr/share/httpd/.config │ └──────────────────────────────────────────────────────────────┘
インストールできた
$ npm run build npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! eccube@4.0.0 build:moc:sass: `gulp sass` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the eccube@4.0.0 build:moc:sass script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /usr/share/httpd/.npm/_logs/2020-05-27T06_08_12_615Z-debug.log ERROR: "build:moc:sass" exited with 1. npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! eccube@4.0.0 build:moc: `run-s build:moc:*` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the eccube@4.0.0 build:moc script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /usr/share/httpd/.npm/_logs/2020-05-27T06_08_12_650Z-debug.log ERROR: "build:moc" exited with 1. npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! eccube@4.0.0 build: `run-s build:moc` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the eccube@4.0.0 build script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /usr/share/httpd/.npm/_logs/2020-05-27T06_08_12_685Z-debug.log
ビルドしようとするとエラーになる EC-CUBE4初心者のためのテンプレートカスタマイズ方法 | ホムペディア https://homupedia.com/eccube4-template-customize.html ■ECCubeカートに追加ボタン 以下に情報はあるが古いみたい EC-CUBE と WordPress を連携させるためのベストプラクティス - Qiita https://qiita.com/nanasess/items/9260f0de30332d65db85 以下のコードで対応できるみたい …と思ったが、CSRFトークン対策が必要 SC_Helper_Session_Ex::getToken() で取れる?昔のバージョンの話? いずれにせよ、無理矢理感は否めない
<script> $(function() { $('.add-cart').on('click', function(event) { // 個数フォームのチェック if ($('#quantity').val() < 1) { $('#quantity')[0].setCustomValidity('1以上で入力してください。'); return true; } else { $('#quantity')[0].setCustomValidity(''); } event.preventDefault(); $form = $('#form1'); $.ajax({ url: $form.attr('action'), type: $form.attr('method'), data: $form.serialize(), dataType: 'json', beforeSend: function(xhr, settings) { // Buttonを無効にする $('.add-cart').prop('disabled', true); } }).done(function(data) { // レスポンス内のメッセージをalertで表示 $.each(data.messages, function() { $('#ec-modal-header').html(this); }); $('#ec-modal-checkbox').prop('checked', true); // カートブロックを更新する $.ajax({ url: "http://13.231.230.5/block/cart", type: 'GET', dataType: 'html' }).done(function(html) { $('.ec-headerRole__cart').html(html); }); }).fail(function(data) { alert('カートへの追加に失敗しました。'); }).always(function(data) { // Buttonを有効にする $('.add-cart').prop('disabled', false); }); }); }); </script> <form action="/products/add_cart/2" method="post" id="form1" name="form1"> <input type="number" id="quantity" name="quantity" required="required" min="1" maxlength="" class="form-control" value="1" /> <button type="submit" class="ec-blockBtn--action add-cart"> カートに入れる </button> <input type="hidden" id="product_id" name="product_id" value="2" /> <input type="hidden" id="ProductClass" name="ProductClass" value="11" /> <input type="hidden" id="_token" name="_token" value="[CsrfToken]" /> ←このコードでは駄目みたい </form> <div class="ec-modal"> <input type="checkbox" id="ec-modal-checkbox" class="checkbox"> <div class="ec-modal-overlay"> <label for="ec-modal-checkbox" class="ec-modal-overlay-close"></label> <div class="ec-modal-wrap"> <label for="ec-modal-checkbox" class="ec-modal-close"><span class="ec-icon"><img src="/html/template/default/assets/icon/cross-dark.svg" alt=""/></span></label> <div id="ec-modal-header" class="text-center">カートに追加しました。</div> <div class="ec-modal-box"> <div class="ec-role"> <label for="ec-modal-checkbox" class="ec-inlineBtn--cancel">お買い物を続ける</label> <a href="http://13.231.230.5/cart" class="ec-inlineBtn--action">カートへ進む</a> </div> </div> </div> </div> </div>
■ECCubeカートに追加ボタン(その2) 商品一覧ページに限定すれば、app/template/default/Product/list.twig に以下の内容を記述すれば、 商品IDが「3」の商品をカートに入れるボタンを追加できた
{% set product_id = 3 %} {% set form = forms[product_id] %} <form name="form{{ product_id }}" id="productForm{{ product_id }}" action="{{ url('product_add_cart', {id:product_id}) }}" method="post"> <div class="ec-productRole__actions"> {% if form.classcategory_id1 is defined %} <div class="ec-select"> {{ form_widget(form.classcategory_id1) }} {{ form_errors(form.classcategory_id1) }} </div> {% if form.classcategory_id2 is defined %} <div class="ec-select"> {{ form_widget(form.classcategory_id2) }} {{ form_errors(form.classcategory_id2) }} </div> {% endif %} {% endif %} <div class="ec-numberInput"><span>{{ 'common.quantity'|trans }}</span> {{ form_widget(form.quantity, {'attr': {'class': 'quantity'}}) }} {{ form_errors(form.quantity) }} </div> </div> {{ form_rest(form) }} </form> <div class="ec-productRole__btn"> <button type="submit" class="ec-blockBtn--action add-cart" data-cartid="{{ product_id }}" form="productForm{{ product_id }}"> {{ 'front.product.add_cart'|trans }} </button> </div>
ただし動くのはこのページに追加したときのみで、商品詳細や店舗概要ページに入力してもシステムエラーになった どうもコントローラ側のプログラムもいじる必要がありそうだが、詳細は不明 セキュリティを気にしないなら、CSRFを無効にする方法はある ただこの場合でも、商品規格などのオプション項目をシステムと連動させる場合は色々作る必要はある(そもそも、セキュリティ上推奨されない方法だが) EC-CUBE3, EC-CUBE4カスタマイズ - LPや別サイトから商品をカート追加が可能に。CSRFトークンを解除する https://umebius.com/eccube/lp_add_cart_csrf_protextion_false/ 以下、何かのときに参考にできそうなコードをメモ。
<form name="form3" id="productForm3" action="/products/add_cart/3" method="post"> <input type="number" id="quantity3" name="quantity" required="required" min="1" maxlength="9" class="quantity form-control" value="1" /> <input type="hidden" id="product_id3" name="product_id" value="3" /><input type="hidden" id="ProductClass3" name="ProductClass" value="12" /> <input type="submit" value="ID3の商品をカートに入れる"> </form> [ {{ form_widget(forms[3]._token) }} ] [ {{ csrf_token('_token') }} ] [ {{ csrf_token('authenticate') }} ] [ {{ csrf_token(constant('Eccube\\Common\\Constant::TOKEN_NAME')) }} ] [ {{ csrf_token_for_anchor() }} ]
■ECCubeカートに追加ボタン(その3) 以下のようにボタンを設置して、規格なしの商品をカートへ入れることができた(app\template\default\Help\about.twig で確認できた) product_class の値は、dtb_product_classテーブルに組み合わせがある
{% set product_id = 3 %} {% set product_class = 12 %} {% set product_name = 'ブラウニー' %} <form action="/products/add_cart/{{ product_id }}" method="post"> <input type="number" name="quantity" value="1" /> <input type="hidden" name="product_id" value="{{ product_id }}" /> <input type="hidden" name="ProductClass" value="{{ product_class }}" /> <input type="hidden" name="_token" value="{{ csrf_token('Eccube\\Form\\Type\\AddCartType') }}" /> <input type="submit" value="{{ product_name }}をカートに入れる"> </form>
以下のようにボタンを設置して、規格ありの商品をカートへ入れることができた product_class の値は、dtb_product_classテーブルに組み合わせがある(カートに入れる規格によって変化する) 以下は特定の規格の組み合わせを決め打ちでカートに入れることになる
{% set product_id = 6 %} {% set product_class = 21 %} {% set product_name = 'レモン' %} <form action="/products/add_cart/{{ product_id }}" method="post"> <input type="number" name="quantity" value="1" /> <input type="hidden" name="product_id" value="{{ product_id }}" /> <input type="hidden" name="ProductClass" value="{{ product_class }}" /> <input type="hidden" name="_token" value="{{ csrf_token('Eccube\\Form\\Type\\AddCartType') }}" /> <input type="submit" value="{{ product_name }}をカートに入れる"> </form>
プルダウンなどで規格を選択できるようにする場合、 「規格1」の選択内容に応じて「規格2」の選択内容を動的に変更したりが必要なので、それなりに手間取りそう (そもそも、商品に応じた規格の取得自体が未調査) _token で渡す値は以下が参考になった (他のトークンとは別に、カートに入れるとき専用のトークンがあるみたい) (2) Ohtani@web制作さんはTwitterを使っています / Twitter https://twitter.com/Ohtani_6/status/1351901175952539650 一例だが、以下のようなトークンの取得方法があるみたい(詳細は未調査)
{{ csrf_token(constant('Eccube\\Common\\Constant::TOKEN_NAME')) }} {{ csrf_token('_token') }} {{ csrf_token('authenticate') }} {{ csrf_token_for_anchor() }} {{ form._token }} {{ constant('Eccube\\Common\\Constant::TOKEN_NAME') }}
■未検証 ・プラグインでのページ作成とプラグインなしのページ作成を試す。データベース連動も含めて ・プラグインの導入を試す 「おすすめ商品管理プラグイン」は機能的に分かりやすそうだし、公式プラグインなので丁度いいかも ・バリデーションの挙動を確認する ・テンプレート内の form_widget や url は独自に定義されている? ・リポジトリは作らなくてもいい? ・Laravelのように、コンストラクタでサービスを受け取っている? EC-CUBE4 デザインカスタマイズ(あっさり解説) - Qiita https://qiita.com/kakuta_yu/items/d36f2e746b59f5db7557 EC-CUBE4 アーカイブ - U-Mebius https://umebius.com/eccube/category/ec-cube4/ EC-CUBE4カスタマイズ - [4系] 新規会員登録画面に項目を追加する方法 https://umebius.com/eccube/eccube4-add-new-form-item-customer/

Advertisement