小田原ワーケーション(小田急電鉄)様の導入事例を公開しました。
地域の”体験”をひとつのサイトで”探す・予約する・決済する”まで一貫して提供するモール型EC。
EC-CUBEの柔軟性で実現した、その舞台裏をご紹介します。
Blog
小田原ワーケーション(小田急電鉄)様の導入事例を公開しました。
地域の”体験”をひとつのサイトで”探す・予約する・決済する”まで一貫して提供するモール型EC。
EC-CUBEの柔軟性で実現した、その舞台裏をご紹介します。
このたび、2025年版「発見たんけん千葉県 わくわくお仕事しらべ隊(松戸市用)」に当社ジョーレンを掲載いただきました。
小学生向けのキャリア教育 副教材であるこの冊子、
松戸市内の小学校に納品され、授業教材として活用されます。
インタビュイーは、自身も小学生のお父さんであるWマネージャー。
「システムエンジニアってどんな仕事?」「ECサイトの機能を見てみよう!」など、
小学生でもイメージしやすい内容となるよう、発行元である株式会社地域新聞社の担当者様と相談しながら誌面を作成しました。
10月には市内の小学校にて出張授業も予定しています。
便利になったインターネット社会で、人と人とを繋ぐお仕事をしているジョーレン。
そんなお仕事の魅力を、社長の熱い思いとともにがっちり伝えてまいります!
今回は、弊社が手掛けた ECサイトのカスタマイズ事例 をご紹介します。
「こんなことできるの?」と思った方、ぜひお気軽にご相談ください。
目次
【1】URLのスラッグ対応
サイトのSEO対策として、商品やカテゴリのURLスラッグをカスタマイズするニーズが増えています。弊社では、商品IDではなく、意味のあるカテゴリ名やキーワードを使用することで、検索エンジンにとって親しみやすいURL構造を実現しました。これにより、
SEO効果の向上とユーザーの利便性が高まりました。
【2】独自クーポンの作成
多くのECサイトでは、特定の条件を満たした顧客にクーポンを配布することが求められます。弊社では、購入金額や会員ランク、特定のカテゴリー購入者向けに、独自のクーポン発行機能を実装しました。これにより、マーケティング施策を柔軟に展開することが可能になり、顧客満足度を向上させています。
【3】独自購入オプションの追加
商品購入時に必要なオプションをカスタマイズできる機能を追加しました。例えば、ギフトラッピングやメッセージカードなど、顧客のニーズに合わせた購入オプションを自由に設定することができ、よりパーソナライズされたショッピング体験を提供しています。
【4】セット商品の販売
セット商品を販売するためのカスタマイズも承りました。特定の商品をセットにして割引価格で提供することで、顧客の購買意欲を刺激し、売上を大幅にアップさせることができました。セット商品には、在庫管理や価格設定など、細かな部分の調整も可能です。
【5】外部レビューとの連携
商品レビューを外部のレビューサイトと連携させることで、信頼性を向上させました。これにより、リアルな口コミをサイト上で紹介できるようにしました。
【6】アンケート作成機能
顧客からのフィードバックを集めるため、アンケート作成機能を導入しました。特定のキャンペーンや商品に対する意見を簡単に収集でき、マーケティング戦略の改善や新商品開発に役立っています。
【7】BtoB向けクローズドサイト機能
法人向け(BtoB)の取引を行うための、クローズドサイト機能も導入しました。全てのページ(会社概要や規約ページを除く)は、ログインしないと閲覧できない設定にすることで、取引先専用の環境を構築しました。
「この機能、うちでも使える?
こういう悩みを解決したいけど、どうしたら?」
そんなお悩みをお持ちでしたら、ぜひJoolenにご相談ください。
お問い合わせはこちら
目次
画像SEOについて考えたことはありますか?
普段は画像をそのまま記事に使用したりしていることもありますが、一つの工夫でアクセスが見込める可能性がある。
今回はブログを書くにあたり、改めて画像SEOについて見直してみました。
Google画像検索で上位に画像が表示されることを目指す取組について。
Google検索では上位画像は通常の検索結果にも表示されることがあるので、通常のSEOを行う他に画像SEOも意識することで露出が増えアクセス増が期待できます。
検索結果の上位に自分が探している内容の画像があると自分が探していた内容かも!と見てしまうことがありますね。
こういった効果を期待したいです。
では、画像SEOに取り組むにあたって行ったらいいことはなんでしょうか。
無意に画像を多用するだけでは記事やコンテンツの品質に関わってきます。
6つのポイントに絞ってみます。
現在はフリー素材の画像など、利用できる画像は多々あります。
しかし、同じような画像素材を使用ていると、他の記事との差別化が難しくなり埋もれていくこともあるでしょう。
独自性のあるオリジナル画像を使用することで他の記事とは差別化がされます。
掲載されている画像=特定のコンテンツという関連付けができますね。
ただ、このオリジナル画像については必ずしもSEOにマイナスの影響を与えるわけではありません。
Google 画像検索 SEO ベストプラクティスにて「Google が画像を検出してインデックスに登録できるようにする」と記載もあるため、独自画像を用意した方が好ましいという形です。
Google 画像検索 SEO ベストプラクティス:
Googleでの評価は、ファイル名も評価の対象となります。
iPhoneやデジカメ等で撮影した場合に設定される「IMG○○.png」等の名前だと、画像がどのような内容であるか認識ができず評価がされません。
短く、画像内容を表現できる名前にすることで評価されやすくなっていきます。
花見の写真などであれば、「hanami.png」や「tokyo_hanami.png」などであると、花見や東京の花見などの評価がされます。
掲載画像についても一工夫することで変わってくることもあるかと思いますので、積極的に行っていきましょう。
代替えテキストを設定することによって、検索エンジンに画像の情報が伝達されます。
タグ内の単語が検索にヒットすることも考えられるので、積極的に設定するのが良いですね。
各画像全てに代替えテキストの用意は手間ではありますが、検索上でのヒット率向上を考えると行っておきたいところです。
ただし、代替えテキスト内にキーワードを多く記載する場合、スパム判定され悪い評価となるので気をつけましょう。
画像の前後や左右に文章を配置することによって画像内容を前後の文脈から判断することがあります。
関連する文章を配置することによって、検索のヒット率向上となります。
関連性を持たせることは記事自体の検索順位を維持するのにも重要となるので配置にも気をつけていきましょう。
サポートされている画像形式を使用、速度と画質を考慮して最適化これらもGoogleの評価対象となることがあります。
速度が極端に遅いページは評価が下がるようになったという話も…
早い=評価が上がるというわけではないので注意です。
サポートされている画像形式を使用
サポートしている形式:BMP、GIF、JPEG、PNG、WebP、SVG、AVIF
速度と画質を考慮して最適化
低画質で何の画像かわからないと画像を掲載している意味がありません。
高画質になるにつれ表示速度にも影響が出るため適度に画像圧縮を行い最適化しましょう。
構造化データで画像をマークアップすることでバッジというものがつきます。
バッジとは、Google画像検索をした時に画像に「パソコン」「カメラ」「椅子」などカテゴリ分けされたものとなります。
何の画像であるかがわかることで認識がされやすくなり、リッチリザルトにも影響が出てきます。
画像サイトマップを送信することで、サイトマップの情報を使用しなければ検出されない可能性のある画像のURLを指定できます。
画像サイトマップ作成は固定画像なら現実的ではあるかと思ういますが、ブログなどの毎度変わる画像に関しては保守の面で手間となるため、導入については要検討なところですね。
今回記事を書くにあたり画像を用意しましたが、画像を作成するだけでも大変だなと感じました。
全てを最初から実行するのは難しいものと思います。
できる範囲から少しずつ取り組んでいくのが良いでしょう。
SEO観点におけるURLについて調べていると
URLのクエリパラメータに関するベストプラクティスとして以下の内容が載っていました。
同じパラメータを 2 回使用しないようにします。Googlebot はどちらかの値を無視する可能性があります。
推奨: ?type=candy,sweet
非推奨: ?type=candy&type=sweet (引用)
参考:URL のクエリ パラメータに関するベスト プラクティス
Eccubeで複数選択可の項目を検索すると、 「?type[]=candy&type[]=sweet」 のような、
上記で非推奨とされる「同じパラメータを複数回使用しているURL」になります。
検索エンジンにインデックスさせる対象が、単一選択されたページのみ(「?type[]=candy」「 ?type[]=sweet」 は対象だが 「?type[]=candy&type[]=sweet」 はインデックス対象ではない)
の場合は気にする必要はないと思いますが、もし、複数選択したページをインデックスさせたいなら対応が必要かも(?)ということで今回、Eccube4.3で試してみました。
目次
前提条件である「複数選択検索」をできる項目が、Eccube4.3の初期状態では無いので、まずはそれを追加します。
今回は「複数選択可なカテゴリ」を追加します。
(カテゴリ検索は既にあるのですが、単一選択しかできないので、複数選択可な状態のなものを追加します)
1-1.FormTypeのカスタマイズ
EccubeのフォームはFormTypeに実装されているので、カスタマイズして、複数選択可用のカテゴリ項目を追加します。
app/Customize/Form/Extension/ 配下に以下のような「SearchProductTypeExtension.php」を追加します。
実装例
<?php
namespace Customize\Form\Extension;
use Eccube\Form\Type\SearchProductType;
use Eccube\Repository\CategoryRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
class SearchProductTypeExtension extends AbstractTypeExtension
{
private CategoryRepository $categoryRepository;
public function __construct(CategoryRepository $categoryRepository)
{
$this->categoryRepository = $categoryRepository;
}
public static function getExtendedTypes(): iterable
{
yield SearchProductType::class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$categories = $this->categoryRepository->getList(null, true);
$builder->add('category_ids', EntityType::class, [
'class' => 'Eccube\Entity\Category',
'choices' => $categories,
'choice_label' => 'NameWithLevel',
'multiple' => true,
'expanded' => true,
'required' => false,
'label' => 'カテゴリ(複数選択)',
'attr' => [
'class' => 'form-control',
],
]);
}
}
これで、既存のSearchProductType に対して、「category_ids」フォーム項目を追加したことになります。
既に存在するカテゴリ検索項目「category_id」との違いは、チェックボックスになるように「’multiple’ => true」「’expanded’ => true」を追加しただけです。
1-2.Repositoryのカスタマイズ
新しく追加したフィールド「’category_ids’」で検索されるようにRepositoryもカスタマイズします。
app/Customize/Repository配下に以下のようなファイルを追加します。
実装例
<?php
namespace Customize\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Eccube\Doctrine\Query\QueryCustomizer;
use Eccube\Repository\QueryKey;
class ProductRepositoryCustomizer implements QueryCustomizer
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* 商品検索にカテゴリ(複数)を追加する
*
* @param QueryBuilder $builder
* @param array $params
* @param $queryKey
*/
public function customize(QueryBuilder $builder, $params, $queryKey): void
{
// category(s)
if (!empty($params['category_ids'])) {
$Categories = $params['category_ids'];
if ($Categories->count() > 0) {
$subQb = $this->entityManager->createQueryBuilder();
$subQb->select('1')
->from('Eccube\Entity\ProductCategory', 'pct_sub')
->where('pct_sub.Product = p') // 'p'は親クエリのエイリアス
->andWhere($subQb->expr()->in('pct_sub.Category', ':Categories'));
$builder->andWhere($builder->expr()->exists($subQb->getDQL()))
->setParameter('Categories', $Categories);
}
}
}
/**
* ProductRepository::getQueryBuilderBySearchData に適用する.
*
* @return string
* @see \Eccube\Repository\ProductRepository::getQueryBuilderBySearchData()
* @see QueryKey
*/
public function getQueryKey(): string
{
return QueryKey::PRODUCT_SEARCH;
}
}
「category_ids
」が選択されている場合は、選択されたカテゴリIDが、カテゴリとして設定されている商品をExistで絞り込んでいます。
1-3.twigのカスタマイズ
app/template/default/Product 配下に src/Eccube/Resource/template/default/Product/list.twig のコードをコピーして配置します。するとapp/template/default/Product/list.twig の方が参照されます。
新しく配置したapp/template/default/Product/list.twigのformタグの中に、category_idsフィールドを追加します。
その時、既存の、「FormTypeのフィールドをfor文でhiddenとして書き出している処理」からcategory_idsフィールドを除外します。
実装例
<form name="form1" id="form1" method="get" action="?">
{{% for item in search_form %}
{# category_idsは除外 #}
{% if item.vars.name != 'category_ids' %}
<input type="hidden" id="{{ item.vars.id }}"
name="{{ item.vars.full_name }}"
{% if item.vars.value is not empty %}value="{{ item.vars.value }}" {% endif %}/>
{% endif %}
{{% endfor %}
{{# 新しい複数選択カテゴリフィールドの表示 #}
{<div class="form-group">
{{ form_widget(search_form.category_ids) }}
{{ form_errors(search_form.category_ids) }}
<button type="submit" class="btn btn-primary mt-2">
{{ '検索' }}
</button>
{</div>
</form>
これで検索結果一覧ページにアクセスすると、複数選択出来るカテゴリのチェックボックスと検索ボタンが追加された画面が表示されます。
これでカテゴリの複数検索すると下記のようなURLになります。(デコードしています)
http://localhost:8080/products/list?mode=&category_id=&name=&pageno=&disp_number=20&orderby=1&category_ids[]=7&category_ids[]=9
※動作確認のため、いくつかカテゴリを追加しています。
これで前提条件である「同じパラメータを複数回使用したURL」の状態になりました。
いよいよ本題であるURLのカンマ区切りに着手します。
2-1.検索実行直前にjavascriptで配列 → 文字列 変更する
チェックボックスで選択した値をそのままの状態で検索実行してしまうと、配列のURLが生成されてしまうので、javascript でカンマ区切りの文字列に変更します。
実装例(app/template/default/Product/list.twig に追記しました)
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('form1');
form.addEventListener('submit', function (e) {
const checkboxes = form.querySelectorAll('input[name="category_ids[]"]:checked');
const selectedValues = Array.from(checkboxes).map(cb => cb.value);
// 既存の複数inputをdisabledにして送信されないようにする(後で1つのinputにまとめるため)
checkboxes.forEach(cb => cb.disabled = true);
// 新しいhidden inputを1つ作成
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'category_ids';
input.value = selectedValues.join(',');
form.appendChild(input);
});
});
</script>
上記のjavascript コードで、以下の内容を実行しています。
これだけでURLは、目的である「同じパラメータは1回しか使用されない」状態になります。
http://localhost:8080/products/list?mode=&category_id=&name=&pageno=&disp_number=20&orderby=1&category_ids=7,9
※デコードしています。
しかし、これだけだと検索がうまくいきません。FormTypeでは配列で送られてくることが期待されていますが、文字列で来たためエラーになってしまいます。
今度はFormType側で文字列 → 配列 に変換して帳尻を合わせます。
2-2.FormTypeの処理時に文字列 → 配列 に変換する
手順1で追加した「SearchProductTypeExtension
」に以下の内容を追記します。
実装例
<?php
namespace Customize\Form\Extension;
・・・省略・・・
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents;
class SearchProductTypeExtension extends AbstractTypeExtension
{
private CategoryRepository $categoryRepository;
public function __construct(CategoryRepository $categoryRepository)
{ ・・・省略・・・ }
public static function getExtendedTypes(): iterable
{ ・・・省略・・・ }
public function buildForm(FormBuilderInterface $builder, array $options)
{
・・・省略・・・
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { $data = $event->getData(); // カンマ区切りの文字列を配列に変換 if (isset($data['category_ids']) && is_string($data['category_ids'])) { $ids = array_filter(array_map('trim', explode(',', $data['category_ids']))); $data['category_ids'] = $ids; $event->setData($data); } });
}
}
PRE_SUBMIT イベントを使って、カンマ区切り文字列を配列に変換しています。
PRE_SUBMIT イベントは「フォームにデータを送信する前に、リクエストのデータを変更する」時に使うものなので、今回のような目的にはピッタリですね。
参考:Symfony Form Events
FormTypeに上記を修正を適用すると、URLの目的を達成しつつ、検索機能も損なわない状態になります。
一応これで今回の試したかったことは出来ました。お試しだったので「category_ids」に特化したコードになってしまいましたが、本当に運用するつもりなら、処理は部品化して汎用的に使えるようにすると良いと思います。
いかがだったでしょうか。今回はたまたま見つけた「同じパラメータを 2 回使用しない」を試してみました。
私は今まで聞いたことが無かったので、SEO対策としての重要度はそこまで高く無いのかもしれません。また、最初にも書いた通り、複数選択された状態をインデックスさせる必要が無い場合はそもそも必要ありません。
それなりに対応コストはかかるので、もし本格的に導入を検討をする際には、一度SEO対策会社の方へ相談してから判断するのが良いでしょう。
こんにちは!
今回はネットショップ運営を考えている方、または今のECサイトに満足していない方に向けて、
日本生まれのECプラットフォーム「EC-CUBE(イーシーキューブ)」をご紹介します。
無料で始められて、しかも柔軟にカスタマイズできるとあって、実は中小企業から大手企業まで幅広く使われているんです。
目次
EC-CUBEは、日本発のオープンソースECプラットフォーム。
言い換えれば、「自分で自由に作れるネットショップの土台」です。
無料でダウンロード・利用できて、自社に合ったショップを思い通りに作れるのが最大の魅力!
デザインの変更も、機能追加も、まるでブロックのパーツのように組み立て自由自在なのです。
✅ 商品の登録やカテゴリ分け
✅ 受注〜発送までの一元管理
✅ 会員登録・購入履歴・ポイント機能
✅ HTML/CSSでの自由なデザイン編集
✅ プラグインで機能拡張(クーポンやSNS連携)
つまり、「これがあればネットショップとして十分」な機能が、はじめからそろっています。
💡 スマホ表示に自動対応
スマートフォンでも見やすく、購入しやすいUIになっています。
💡 会員ランクやポイント制度もカンタン導入
リピーター獲得の施策もばっちり。
💡 越境ECも視野に(多言語・多通貨対応)
必要に応じて、海外販売の足がかりにもなります。
💡 エンジニアにはうれしい構造
開発やカスタマイズがしやすい設計、開発コミュニティやドキュメントも充実。
💡 管理画面がわかりやすい!
日本語で操作に迷わず、誰でもショップ運営をスタートできます。
* これから本格的にネットショップを始めたい方
* 自由にカスタマイズして差別化したい方
* ShopifyやBASEでは物足りなくなってきた方
* 自社独自の販売スタイルに合わせて構築したい方
自分で作りたい人にも、プロに頼んで構築する人にも使いやすいのがEC-CUBEの魅力。
まずは一度、公式サイトやデモで触ってみてはいかがでしょうか?
EC-CUBEは、アイデア次第でショップの可能性がどんどん広がる柔軟なプラットフォームです。
「やってみたいけど、どこから手を付けたらいいかわからない」「自社向けにカスタマイズしたい」など、
導入や運用でお悩みがあれば、ぜひお気軽にご相談ください。
お問い合わせはこちら
目次
弊社では長らく、社内サービスを 単一の AWS アカウント に集約して運用してきました。サービス数自体は多くないものの、運用メンバーが増えるにつれて権限の境界が曖昧 となり、「誰がどこまで操作できるのか」を把握しづらい状況に陥っていました。
この課題を解決するため、AWS Organizations を活用した マルチアカウント戦略 を導入し、環境・責務ごとにアカウントを分離する取り組みを開始。本記事では、その背景と具体的なアプローチ、そして得られたメリットを紹介します。
AWS Organizations を用いて 環境(開発・本番)や目的(ネットワーク・共有サービス)ごとにアカウントを分割 し、ガバナンスを効かせつつスケーラブルに拡張していく方法です。アカウントはそれぞれがセキュリティ境界となり、権限・課金・ログなどを独立して管理できます。
アカウント単位で IAM ロールを限定できるため、操作ミスなどにより特定のサービスが停止するリスクを軽減できます。サービスごとにアカウントを分けることで、影響範囲を明確に分離し、安定した運用が可能になります。
コスト配分タグでもある程度の可視化は可能でしたが、マルチアカウント構成によりアカウントごとの使用量が分けて見られるようになり、より直感的かつ明確にサービスごとのコストを把握できるようになります。請求自体は管理アカウントに集約されるものの、各アカウントの使用状況が明確になることで、予算アラートの設定や異常検知にも役立ちます。
ルーティングを担うALB をネットワークアカウントに配置し、そこから別アカウントに移行した EC2 インスタンスへトラフィックを転送する構成とします。
本記事では、実際にHPインスタンスを別アカウントに移行したのでその手順を記載します。
1. 移行元アカウントでVPC > ピアリング接続からピアリング接続を作成をクリック
2. VPC ID(リクエスト元)は接続元VPCのID、VPC ID(アクセプタ)は接続先VPCのIDをそれぞれ入力
今回のケースでは、アクセプタ側が別アカウントのため、アカウントID(アクセプタ)には接続先のアカウントIDを、VPC ID(アクセプタ)には移行先VPCのIDを入力
3. 移行先アカウントでVPC > ピアリング接続を表示するとリソースができているので、「リクエストを承諾」をクリック
4. それぞれのVPCでルーティングできるよう、ルートテーブルに設定を追加
1. ALBが存在するアカウントで
2. ALBのセキュリティグループを共有する
1. EC2のコンソール > インスタンスの状態 > イメージとテンプレート >イメージを作成から、AMIを作成する
2.イメージ名を入力し、「イメージを作成」を選択する
※本番稼働しているインスタンスの場合、「インスタンスを再起動」には要注意。チェックを入れていると、インスタンスが再起動されてしまいます。
3. EC2 > AMIから、作成したAMIを移行先のアカウントへ共有
4. 移行先のアカウントでEC2 > AMIから、「AMIからインスタンスを起動」をクリックで起動
5. EC2のセキュリティグループ > インバウンドルールは、RAMで共有したALBのセキュリティグループを紐づける
※この時、カスタムルール内のプルダウン内にはALBのセキュリティグループは存在しないと思うので、セキュリティグループIDで直接入力する
1. EC2 > ターゲットグループから、ターゲットグループの作成をクリック
2. 他アカウントのEC2は直接参照できないので、グループの詳細の指定は「IPアドレス」を選択
3. 「その他のプライベートアドレス」を選択し、移行先のEC2インスタンスのプライベートIPアドレスを入力し、ターゲットグループを作成
4. ALBのターゲットグループを差し替えて、画面が表示されれば完了
単一アカウント運用からマルチアカウント構成への移行は、セキュリティや運用の観点から重要な一歩でした。特に、IAMの権限分離やコストの明確化といった課題は、アカウントを分割することでシンプルかつ直感的に解決できることが実感できました。
今回のように、ALBをネットワークアカウントに残したまま、EC2をサービスアカウントへ移行することで、既存構成を大きく変更せずにスムーズな切り出しが可能です。
今後も、サービス単位や環境単位でのアカウント分離をさらに進めることで、よりガバナンスが効いた、安全かつスケーラブルなクラウド環境を構築していきたいと考えています。
本記事が、同様の課題を抱える方々の参考になれば幸いです。今後も運用で得られた知見を積極的に共有していきますので、ご期待ください。
目次
ここでは以下のように、動的なページ(例:検索結果などの属性を使った絞り込み条件を含んだURL)のURLを、?を含むクエリパラメータを使わずにスラッシュで全て表現する方法のことを指します。
動的ページURL:https://www.example.com/list/?pets=cats
静的ページURL:https://www.example.com/pets/cats/
ときどき、SEO改善を目的として上記のような施策を耳にするのですが、他のSEO施策に比べると大がかりな内容なので、「どのような効果があるのか?」というのが気になったので調べてみました。
公式ドキュメントでは「類似トピックのページをディレクトリにまとめる」という項の中で説明されていました
「特にディレクトリ(フォルダ)を使って類似のトピックをまとめていると、各ディレクトリ内の URL が変更される頻度を Google が学習しやすくなります。」(引用)
変更の頻度を学習する???と思っていると、より具体的な例も記載されていました。
「policiesディレクトリ内のコンテンツはめったに変更されませんが、promotionsディレクトリ内のコンテンツはかなり高い頻度で変更されます。Googleはこの情報を学習することで、ディレクトリごとのクロール頻度を変えています」(引用)
確かにサイトポリシーなど変更頻度が低いページと最新プロモーションが頻繁に更新されるページが規則性なくバラバラに配置されているとクロールする方は大変ですね。
公式でも「Googlebotが1つのサイトをクロールできる時間には限界があります※1」と明言されている通り、Googlebotは必ずしもクロールしてくれるわけではないので、特に商品の数だけページが量産されるECサイトは、重要度の高いページが優先してクロールされるよう設計することが重要でしょう。
※1:クロールの一般論より
ここまで読んできて関連コンテンツは同じディレクトリに格納する方が良いというのは分かりました。
しかし、今回焦点を当てているのは動的ページURLで、これらは通常、日々更新されるものなので、
クロールの更新頻度は全て頻繁に来てほしい対象ではないでしょうか?と新たな疑問が浮かびました。
ということでもう少し見てみると、動的URLについては「ファセットナビゲーションURLのクロール管理」という項で説明されていました。ファセットナビゲーションURLのクロール管理
ここで動的URLと呼んでいたものは、上記ページではファセットナビゲーションと呼称されています。
このページでは「ファセットナビゲーションの最適化方法」がいくつか紹介されていますが、
とりわけ「ファセットナビゲーションのベストプラクティス」と、あるべき姿が名言されています。
ここでは、いのいちばん、それも太字で強調した上で&(?を使う前提)を使った通常のクエリパラメータが推奨されています。静的化したURLについても言及されていますが、太字の強調もないですし、なにより「その場合はこれに注意して」という注意点だけで推奨されていません。
色々と注意点は記載されているようですが、公式ドキュメントでは動的URLが推奨されていました。
ではなぜ公式ドキュメントではクエリパラメータが推奨されている一方で「動的ページURLの静的化すべき」という声も根強いのでしょうか。
次のサイトには「昔の検索エンジンはクエリパラメータの読み取りができず、2008年に識別できるようになった」という記載があります。
また、次のGoogleの公式ブログ記事「DynamicURLsvs.staticURLs」も2008年に公開されたものなのですが、こちらでは「動的なURLはクロールできない=誤解である」と記載があり、静的URLへの変更も推奨されていません。
このことから2008年を起点にクエリパラメータのクロールは徐々に改善されてきている、と考えられます。最近の「静的URL VS 動的URL」関連の記事を読むと、SEO専門のページでも「静的URL/動的URL、どちらでも良い」という記事が多いように思えますので、あくまで個人の予想ではありますが、「静的URL=SEOに有利」というのは過去の名残なのではないかと考えられます。
※「2008年を起点に改善」と記載しましたが、2010年代の記事では、静的URLにした結果改善されたというのも目にするため、即時100%改善された、というよりは徐々に改善されてきて、最近「静的URL/動的URL、どちらでも良い」といえる状況になったのでは、と思われます。
今回は動的URLの静的化をSEOの観点で見ていきました。
ここでの結論としては静的URLは特に推奨されていない、としていますが、動的URLを採用するにしても、クローラーのリソースを食いつぶさないよう、SEO的は工夫が必要になります。また、URLが短くシンプルな静的化されたURLは、拡散する際にユーザーフレンドリーであるなど、別視点でもメリットはありますので、目的によっては採用する必要もあるかと思います。
それでも個人的には(条件の数にもよりますが)URLを静的化するよりは、動的URLのまま工夫をする方が実装的には現実的かと思います。どちらを採用するにせよ、動的URLはECサイトでは重要なページに位置付けられますので、きちんと設計する必要がありますね。
また気になることがあればまとめていきたいと思います。
PHPでデバックするときに一番手っ取り早いのはver_dump関数ですが、見たい内容を毎回記述する必要があったり、画面表示しない処理(CSVダウンロードなど)だとデバックしにくい面もあります。
そこでXdebugです。
公式:https://xdebug.org/
IDEでブレークポイントをつけた箇所で処理が止まってくれて、変数の中身などを確認することが出来ます。 例えば次の画像のように$idの内容が確認できます。
xdebugの仕組みの概要は以下です。
IDEによってWebサーバーが「呼び出される」のかと思っていましたが、実際は逆で Xdebug(Webサーバー)がIDE に接続しに行くようです。
この辺りは以下の動画が分かりやすかったので、気になる方はご視聴をお勧めします。
#03 – PHP Advanced Debugging With Xdebug- How Xdebug Works
とても便利なのですが、設定が少しややこしいので、今回は現時点で最新のEccube4.3をローカルに構築して、Xdebugの導入してみました!
目次
まずはEccubeのコードを用意します。
EccubeはOSS(オープンソースソフトウェア)で、以下のGitHubから取得できます。
https://github.com/EC-CUBE/ec-cube
コードを適当なディレクトリの配置後、初回インストールを実施するのですが、今回はXdebugの起動を確認すれば良いだけなので、初回インストール状態を残すべく、以下の変更を加えます
docker-compose.yml
volumes:
html-app: # ← ソースコードが含まれるボリューム(初回インストール後も残す)
services:
ec-cube:
volumes:
- html-app:/var/www/html
この状態で初回インストールを行います。
参考:Docker Composeを使用してインストールする
#初回インストールコマンド
# コンテナの起動 (初回のみビルド処理あり)
docker-compose up -d
# 初回はインストールスクリプトを実行
docker-compose exec -u www-data ec-cube bin/console eccube:install -n
ローカルにアクセスできたらOK。
http://localhost:8080/
ルート直下にあるDockerfile、docker-compose.yamlを変更します。
1.Dockerfileの修正
PECLでインストールします。以下を追記します。
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug ## 追記
以下をコメントアウトします。(2度目立ち上げ時に失敗してしまうのでコメントアウト)
#COPY dockerbuild/docker-php-entrypoint /usr/local/bin/
2.php.iniにxdebugの設定を追加
ビルド時に「dockerbuild/php.ini」のファイルをphp.iniに読み込んでいるので、ここに以下の設定を追記します。
[xdebug]
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.client_port = 9003
xdebug.client_host = host.docker.internal
個人的にclient_hostが毎回ハマるポイントです。
Macでは上記の「host.docker.internal」で問題ないらしいのですが、 私の環境(WLS2)だとうまくいきませんでした。
※最近のバージョンではWLS2でも「host.docker.internal」で出来る、という記事もみたのですが、残念ながら私の環境では出来ませんでした。
色々試行錯誤した結果以下の値を設定することで成功しました。
・コマンドプロンプトや PowerShell でipconfigを実行した後の「イーサネット アダプター vEthernet (WSL):IPv4 アドレス 」の値
※ただしこの値は固定ではない(再起動などで変更される可能性がある)ので、他の方法が出来る場合は他の方法を採用した方が良いと思います。
> ipconfig
イーサネット アダプター vEthernet (WSL):
IPv4 アドレス . . . . . . . . . . : 172.**.***.***
仕組みの概要で触れた通り、IDEはXdebugからの通信を待機して受け取る必要があるので、設定が必要です。
1.設定->PHP->サーバ でサーバーを追加する
プラスマークから追加します。
ここでは主にパスマッピングが重要です。
パスマッピングの設定によって「Xdebug 経由でやってくる“/var/www/html/はローカルではこのパスですよ」と伝えることが出来ます。
名前 : 何でも良い。
ホスト : localhostで設定しているがそれ以外の値でも成功する。
ポート : 80で設定しているがそれ以外の値でも成功する。
デバッガー : Xdebug を選択
パスマッピングを使用する : オンにして対応するパスを設定する
設定値を入れたら適用する
2.設定->PHP->デバッグ でXdebugの設定を確認する
デバッグポート: 9000、9003の2つがデフォルトで指定されていますが、これは
Xdebugのポートのデフォルトが、Xdebug2では9000だったのが、Xdebug3で9003に変更されたためのようです。
今回はPHP8でXdebug3になるので、9003のみにして、
あとは「外部接続を受け入れる」にチェックが入っていることを確認して、適用保存します。
※9000,9003の2つが設定されたままでも問題ありません。
3.実行 / デバッグ構成 を設定する
PhpStormヘッダ部に「現在のファイル」となっている部分をクリックすると
「実行構成の編集」というメニューが表示されるのでそれをクリック。
プラスマークから「PHPリモートデバッグ」を追加します。
名前に何でも良いので値を設定し、適用します。
※「IDEキーでデバッグ接続をフィルターする」にチェックを入れる記事が多いですが、今回はチェック無しで出来たのでOFFのまま進めます。
手順1で立ち上げた環境ですが、一度
ec-cube_1コンテナに入り、 php -v で表示される情報の中に「Xdebug」の文字があればインストールされています。
1.PhpStormのヘッダ部にある電話のようなマークをクリックしてリッスンを開始します。
2.PhpStormのヘッダ部にある虫のようなマークをクリックしてデバッグを開始します。
3.ステップさせたいところにブレークポイントを設定して、ブラウザからリクエストを実行。
ステップデバッグが出来るようになると、初回のダイアログが表示されます。
承認し、最初の画像のようにブレークポイントで停止してくれるようになれば設定完了です!
いかがだったでしょうか。
開発を助けてくれるXdebugですが、EccubeだけではなくてPHPであれば利用できますので、
PHPで開発されている方はローカル環境に入れてみてはいかがでしょうか。
※Xdebugはあくまで開発専用の機能なので、本番環境には絶対に入れてはいけません。
Shopify標準では検索は、オンラインストア内で特定の商品、ページ、またはブログ記事を検索して見つけることができるようになっています。
しかし、そもそもブログ機能を使わなかったり、シンプルに商品だけ表示させたい、というケースも多いと思います。
liquid上で検索結果ページのproduct-cardを回している箇所を探し、
{% if item.object_type == ‘product’ %}と絞ることで、ブログ記事を除いた商品のみ表示させることは成功しました。
しかし、検索結果の表示件数には反映されませんでした。
これではユーザーが戸惑ってしまいますよね。
search.results_count や search.results はすべての検索対象(商品・ブログ記事・ページなど)を含んだ数を返すため、表示件数にもブログ記事がカウントされてしまうという問題が起きているようです。
そこで今回は、検索フォームのURLに type=product を加える方法を試してみます。
この方法では search.results_count や search.results に含まれるデータも商品だけになるので、「表示件数に反映されない問題」も解決します。
また、コードはシンプルで1行追加するだけ、フィルタ処理やループの条件は変わらないため、不要になれば元に戻すことも簡単です。
type=product を含める変更:
<input name="type" type="hidden" value="product" />
例:
<form class="search-form" action="/search" method="get">
<input name="q" type="search" placeholder="検索ワードを入力">
<input name="type" type="hidden" value="product">
<button type="submit">検索</button>
</form>
これにより、検索結果ページに商品のみ表示されるようになり、同時に正確な検索結果件数になりました。
注意したい点は、既存リンクやシェアされたURLが type=product を含んでいないと効果がないため、他のページやバナーからのリンクが /search?q=〇〇 のままにならないよう、すべての検索導線で type=product を統一する必要があります。
メリット・デメリットを確認した上で参考にしてみてください。
type=product が使えるケース:
・商品以外の検索結果(記事やページ)が邪魔になっている
・ユーザーに商品だけを探してほしい(例:EC特化の店舗)
・商品数が多く、ブログやページはほとんど活用していない
避けた方がいいケース:
・ブログ記事や固定ページに重要なコンテンツが多い
・検索で商品以外のコンテンツ「ガイド」「配送方法」などを探す人が多い
・SEO対策でブログ記事がSEOや販促で活用されている