EC-CUBE4でメールアドレスではなく任意のユーザー名でログインする方法
EC-CUBE Advent Calendar 2020 17日目の記事です。
時々お客さんからの要望で、メールアドレスだけではなく任意のユーザー名でログインさせたいという要望があります。 もっと詰めていくと、会員登録時には「任意のユーザー名」「メールアドレス」「パスワード」だけで登録させたいというものまであります。
その場合、住所はどのタイミングで入力させるのかというと購入画面で入力させるようにすれば良いということですが、購入画面で購入者情報と配送情報を入力させる方法は別の機会に説明します。
今回は会員登録画面を「ユーザー名」「メールアドレス」「パスワード」だけの項目にし、ユーザー名でもログインできる方法を説明します。
続きを読むEC-CUBE4とWordPressを連携する方法
EC-CUBE Advent Calendar 2020 15日目の記事です。
EC-CUBE4でWordPressの記事を取得する方法を先日書きました。
EC-CUBE4でWordPressの投稿記事を簡単に出力する方法 - AmidaikeBlog
今回は記事を取得するのではなく、WordPressからEC-CUBE4の関数やEC-CUBE4からWordPressの関数を利用できる方法を説明します。
ちなみにEC-CUBE2系はこちらの記事を参考にしてください。
今回は前回とは異なり、EC-CUBE4とWordPressは同階層にインストールするようにします。
EC-CUBE4のURL
http://xxxxx/
というように、同一ドメイン、同階層でEC-CUBE4とWordPressが動作されている事を想定しています。
続きを読むEC-CUBE4でお問い合わせ内容をDBへ保存する方法
EC-CUBE Advent Calendar 2020 11日目の記事です。
EC-CUBE4のお問い合わせフォームの内容は標準ではメールアドレスしか送られません。
ただ、お客様によっては時々お問い合わせフォームの内容をDBへ保存しておきたいという方がいます。
今回はお問い合わせフォームの内容をDBへ保存する方法を説明します。
続きを読むEC-CUBE4でParsleyを利用したリアルタイムバリデーションの実装方法
EC-CUBE Advent Calendar 2020 8日目の記事です。
EC-CUBE4でフォーム画面の入力チェックはサーバ側のチェックで行っている画面と、HTML5のrequired属性を設定して入力チェックを行っている画面の2パターン存在しています。
サーバ側で入力チェックが必須なのは当然ですが、項目のフォーカスアウト時にリアルタイムバリデーションを行うように使い勝手をもう少し良くするために、入力チェックライブラリであるParsleyを利用します。
利用方法はサイトを見れば一目瞭然なのですが、EC-CUBE4で利用する方法を説明します。
Parsleyのダウンロード、配置
こちらよりparsley.zipファイルをダウンロードします。
parsley.zip
リンクを押すとGitHubの画面へ遷移するのでSource code (zip)
をダウンロード後解凍し、distディレクトリにあるi18n
ディレクトリとparsley.min.js
を下記のディレクトリへ配置します。
html/template/default/assets/js
Parsleyの利用方法
会員登録入力画面を参考にParsleyを設定してみます。
src/Eccube/Resource/template/default/Entry/index.twig
ファイルを開き、以下の内容をを追記します。
{% block javascript %} <script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script> {% endblock javascript %} ↓ {% block javascript %} <script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script> <script src="{{ asset('assets/js/parsley.min.js') }}"></script> <script src="{{ asset('assets/js/i18n/ja.js') }}"></script> <script> $(function() { Parsley.options.trigger = 'keyup focusout change input'; $('#form1').parsley({ classHandler: function(ParsleyField) { return ParsleyField.$element; }, errorsWrapper: '<p class="ec-errorMessage"></p>', errorTemplate: '<span></span>' }).on('field:validated', function() { var ok = $('.parsley-error').length === 0; if (!ok) { $('.bg-load-overlay').remove(); if (this.$element.attr('required')) { if (this.$element.hasClass('parsley-success')) { this.$element.parent('div').removeClass('error'); } else { this.$element.parent('div').addClass('error'); } } } }); }); </script> {% endblock javascript %}
Parsley.options.trigger = 'keyup focusout change input';
という箇所は、エラーチェックを行うタイミングを指定していますので、不要な方は削除してください。
次に、formタグにid属性を付与します。
<form method="post" action="{{ url('entry') }}" novalidate class="h-adr"> ↓ <form id="form1" method="post" action="{{ url('entry') }}" novalidate class="h-adr">
この設定を行う事で、フォーカスアウト時にリアルタイムバリデーションを行ってくれるようになります。
一部デザイン崩れを起こす箇所もありますが、そこは適宜修正してください。
また、独自にデザインを作成している方は、parsleyに記載されている内容をデザインに合わせて修正してください。
Bootstrap4を利用してフォーム画面を作成されている方は以下の内容を適用する事でエラー箇所がキレイに表示されます。
$('#form1').parsley({ errorClass: 'is-invalid', successClass: 'is-valid', classHandler: function(ParsleyField) { return ParsleyField.$element; }, errorsWrapper: '<div class="invalid-feedback"></div>', errorTemplate: '<div></div>' }).on('field:validated', function() { var ok = $('.is-invalid').length === 0; if (!ok) { $('.bg-load-overlay').remove(); } });
今回は必須入力のみのチェックを行なっていますが、他にも電話番号やメールアドレス等の入力チェックもありますので詳しくはParsleyのドキュメントをご覧ください。
EC-CUBE4で利用している郵便番号ライブラリの変更
EC-CUBE Advent Calendar 2020 6日目の記事です。
EC-CUBE4で会員登録等に住所補完を目的として利用している郵便番号ライブラリですが、こちらのyubinbangoライブラリを利用しています。
このライブラリは非常に便利なのですが、EC-CUBEにもissueが上がっているように一度入力した郵便番号へフォーカスを当てると、住所の項目が消えてしまいます。
通常利用している分にはそこまで大きな問題ではありませんが、どうしても不便だという方もいるため、別の郵便番号ライブラリを反映して対応するようにします。
ajaxzip3の利用
yubinbangoライブラリと同一作者が作成されているajaxzip3を利用します。
GitHubのREADMEにも記載されている通り、yubinbangoライブラリをオススメしているため、特に不都合がない方は無理に変更する必要はありません。
ソース変更箇所
郵便番号の住所補完を行なっている箇所は以下となります。
src/Eccube/Resource/template/default/Contact/index.twig src/Eccube/Resource/template/default/Entry/index.twig src/Eccube/Resource/template/default/Mypage/change.twig src/Eccube/Resource/template/default/Mypage/delivery_edit.twig src/Eccube/Resource/template/default/Shopping/nonmember.twig src/Eccube/Resource/template/default/Shopping/shipping_edit.twig src/Eccube/Resource/template/default/Shopping/shipping_multiple_edit.twig src/Eccube/Resource/template/admin/Customer/delivery_edit.twig src/Eccube/Resource/template/admin/Customer/edit.twig src/Eccube/Resource/template/admin/Order/edit.twig src/Eccube/Resource/template/admin/Order/shipping.twig src/Eccube/Resource/template/admin/Setting/Shop/shop_master.twig
このtwigファイル内に記載されている箇所を以下のように変更します。
<script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script> ↓ <script src="//yubinbango.github.io/ajaxzip3/ajaxzip3.js" charset="UTF-8"></script>
次に、この郵便番号を利用できるようにfunction.js
へ処理を追加します。
処理内容はこちらを参考にさせてもらっています。
- html/template/default/js/function.js
var postalHandler = function() { $postal = $('.p-postal-code'); $region = $('.p-region-id'); $locality = $('.p-locality'); AjaxZip3.zip2addr($postal[0].name, '', $region[0].name, $locality[0].name, '', '', false); }; $(function() { var postalHandler = function() { $postal = $('.p-postal-code'); $region = $('.p-region-id'); $locality = $('.p-locality'); AjaxZip3.zip2addr($postal[0].name, '', $region[0].name, $locality[0].name, '', '', false); }; $('.p-postal-code').change(function() { if ($(this).val().length >= 7) { postalHandler(); } }); });
- html/template/admin/assets/js/function.js
$(function() { var postalHandler = function(){ $postal = $('.p-postal-code'); $region = $('.p-region-id'); $locality = $('.p-locality'); AjaxZip3.zip2addr($postal[0].name, '', $region[0].name, $locality[0].name, '', '', false); }; $('.p-postal-code').change(function(){ if ($(this).val().length >= 7) { postalHandler(); } }); });
また、app/template/admin/Order/edit.twig
ファイルのみ以下の処理を{% block javascript %}〜{% endblock javascript %}
内へ記述します。
- app/template/admin/Order/edit.twig
var postalHandler = function(id) { $postal = $(id + ' .p-postal-code'); $region = $(id + ' .p-region-id'); $locality = $(id + ' .p-locality'); AjaxZip3.zip2addr($postal[0].name, '', $region[0].name, $locality[0].name, '', '', false); }; $('#shippingInfo .p-postal-code').change(function() { if ($(this).val().length >= 7) { postalHandler('#shippingInfo'); } }); $('#ordererInfo .p-postal-code').change(function() { if ($(this).val().length >= 7) { postalHandler('#ordererInfo'); } });
以上でajaxzip3が利用できるようになり、郵便番号へフォーカスを当てても一度入力した住所が消えることはありません。
一点対応できていない箇所として、
p-street-address
とp-extended-address
が対応できていませんので、そちらも適用させたい方は、
EC-CUBE4で郵便番号にTABでフォーカス移動すると番地が消える - Qiita
の処理をfunction.js
へ適用させるようにしてください。
EC-CUBE4でWordPressの投稿記事を簡単に出力する方法
EC-CUBE Advent Calendar 2020 4日目の記事です。
EC-CUBE4でサイトを公開したが、商品紹介記事などはWordPressで作成しており、 その記事をEC-CUBE側で公開したいという要望が時々あります。
そこで簡単にWordPress側から記事を取得してEC-CUBE4へ表示させる方法を書いていきます。
今回は、
EC-CUBE4のURL
http://xxxxx/
というように、同一ドメインでEC-CUBE4とWordPressが動作されている事を想定しています。
前提としてWordPress側でREST APIが有効になっている必要があります。
WordPressから記事情報を取得
トップページへアクセスした時にブログ記事を取得するパターンを想定してみます。今回は簡単にController側で処理をしてその内容を表示させます。
以下の内容をTopController.phpへ記述します。
- src/Eccube/Controller/TopController.php
/** * @Route("/", name="homepage") * @Template("index.twig") */ public function index(Request $request) { // ブログ情報を取得 $posts = json_decode(file_get_contents($request->getSchemeAndHttpHost().$request->getBasePath().'/blog/wp-json/wp/v2/posts?per_page=3&_embed')); $blogDatas = []; foreach ($posts as $data) { $item = []; $item['title'] = $data->title; $item['date'] = $data->date; $item['link'] = $data->link; $name = 'wp:featuredmedia'; if (isset($data->_embedded->{$name})) { $item['attachment'] = $data->_embedded->{$name}[0]; } $name = 'wp:term'; if (isset($data->_embedded->{$name})) { $item['category'] = $data->_embedded->{$name}[0]; } $blogDatas[] = $item; } return [ 'blogDatas' => $blogDatas, ]; }
ソース中にある
// ブログ情報を取得 $posts = json_decode(file_get_contents($request->getSchemeAndHttpHost().$request->getBasePath().'/blog/wp-json/wp/v2/posts?per_page=3&_embed'));
がWordPress側と通信して記事を取得する部分となります。
$request->getSchemeAndHttpHost().$request->getBasePath()
でドメイン部分を構築させています。Requestクラスについて詳しくはこちらをご覧ください。 fivestar.hatenablog.com
その後に続く、
/blog/wp-json/wp/v2/posts?per_page=3&_embed
がREST APIを利用した記事取得部分となります。
「per_page=xx」というパラメータをつける事でxx件まで記事のデータを取得することができます。 今回は3としているため、3件データを取得しています。
また、「&_embed」というパラメーターはアイキャッチの情報を含めたデータを取得することができます。 他のAPIの種類やパラメーターはこちらをご覧ください。
Reference | REST API Handbook | WordPress Developer Resources
これでデータが取得できましたので、twig側へ扱いやすいように整形します。
$blogDatas = []; foreach ($posts as $data) { $item = []; $item['title'] = $data->title; $item['date'] = $data->date; $item['link'] = $data->link; $name = 'wp:featuredmedia'; if (isset($data->_embedded->{$name})) { $item['attachment'] = $data->_embedded->{$name}[0]; } $name = 'wp:term'; if (isset($data->_embedded->{$name})) { $item['category'] = $data->_embedded->{$name}[0]; } $blogDatas[] = $item; }
この箇所は、WordPress側から取得したデータ必要な分だけ取り出しています。タイトルや日付、リンク先など最低限必要なものを取り出しています。
これでController側の処理が終わりました。次にTwigへの記述方法を説明します。
Twigへ取得した記事情報を表示させる
twig側は特に難しい事をするわけでもなく普通にタグを記述するのみです。今回は直接src直下にあるindex.twigを修正します。
- src/Eccube/Resource/template/default/index.twig
{% if blogDatas|length > 0 %} <section class="blog"> <div class="row"> {% for blogData in blogDatas %} <div class="col-3"> <a href="{{ blogData.link }}" target="_blank" rel="noopener"> {% if blogData.attachment is defined %} <figure class="thumbnail"> <img src="{{ blogData.attachment.source_url }}" alt="{{ blogData.title.rendered }}"> </figure> {% endif %} {% if blogData.category is defined %} <p class="category">{% for category in blogData.category %}{{ category.name }}{% if not loop.last %},{% endif %}{% endfor %}</p> {% endif %} <p class="title">{{ blogData.title.rendered }}</p> </a> <time>{{ blogData.date|slice(0, 10)|replace({'-': '.'}) }}</time> </div> {% endfor %} </div> </section> {% endif %}
blogDatas
はController側から渡された変数となり、記事が存在すれば表示させるという処理を行っています。
タグの記述方法はデザインに合わせて記述してください。
上記を記述後、EC-CUBE4のトップページへアクセスするとブログ記事が表示されるようになります。
以上でWordPressから記事を取得して表示させる方法となりますので参考にしてください。
GoogleショッピングへEC-CUBEに登録されている商品を登録する方法
Googleショッピングが日本でも無料で利用できることがアナウンスされています。
すでにEC-CUBEで掲載されている商品をGoolgeショッピングへ登録するには手動またはスプレッドシートで登録できますが、 バッチを利用して一括で登録できる方法を説明します。
準備としてGoogleマーチャントセンターへ登録が必要になります。
登録が完了したら、メニューのレンチアイコンからContent API
メニューを選択します。
選択後、「認証」メニューを選択し新しいAPIキーを+アイコンを押して作成します。
作成すると、content-api-key.json
ファイルがダウンロードされるのでこれを利用して連携処理を行います。
ここまで準備ができたら、今回はEC-CUBE3でGoogleショッピングの連携を行います。
Googleショッピング連携を行うにはライブラリが公開されていますので、 そのライブラリを利用して処理を行います。
ターミナルを起動し、EC-CUBE3がインストールされているディレクトリまで移動します。
以下のコマンドを実行してライブラリをインストールします。
最近、composerが2系へバージョンアップされていますので、混乱を防ぐためにもローカル環境へcomposer.pharをダウンロードし、composerコマンドを実行するようにします。
wget https://getcomposer.org/download/1.10.17/composer.phar
をしてローカル環境に落としてきた後、以下を実行します。
php composer.phar require google/apiclient:"^2.0" --ignore-platform-reqs
--ignore-platform-reqs
というパラメーターは、Composerの依存性チェックでphpバージョンエラーを一時的に回避するために付与しています。
インストールが完了したら、先ほどダウンロードしたcontent-api-key.json
ファイルを、
app/config/eccube
ディレクトリへ保存します。
また、マーチャントIDが必要になるのですがこちらのIDはGoogleマーチャントセンター管理画面の右上にあるユーザー名の横にある数字となります。
次にCommandを利用したプログラムを作成します。
src/Eccube/Command/ProductUpdateCommand.php
というファイルを作成し、以下のソースを記入します。
<?php namespace Eccube\Command; use Eccube\Application; use Eccube\Entity\BaseInfo; use Eccube\Entity\Product; use Knp\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; /** * Class ProductUpdateCommand * * @package Eccube\Command\Mall\Google */ class ProductUpdateCommand extends Command { protected function configure() { $this ->setName('google:productupdate') ->setDescription('Googleショッピング連携処理') ->setHelp(<<<EOF The <info>%command.name%</info> command Product Update Google Shopping, EOF ); } /** * Googleショッピング連携 * * @param InputInterface $input * @param OutputInterface $output * @return int|null|void */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); /** @var Application $app */ $app = $this->getSilexApplication(); $io->title('Googleショッピング連携処理 Start.'); putenv('GOOGLE_APPLICATION_CREDENTIALS='.$app['config']['root_dir'].'/app/config/eccube/content-api-key.json'); $merchantId = 'マーチャントIDを設定'; $url = 'サイトURLを設定'; // 例) https://example.com $client = new \Google_Client(); $client->useApplicationDefaultCredentials(); $client->addScope('https://www.googleapis.com/auth/content'); $service = new \Google_Service_ShoppingContent($client); $Products = $app['eccube.repository.product']->findBy(array('Status' => 1)); // バッチ処理 $entries = []; $i = 0; /** @var Product $Product */ foreach ($Products as $Product) { $entry = new \Google_Service_ShoppingContent_ProductsCustomBatchRequestEntry(); $entry->setBatchId($i); $entry->setMerchantId($merchantId); $entry->setMethod('insert'); $ShoppingProduct = new \Google_Service_ShoppingContent_Product(); $ShoppingProduct->setOfferId($Product->getCodeMin()); $ShoppingProduct->setTitle($Product->getName()); $ShoppingProduct->setDescription($Product->getDescriptionDetail()); $ShoppingProduct->setLink($url.'/products/detail/'.$Product->getId()); $ShoppingProduct->setImageLink($url.$app['config']['image_save_urlpath'].'/'.$Product->getMainListImage()); $ShoppingProduct->setContentLanguage('ja'); $ShoppingProduct->setTargetCountry('JP'); $ShoppingProduct->setChannel('online'); $ShoppingProduct->setAvailability('in stock'); if (!$Product->getStockFind()) { $ShoppingProduct->setAvailability('out of stock'); } // $ShoppingProduct->setCondition(''); $ShoppingProduct->setGoogleProductCategory('1025'); // https://www.google.com/basepages/producttype/taxonomy-with-ids.ja-JP.txt より適切なカテゴリIDを設定 $ProductCategories = $Product->getProductCategories(); $path = ''; foreach ($ProductCategories as $ProductCategory) { $path = $ProductCategory->getCategory()->getRootCategory()->getName(); } $ShoppingProduct->setBrand($path); // $ShoppingProduct->setGender(''); // $ShoppingProduct->setColor(''); $price = new \Google_Service_ShoppingContent_Price(); $price->setValue($Product->getPrice02IncTaxMin()); $price->setCurrency('JPY'); $shipping_price = new \Google_Service_ShoppingContent_Price(); $shipping_price->setValue('0'); // 配送料を設定 $shipping_price->setCurrency('JPY'); $shipping = new \Google_Service_ShoppingContent_ProductShipping(); $shipping->setPrice($shipping_price); $shipping->setCountry('JP'); // $shipping->setService('Standard shipping'); $ShoppingProduct->setPrice($price); $ShoppingProduct->setShipping(array($shipping)); $entry->setProduct($ShoppingProduct); $entries[] = $entry; $i++; } $batchRequest = new \Google_Service_ShoppingContent_ProductsCustomBatchRequest(); $batchRequest->setEntries($entries); $result = $service->products->custombatch($batchRequest); $io->text('処理件数 : '.$result->count()); $io->success('Googleショッピング連携処理 End.'); } }
上記ファイルを作成後、
php app/console google:productupdate
を実行するとGoogleショッピング連携処理が行われます。
処理を実行後、1時間程度待つとマーチャントセンターへ商品が登録されているのが確認できます。
正常に登録されていればGoogleショッピング連携処理が完了なので、 運用に合わせて適宜対応してください。
4系については今後公開します。