■目次
ECCube4ダウンロード導入導入: Vagrant導入: Docker + 公式サイト版導入: XAMPP + 公式サイト版導入: EC2 + GitHub版導入: EC2 + 公式サイト版インストールインストール後の設定運用のためのメモメールの送信先カスタマイズカスタマイズ: 対応ステータスの調整カスタマイズ: ページの作成カスタマイズ: マイグレーションでのデータ管理カスタマイズ: 会員情報に項目を追加カスタマイズ: 商品情報に項目を追加カスタマイズ: ご注文手続きに項目を追加カスタマイズ: テーブルを作成カスタマイズ: テーブルに項目を追加カスタマイズ: 独自に追加したテーブルからデータを取得カスタマイズ: お問い合わせフォームの複製カスタマイズ: 新着情報管理をもとに独自の記事管理を作成プラグインの導入プラグインの導入: プラグインを導入済みのECCubeをセットアップする場合プラグインの導入: プラグインを追加削除したときの挙動を検証プラグインの導入: ECCube VeriTrans4G決済プラグイン(4.0系)プラグインの作成テスト本番環境(AWS想定)での稼働トラブルメモ
■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 さくらのレンタルサーバにてEC-CUBE4を使って格安でオンラインショップを作る(第一回) | さくらのナレッジ https://knowledge.sakura.ad.jp/25102/ ■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/ 各テーブルには以下の接頭語が付いている 自分でテーブルを追加する場合も、このルールに基づいておくと良さそう mtb_ ... マスタデータ(例: mtb_pref) dtb_ ... データテーブル(例: dtb_order_detail) plg_ ... プラグイン(例: plg_vt4g_order_payment) ■オーナーズストア ECCube自体は無料だが、プラグインは有料のものが多い ECCubeの導入は結局のところ、結構なお金がかかるかもしれない オーナーズストア | ECサイト構築・リニューアルは「ECオープンプラットフォームEC-CUBE」 https://www.ec-cube.net/owners/
■ダウンロード
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でのファイル同期の速度が問題になっているのかもしれない
■導入: Vagrant
■環境構築 ※eccube のPlaybookで環境を作成 ■ECCubeの初期設定 以下にアクセス http://eccube4.local/
■導入: Docker + 公式サイト版
※公式のDockerfileで起動できなかったので、独自に作成した docker-compose で起動 以前に作成したPHP7環境で起動してみる ※動作が非常に重く、何の工夫もなく使うのは現実的ではない Vagrantで起動する方がマシだが、同期対象を絞るなどで改善の余地はあるみたい ■起動 $ cd /c/Users/refirio/docker/eccube4/docker $ docker-compose build $ docker-compose up -d $ docker-compose down システム要件 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/quickstart_requirement ■ECCubeの初期設定 起動後、html 内にECCube4のファイルを丸ごと配置した http://eccube4.local/ にアクセスすると、しばらく待ったあと以下が表示された
Warning: SessionHandler::read(): Session data file is not created by your uid
セッションの保存場所が特殊みたいなので、一般的なものにしてみる C:\Users\refirio\docker\eccube4\html\app\config\eccube\packages\framework.yaml
12:- save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%' 12:+ save_path: '/tmp/%kernel.environment%'
インストール画面が表示されたが、以下が表示されている
[必須] zip拡張モジュールが有効になっていません。 システム要件をご確認ください [必須] intl拡張モジュールが有効になっていません。 システム要件をご確認ください [推奨] apc拡張モジュールが有効になっていません。
C:\Users\refirio\docker\eccube4\docker\php\Dockerfile
3:+ libicu-dev \ 4:+ libzip-dev \ 9:- && docker-php-ext-install gd pdo_mysql mysqli mbstring \ 9:+ && docker-php-ext-install gd pdo_mysql mysqli mbstring intl zip \
これで「必須」が消えた 「推奨」は表示されたままだが、「apc拡張モジュール」はPHP5の機能みたいなので無視して良さそう 【EC-CUBE on Docker】「[必須]intl拡張モジュールが有効になっていません」の原因と解決方法 | まいにち1UP!! https://tetrablog.net/eccube-on-docker-no-intl dockerfileでPHPのzip拡張を導入するメモ - Qiita https://qiita.com/Y-Kanoh/items/9d450a6868183557042f Docker php:7.3-fpm で zip モジュールを使えるようにしようとして発生したエラーと解決方法 - oki2a24 https://oki2a24.com/2019/03/20/solve-build-error-to-use-php-zip-module-in-docker-php-7-3-fpm/ ■公式手順でインストールできなかったメモ インストール方法 - < for EC-CUBE 4.0 Developers /> https://doc4.ec-cube.net/quickstart_install#docker%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%8... 手順通りに実行 $ cd /c/Users/refirio/docker/eccube4 $ docker build -t eccube4-php-apache . The following packages have unmet dependencies: libicu-dev : Depends: libicu57 (= 57.1-6+deb9u3) but 57.1-6+deb9u4 is to be installed E: Unable to correct problems, you have held broken packages. The command '/bin/sh -c apt-get update && apt-get install -y curl apt-utils apt-transport-https debconf-utils gcc build-essential zlib1g-dev git gnupg2 unzip libfreetype6-dev libjpeg62-turbo-dev libpng-dev libzip-dev libicu-dev vim git ssl-cert && docker-php-ext-install -j$(nproc) zip gd mysqli pdo_mysql opcache intl && rm -rf /var/lib/apt/lists/*' returned a non-zero code: 100 libicu57に問題があるらしい C:\Users\refirio\docker\eccube4\Dockerfile
13:- libpng-dev libzip-dev libicu-dev vim git ssl-cert \ 13:+ libpng-dev libzip-dev vim git ssl-cert \
試しに省いてみる configure: error: Unable to detect ICU prefix or no failed. Please verify ICU install prefix and make sure icu-config works. checking for location of ICU headers and libraries... checking for pkg-config... /usr/bin/pkg-config checking for icu-config... no not found The command '/bin/sh -c apt-get update && apt-get install -y curl apt-utils apt-transport-https debconf-utils gcc build-essential zlib1g-dev git gnupg2 unzip libfreetype6-dev libjpeg62-turbo-dev libpng-dev libzip-dev vim git ssl-cert && docker-php-ext-install -j$(nproc) zip gd mysqli pdo_mysql opcache intl && rm -rf /var/lib/apt/lists/*' returned a non-zero code: 1 ICUが無いと言われる $ apt-get install libicu-dev $ sudo yum install libicu-devel これらでインストールできず
■導入: 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
■インストール
※以降特に断りがなければ、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/
■インストール後の設定
■メール送信 メールを送信する場合は以下を編集する 以下はGmailのSMTPを使う例 .env
25:- MAILER_URL=smtp://localhost:25 25:+ MAILER_URL=smtp://smtp.gmail.com:465?encryption=ssl&auth_mode=login&username=example@gmail.com&password=XXXXXXXXXXXXXXXX
■コマンドラインインターフェイス コマンドラインインターフェイス - < 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 ただしこの設定にすると、キャッシュされない代わりに動作が重くなるので注意 ■リポジトリへの登録 初回構築時なら、これくらいのタイミングでいったんリポジトリにコミット&プッシュしておくといい ■店舗設定 管理画面 → 設定 → 店舗設定 → 基本設定 最低限、4つある各メールアドレスを設定しておく 注文メールやお問い合わせメールは「問い合わせ受付メールアドレス」に対してBccで送信される ■マイグレーション SSHで接続して以下を実行する (実行が必要なマイグレーションがあれば実行される) $ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console doctrine:migrations:migrate ■キャッシュクリア SSHで接続して以下を実行する 管理画面からよりも確実みたい $ sudo su -s /bin/bash - apache $ cd /var/www/main/html $ php bin/console cache:clear --no-warmup
■運用のためのメモ
■受注一覧 「お問い合わせ番号」を入力すると、送信されるメールに記載される 自動で何かと連動するのではなく、単に番号を表示するだけみたい? ■基本設定 「設定 → 店舗設定 → 基本設定」 店名は、titleタグの内容などに使われる 会社名、住所、電話番号、店舗営業時間、取り扱い商品説明文、店舗からのメッセージは、 ユーザ側のフッタにある「当サイトについて」のリンク先ページに表示される ■CSS管理 「コンテンツ管理 → CSS管理」 コードを登録すると以下のファイルに反映され、ユーザ側で読み込まれる user_data\assets\css\customize.css ■JavaScript管理 「コンテンツ管理 → JavaScript管理」 コードを登録すると以下のファイルに反映され、ユーザ側で読み込まれる user_data\assets\js\customize.js ■メール設定 「設定 → 店舗設定 → メール設定」 例えば「注文受け付けメール」の「テキスト」と「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 ■権限設定 「設定 → システム設定 → 権限管理」 例えば「拒否URL」に「/setting」と登録すると、「/setting」以下のページにアクセスできなくなる 例えば「拒否URL」に「/setting/system/authority」と登録すると、権限管理ページにのみアクセスできなくなる 例えば「拒否URL」に「/product」と登録すると、商品管理ページにのみアクセスできなくなる 左メニューからも項目が非表示になる 「/product」を登録した場合、「商品管理」の親メニューごと非表示になる ■管理画面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 に移行すると、この設定内容は管理画面から変更できなくなる ■ログ表示 「設定 → システム設定 → ログ表示」 複数台構成でログを同期しないなら、そのときアクセスしているサーバのログだけが表示される スティッキーセッションを設定しておけば、再読み込みごとに変わることはないが、ロードバランサーの設定を確認しておく ■マスターデータ管理 「設定 → システム設定 → マスターデータ管理」 実際に権限を追加してみる mtb_authorityを選択して以下を登録して「保存」をクリック ID: 2 Name: テスト これで「メンバー管理」や「権限管理」に反映される ■テンプレートをダウンロード 「オーナーズストア → テンプレート → テンプレート一覧」 「デフォルト」のダウンロードボタンを押すと、以下の内容をダウンロードできる app/template/default ■Git管理の考察 ※要検証&要検討 CSS管理、JavaScript管理、メール設定 などにより、サーバ上のファイルが編集される GitからのPULLによりプログラムをサーバ上に反映している場合、コンフリクトが発生する可能性がある Git管理をどうするかは検討が必要 管理画面からファイルを編集すると、最終行の改行を削除されてしまう エディタ内で改行を追加しても削除されてしまう そうでなくてもコンフリクトの原因になるので、管理画面からの編集は禁止しておくべきか ただし、それはそれで利便性を損なっている GitHubなら、サーバ上から定期的に自動コミットさせるという手はありそう 加えて、専用画面から任意のタイミングでもコミットできるツールや、 サーバ上のファイルに変更があったものを一覧&詳細表示するツールもあると良さそう それなら、Gitを扱う開発者が気を付けておけば、管理画面からはGitの知識がなくても操作できそう ただし検収環境など他環境も作るなら、そこでの変更を取り込むのは問題がありそう
■メールの送信先
管理画面の「設定 → 店舗設定 → 基本設定」にて、以下のアドレスを登録できる ・送信元メールアドレス(From) ・問い合わせ受付メールアドレス(From, ReplyTo) ・返信受付メールアドレス(ReplyTo) ・送信エラー受付メールアドレス 「注文受付メールアドレス」が無いので注文されても管理者にメールは送信されず、注文内容は管理画面から確認するしかないのだろうか …と思ったが、src/Eccube/Service/MailService.php の350行目あたりを確認する限り、「問い合わせ受付メールアドレス(From, ReplyTo)」に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());
よって ・問い合わせ受付メールアドレスには、注文や問い合わせを受け付けたいメールアドレスを入力する ・メールは、顧客に送られるものと同じものがBccで送られてくる という挙動みたい
■カスタマイズ
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内にファイルをコピーし、そのコピーしたファイルを編集する ■キャッシュの扱い コントローラやテンプレートは、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 などをピンポイントで手動削除すれば高速に表示確認できる ただし無理矢理感は否めない また、.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ファイルの編集」を参照 ■メンテナンスモード キャッシュクリア時やプラグインのインストール途中など、 ユーザ側にアクセスしてほしくない場合はメンテナンスモードを利用できる 以下のようにすると、ユーザ側の表示がメンテナンス画面に変更される $ 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 ■商品規格 商品ごとに「この色でこの大きさなら○円で在庫は○円」を登録できる ただし規格は1商品に対して2つまでしか紐付けられないので注意 以下のプラグインを使えばある程度柔軟に対応できるかもしれないが、 注文内容の商品に対するオプション内容を、管理画面から変更できないので注意 (開発元に確認済み。いったん商品を削除しての再追加はできる) 4.0系|商品オプションプラグイン for EC-CUBE4|株式会社ブラテック https://www.ec-cube.net/products/detail.php?product_id=1787 ■税抜価格(税別価格)で表示 プラグインがあるくらいなので、標準機能では対応していないみたい 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 消費税転嫁対策特別措置法により、税抜表示が問題になる可能性があるらしい? 要勉強 消費税転嫁対策特別措置法 | 消費者庁 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/ ■商品登録時の価格を内税にする 税率設定を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 ■商品一覧での並び順 管理画面から簡単に並び順を変更する機能は無いみたい プログラムを改造するか、プラグインを探すなどする必要がある [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
■カスタマイズ: 対応ステータスの調整
■対応ステータスの追加 設定 → システム設定 → マスタデータ管理 → 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 ... 返品
処理の際にこれらの値を使用する まず、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() が実行される これにより、入金日時が記録される
■カスタマイズ: ページの作成
■下層ページの作成(管理画面から作成) 管理画面の「コンテンツ管理 → ページ管理 → 新規作成」から作成できる ヘッダなどが表示されたページにしたければ、「レイアウト設定」で「下層ページ用レイアウト」などを選択する 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\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 %}
■カスタマイズ: マイグレーションでのデータ管理
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 のように必要がある マイグレーションの実行順に影響はありそうなので注意が必要そう
■カスタマイズ: 会員情報に項目を追加
※CSVエクスポートなども正しく動作するかは未確認 ※プラグインで対応する、という手段も検討する EC-CUBE4カスタマイズ - [4系] 新規会員登録画面に項目を追加する方法 https://umebius.com/eccube/eccube4-add-new-form-item-customer/ ■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」と表示される つまり、表示の際に時差は調整されるみたい
■カスタマイズ: お問い合わせフォームの複製
未検証だが、以下が参考になりそう [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
■動作確認 管理画面の「コンテンツ管理 → 記事管理」から、データの登録編集削除を行える 挙動は基本的に「新着情報管理」と同じ
■プラグインの導入
プラグイン → プラグインを探す から「商品レビュー管理プラグイン」をインストール インストール完了のダイアログが表示されたら「完了」ボタンを押す プラグイン一覧画面に遷移するので、プラグインを有効化する インストールできない場合、以下などを参考にする また管理画面からのインストールよりも、コマンドラインからのインストールの方が格段に安定しているらしい 複数回エラーになった場合は、コマンドラインからのインストールを試すといい EC-CUBE4系でのプラグインエラーの原因と予防対策 - Qiita https://qiita.com/nanasess/items/791c9ec98f69ada93ea0 ■インストールについて考察 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の導入には結局結構なお金がかかるかもしれない また 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=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=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|あずみ.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系|キービジュアル管理 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
■プラグインの導入: プラグインを導入済みの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
以下を参考に、この現象を回避する 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 あとは、必要に応じて管理画面からプラグインを有効化&設定する ■メモ 公式プラグインは上記で対応できそうだが、独自プラグインの場合はさらに問題があるみたい 独自プラグインのためにComposerで依存パッケージをインストールすると、依存解決ができなくなるらしい 現状問題は放置されている。ECCubeはComposerと相性が悪いみたい EC-CUBE4 独自プラグイン開発 ?独自プラグイン開発Tips - Qiita https://qiita.com/haruna-nagayoshi/items/27108c75eaf9511f3524 なかなかややこしいことになるようなので、どうしても対応するなら ・プラグインとvendorの内容を丸ごとGit管理対象にする ・データベースは「プラグイン導入済みの状態のdump」を作成し、初期データベースとする くらいか ただし強引さは否めない
■プラグインの導入: プラグインを追加削除したときの挙動を検証
■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 ※「EC-CUBE対応バージョン」は「4.0.0, 4.0.1, 4.0.2, 4.0.3」となっている 現状4.0.4はサポートしていないとのこと このプラグインを使うなら、ECCube本体は4.0.3を使う方が無難か。もしくは実質4.0.4でも問題ないか 以下にマニュアルがあるので、これをもとに進める 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決済情報」が表示されている 「売上確定(実売上)実行」をクリックすると売上が確定されるみたい 対応状況: 新規受付 支払方法: クレジットカード決済 決済状況: 売上 なお「対応状況」は自動で変わらなかった(「新規受付」のまま) 手動で「入金済み」に変更することはできた ベリトランス管理画面の取引検索画面で再度確認すると、 「ステータス」が「売上」となっているデータが追加されていた ■決済のキャンセル ・ECCubeの注文詳細画面で「[売上済]再売上(実売上)実行」をクリックすると、ベリトランス管理画面で「ステータス」が「キャンセル(売上)」が追加された 前回の売上をキャンセルして、再度売上にしたということ? ★要確認 ・同画面で「取消(返品)実行」をクリックすると、決済が取り消された 決済後にキャンセルしたことで、データとしてはおかしな状態になった?詳細画面に「お支払い合計と決済の金額が異なります。」と表示されている また、「対応状況」は「新規受付」になっている ★要確認 ■「与信」を「与信+売上」に変更 クレジットカード決済のオーソリとは?安全性をさらに高める対策も紹介|クレジットカード決済代行のベリトランス株式会社 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/ ■「本人認証(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 ログダウンロード」 ダウンロードできそうだがダウンロードできない 現状ログが無いだけ?そもそもどこのログをダウンロードするのか 要確認
■プラグインの作成
■プラグインでページを作成 $ 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 ■プラグインで作成したページにヘッダとフッタを表示 引き続き、プラグインの有効化時にページとレイアウトに関する情報がデータベースに登録されるようにする 以下のファイルを作成する このファイルはプラグインの有効化・無効化時に実行されるので、ここにページレイアウト登録のためのプログラムを記述する 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が作成されていることを確認する C:\vagrant\eccube4\code\main\html\app\Plugin\SamplePage\Entity\Config.php 以下にRepositoryが作成されていることを確認する C:\vagrant\eccube4\code\main\html\app\Plugin\SamplePage\Repository\ConfigRepository.php 以下にFormが作成されていることを確認する(管理画面のフォームに項目を追加するためのもの?要確認) C:\vagrant\eccube4\code\main\html\app\Plugin\SamplePage\Form\Type\Admin\ConfigType.php 以下にControllerが作成されていることを確認する C:\vagrant\eccube4\code\main\html\app\Plugin\SamplePage\Controller\Admin\ConfigController.php 以下にEventが作成されていることを確認する C:\vagrant\eccube4\code\main\html\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); } }
以下のファイルを作成する C:\vagrant\eccube4\code\main\html\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の解説を探すべきか もとからかなりの数のテストが書かれているみたい
■本番環境(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?encryption=ssl&auth_mode=login&username=example@gmail.com&password=xxxxxxxxxxxxxxxx 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?encryption=ssl&auth_mode=login&username=example@gmail.com&password=xxxxxxxxxxxxxxxx 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」があればそれを読み込むという分岐を設ける で対応できるかもしれないが、何か特定の機能を使えなくなっていないかは要検証 また、この方法でうまくいくなら .htaccess で設定を行うよりもいいかもしれない (.htaccess で設定すると、一部の機能を管理画面から設定できなくなるため。ただし階層を変えた .env を本当に編集できるかは要検証) phpdotenvで環境変数を制御する - demolog http://skitn.hateblo.jp/entry/2018/04/22/212210 phpdotenvについて。 - Qiita https://qiita.com/H40831/items/aaf8ffa727928661ad1d
■トラブル
■プログラムに異常は見当たらないのにエラーになる 管理画面の「コンテンツ管理 → キャッシュ管理」からキャッシュを削除する 以下のコマンドで削除してもいい(むしろ、コマンドで削除する方が余計なデータが残らなくていいかも) $ php bin/console cache:clear --no-warmup ■データベースに接続できたりできなかったりする データベースへの接続パスワードに「$」が含まれていたときの挙動が怪しい .env でパスワードをシングルクォートで囲えばいいかと思ったが、それでも接続エラーになることがある Symfonyの仕様かもしれないが詳細は不明 ひとまず、データベースへの接続パスワードを変更して対応した
■メモ
■参考:導入の際に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>
■未検証 ・プラグインでのページ作成とプラグインなしのページ作成を試す。データベース連動も含めて ・プラグインの導入を試す 「おすすめ商品管理プラグイン」は機能的に分かりやすそうだし、公式プラグインなので丁度いいかも ・バリデーションの挙動を確認する ・テンプレート内の 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/