EC-CUBEでポップアップを実装・カスタマイズする方法【プラグイン開発完全ガイド】

EC-CUBEで「カート離脱を防ぎたい」「キャンペーンを効果的に訴求したい」と考えたことはありませんか。ポップアップは適切なタイミングで表示することで、コンバージョン率の向上に大きく貢献します。しかし、EC-CUBEでポップアップを実装するには、プラグイン開発やTwigテンプレートの知識が必要です。この記事では、EC-CUBEでポップアップを実装・カスタマイズする具体的な方法を、プラグイン開発の基礎から実践的なカスタマイズ例まで詳しく解説します。開発経験が浅い方でも理解できるよう、手順を丁寧に説明していますので、ぜひ最後までお読みください。

離脱防止ポップアップのデータプッシュを試して、CV数を向上させませんか?
[今すぐ無料で試してみる]


EC-CUBEのポップアップ実装で知っておくべき基礎知識

EC-CUBEでポップアップが必要な主なシーン

EC-CUBEでポップアップを活用すべきシーンは、ユーザーの購買行動を促進したい場面です。具体的には、新着商品やキャンペーン告知、クーポン配布、会員登録促進、カート離脱防止など、多岐にわたります。

まず、新着商品やキャンペーン告知では、トップページにアクセスしたユーザーに対して、タイムリーな情報を視覚的に訴求できます。通常のバナー表示よりも目立つため、クリック率が向上します。

クーポン配布や会員登録促進では、初回訪問者や会員登録していないユーザーに対して、特別なオファーを提示することで、会員登録を促すことができます。ポップアップで「初回限定10%OFFクーポン」などを表示することで、購買のハードルを下げます。

カート離脱防止では、ユーザーがサイトから離脱しようとする際に、ポップアップで引き留めるメッセージやクーポンを表示します。マウスカーソルがブラウザの上部に移動したタイミングで表示することで、離脱率を下げることが可能です。

購入完了通知では、注文完了後にサンクスメッセージやSNSシェアボタンをポップアップ表示することで、リピート購入や口コミ拡散を促進できます。

  • 新着商品・キャンペーン告知:トップページ訪問時に最新情報を訴求
  • クーポン配布・会員登録促進:初回訪問者に特別オファーを提示
  • カート離脱防止:離脱しようとするユーザーを引き留める
  • 購入完了通知:注文後のエンゲージメント向上

EC-CUBE 4.xのポップアップ実装方式の選び方

EC-CUBE 4.xでポップアップを実装する際は、プラグイン開発方式と/app/Customizeフォルダ利用方式の2つの主要な方法があります。それぞれの特徴を理解して、目的に応じた実装方式を選びましょう。

プラグイン開発方式は、EC-CUBEの標準機能を改変せずに新機能を追加できる方法です。メリットとして、本体のソースコードを直接編集しないため、EC-CUBEのバージョンアップ時に影響を受けにくく、メンテナンス性が高い点があります。また、プラグインとしてパッケージ化することで、他のEC-CUBEサイトでも再利用可能です。デメリットとしては、開発の初期設定やフォルダ構成の理解に時間がかかること、プラグインでは言語ファイルの上書きができない点があります。

/app/Customizeフォルダ利用方式は、プラグインと同様の拡張が可能で、プラグインとの違いは言語ファイルの上書きが可能な点です。メリットは、プラグインよりも手軽に実装でき、言語ファイルのカスタマイズが必要な場合に便利です。デメリットは、プラグインのようにパッケージ化して再利用することが難しい点です。

バージョン別の互換性については、EC-CUBE 4.0〜4.1系と4.2〜4.3系でプラグインの記述方法が異なるため、対象となるEC-CUBEのバージョンを確認してから開発を始める必要があります。

実装方式の選び方

選択基準プラグイン開発/app/Customize
小規模カスタマイズ
大規模機能追加
再利用性×
言語ファイル上書き×
メンテナンス性

プラグイン開発によるポップアップ実装方法【推奨】

プラグイン開発の準備と環境構築

プラグイン開発を始める前に、適切な環境構築を行うことが重要です。まず、app/Plugin/フォルダ構成を理解しましょう。EC-CUBEのプラグインは、app/Plugin/配下に配置され、各プラグインは独自のフォルダを持ちます。

基本的なプラグインフォルダ構成は以下の通りです。フォルダ名はプラグインの識別子となるため、英数字で命名します(例:PopupPlugin)。

app/Plugin/PopupPlugin/
├── composer.json          # プラグインの基本情報
├── PluginManager.php      # インストール・アンインストール処理
├── Nav.php               # 管理画面メニュー・ブロック登録
├── Controller/           # コントローラファイル
├── Entity/              # エンティティ(データベーステーブル定義)
├── Form/                # フォーム定義
├── Repository/          # リポジトリ(データ取得処理)
├── Resource/            # テンプレートやアセットファイル
│   ├── template/        # Twigテンプレート
│   └── assets/          # CSS、JavaScript
└── Service/             # サービスクラス

composer.jsonの設定では、プラグインの基本情報を記述します。名前、バージョン、説明、作者情報などを定義し、EC-CUBEのバージョン互換性も指定します。

開発環境でのデバッグ設定については、.envファイルでAPP_ENV=devとAPP_DEBUG=1を設定することで、デバッグモードが有効になります。デバッグモードではキャッシュがクリアされるため、ファイル変更が即座に反映され、開発効率が向上します。また、Symfonyのプロファイラーツールも利用でき、画面右下のアイコンからデバッグ情報を確認できます。

ポップアップ用プラグインの基本実装手順

ポップアップ用プラグインの実装は、Nav.phpでTwigBlockを登録し、Twigテンプレートを作成し、JavaScriptで制御する流れです。

まず、Nav.phpでTwigBlockを登録します。Nav.phpは、管理画面のメニュー項目やフロント画面へのブロック追加を定義するファイルです。ポップアップを表示する場合、ページのどの位置に挿入するかを指定します。

<?php
namespace Plugin\PopupPlugin;

use Eccube\Common\EccubeNav;

class Nav implements EccubeNav
{
    public static function getNav()
    {
        return [
            'popup' => [
                'name' => 'ポップアップ管理',
                'icon' => 'fa-bell',
                'children' => [
                    'popup_list' => [
                        'name' => 'ポップアップ一覧',
                        'url' => 'popup_admin_list',
                    ],
                ],
            ],
        ];
    }
}

次に、Resource/template/Block/modal.twigを作成します。このファイルに、ポップアップのHTML構造を記述します。

<div id="popup-modal" class="popup-overlay" style="display:none;">
    <div class="popup-content">
        <button class="popup-close">&times;</button>
        <h2>{{ popup_title }}</h2>
        <div class="popup-body">
            {{ popup_content|raw }}
        </div>
    </div>
</div>

JavaScriptでトリガー制御を実装します。ページ読み込み時、スクロール時、離脱時など、表示タイミングを制御します。

document.addEventListener('DOMContentLoaded', function() {
    const modal = document.getElementById('popup-modal');
    const closeBtn = document.querySelector('.popup-close');
    
    // 3秒後に表示
    setTimeout(function() {
        modal.style.display = 'flex';
    }, 3000);
    
    // 閉じるボタンのクリックイベント
    closeBtn.addEventListener('click', function() {
        modal.style.display = 'none';
    });
});

CSS設計では、position: fixedで画面中央に固定表示し、z-indexで他の要素より前面に表示します。

.popup-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 9999;
}

.popup-content {
    background: #fff;
    padding: 30px;
    border-radius: 8px;
    max-width: 500px;
    position: relative;
}

.popup-close {
    position: absolute;
    top: 10px;
    right: 10px;
    background: none;
    border: none;
    font-size: 24px;
    cursor: pointer;
}

プラグインのパッケージング・公開方法

プラグインを完成させたら、tar.gz形式で圧縮してパッケージ化します。正しい手順でパッケージ化することで、EC-CUBEオーナーズストアへの公開や他サイトへの展開が可能になります。

tar.gz圧縮の正しい手順は、プラグインフォルダ全体をアーカイブすることです。コマンドラインから以下のように実行します。

cd app/Plugin/
tar -czf PopupPlugin.tar.gz PopupPlugin/

オーナーズストアへの登録プロセスでは、まずEC-CUBE公式サイトでパートナー登録を行い、プラグイン情報を登録します。プラグインの説明、スクリーンショット、価格設定などを記入し、圧縮したtar.gzファイルをアップロードします。審査を通過すると、オーナーズストアで公開されます。

バージョン管理とアップデート対応では、プラグインのバージョンをcomposer.jsonで管理します。機能追加やバグ修正を行った際は、バージョン番号を更新し、PluginManager.phpでアップデート処理を実装します。これにより、既存ユーザーが安全にアップデートできるようになります。


/app/Customizeフォルダを使った実装方法

Customizeフォルダによる拡張のメリット

/app/Customizeフォルダを使った実装方式は、プラグイン開発よりも手軽にEC-CUBEを拡張できる方法です。特に小規模なカスタマイズや、サイト固有の機能追加に適しています。

本体上書きを避けた安全なカスタマイズが可能です。EC-CUBEのコア機能を直接編集することなく、/app/Customize配下にファイルを配置することで、独自機能を追加できます。これにより、EC-CUBEのバージョンアップ時にカスタマイズ部分が上書きされるリスクを回避できます。

言語ファイルの上書き対応が可能です。プラグイン開発では言語ファイルの上書きができませんが、Customizeフォルダでは可能なため、管理画面やフロント画面の表示文言をカスタマイズしたい場合に便利です。

小規模カスタマイズに最適な理由は、プラグインのような複雑なフォルダ構成やcomposer.json設定が不要で、必要なファイルだけを配置すればよい点です。開発工数を削減できるため、スピーディーな実装が求められる場合に有効です。

Customizeフォルダの主なメリット

  • 本体コードを直接編集しないため、安全性が高い
  • 言語ファイルのカスタマイズが可能
  • プラグインより手軽に実装できる
  • 小規模なカスタマイズに最適

Twigテンプレートでポップアップを実装

Twigテンプレートを編集してポップアップを実装する手順を解説します。/app/template/配下のTwigファイルを編集することで、フロント画面にポップアップを追加できます。

/app/template/デフォルトのTwig編集方法では、まず既存のテンプレートファイルをコピーします。例えば、トップページにポップアップを表示したい場合は、app/template/default/index.twigを編集します。

{% block modal %}…{% endblock %}の活用では、Twigのブロック機能を使ってポップアップ領域を定義します。

{% extends 'default_frame.twig' %}

{% block main %}
    {# 既存のコンテンツ #}
    {{ parent() }}
    
    {% block modal %}
        <div id="campaign-popup" class="modal-overlay">
            <div class="modal-content">
                <button class="modal-close">&times;</button>
                <h2>春の新作キャンペーン</h2>
                <p>今なら全商品10%OFF!</p>
                <a href="{{ url('product_list') }}" class="btn-primary">商品を見る</a>
            </div>
        </div>
    {% endblock %}
{% endblock %}

既存ページへのポップアップ組み込みでは、JavaScript ファイルとCSSファイルも合わせて作成し、app/template/default/assets/配下に配置します。JavaScriptで表示タイミングやクローズ処理を制御し、CSSでデザインを調整します。

この方法は、特定のページだけにポップアップを表示したい場合や、デザインを細かくカスタマイズしたい場合に有効です。

離脱防止ポップアップのデータプッシュを試して、CV数を向上させませんか?
[今すぐ無料で試してみる]


実践的なポップアップカスタマイズ例

トップページ新着商品ポップアップの実装

トップページに訪問したユーザーに対して、新着商品をポップアップで表示する機能を実装します。TwigExtensionを活用することで、データベースから商品情報を取得し、テンプレートに渡すことができます。

TwigExtensionの作成方法では、Twig\Extension\AbstractExtensionを継承したクラスを作成します。

<?php
namespace Customize\Twig\Extension;

use Eccube\Entity\Product;
use Eccube\Repository\ProductRepository;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class NewProductExtension extends AbstractExtension
{
    private $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    public function getFunctions()
    {
        return [
            new TwigFunction('get_new_products', [$this, 'getNewProducts']),
        ];
    }

    public function getNewProducts($limit = 5)
    {
        return $this->productRepository->findBy(
            ['Status' => Product::DISPLAY_SHOW],
            ['create_date' => 'DESC'],
            $limit
        );
    }
}

商品データの取得とテンプレート連携では、上記で作成したTwig関数をテンプレート内で呼び出します。

{% set newProducts = get_new_products(3) %}

<div id="new-product-popup" class="popup-overlay" style="display:none;">
    <div class="popup-content">
        <button class="popup-close">&times;</button>
        <h2>新着商品のご案内</h2>
        <div class="product-list">
            {% for product in newProducts %}
                <div class="product-item">
                    <img src="{{ asset(product.main_list_image|no_image_product, 'save_image') }}" alt="{{ product.name }}">
                    <h3>{{ product.name }}</h3>
                    <p class="price">¥{{ product.getPrice02IncTaxMin|price }}</p>
                </div>
            {% endfor %}
        </div>
    </div>
</div>

表示タイミングの制御(初回訪問時のみ等)では、LocalStorageを使って、ユーザーが一度ポップアップを閉じたら、次回以降は表示しないようにします。

document.addEventListener('DOMContentLoaded', function() {
    const modal = document.getElementById('new-product-popup');
    const closeBtn = modal.querySelector('.popup-close');
    const storageKey = 'newProductPopupShown';

    // 初回訪問かチェック
    if (!localStorage.getItem(storageKey)) {
        setTimeout(function() {
            modal.style.display = 'flex';
        }, 2000);
    }

    closeBtn.addEventListener('click', function() {
        modal.style.display = 'none';
        localStorage.setItem(storageKey, 'true');
    });
});

カート離脱防止ポップアップの実装

カート離脱防止ポップアップは、ユーザーがサイトから離脱しようとする際に、引き留めるメッセージやクーポンを表示する機能です。コンバージョン率向上に直結する重要な施策です。

JavaScriptでの離脱検知処理では、マウスカーソルがブラウザの上部に移動したタイミング(離脱の兆候)を検知します。

let exitIntentTriggered = false;

document.addEventListener('mouseout', function(e) {
    // マウスがウィンドウ外に出た(上部に移動した)場合
    if (e.clientY < 0 && !exitIntentTriggered) {
        showExitPopup();
        exitIntentTriggered = true;
    }
});

function showExitPopup() {
    const modal = document.getElementById('exit-intent-popup');
    modal.style.display = 'flex';
}

クーポンコード表示機能の追加では、離脱防止ポップアップ内に特別なクーポンコードを表示します。

<div id="exit-intent-popup" class="popup-overlay" style="display:none;">
    <div class="popup-content">
        <button class="popup-close">&times;</button>
        <h2>ちょっと待って!</h2>
        <p>今だけ特別に、<strong>10%OFFクーポン</strong>をプレゼント!</p>
        <div class="coupon-code">
            <span class="code">SAVE10</span>
        </div>
        <p class="small">※このクーポンはカート画面で入力してください</p>
        <button class="btn-primary" onclick="continueToCart()">カートに戻る</button>
    </div>
</div>

LocalStorageを使った表示回数制限では、同じユーザーに何度もポップアップを表示しないようにします。

const POPUP_LIMIT = 3; // 最大表示回数
const STORAGE_KEY = 'exitPopupCount';

function showExitPopup() {
    let count = parseInt(localStorage.getItem(STORAGE_KEY) || '0');
    
    if (count < POPUP_LIMIT) {
        const modal = document.getElementById('exit-intent-popup');
        modal.style.display = 'flex';
        localStorage.setItem(STORAGE_KEY, String(count + 1));
    }
}

注文フォームにカスタム項目を追加

注文フォームにカスタム項目を追加する場合、OrderTypeExtensionを使ってフォームを拡張します。例えば、「ギフト用ラッピング」や「配送希望時間」などの項目を追加できます。

OrderTypeExtensionの作成では、Symfony\Component\Form\AbstractTypeExtensionを継承したクラスを作成します。

<?php
namespace Customize\Form\Extension;

use Eccube\Form\Type\Shopping\OrderType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;

class OrderTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('gift_wrapping', ChoiceType::class, [
            'label' => 'ギフトラッピング',
            'choices' => [
                '希望しない' => 0,
                '簡易ラッピング(無料)' => 1,
                'プレミアムラッピング(+500円)' => 2,
            ],
            'expanded' => false,
            'multiple' => false,
            'required' => false,
        ]);
    }

    public static function getExtendedTypes(): iterable
    {
        return [OrderType::class];
    }
}

データベース拡張(bin/console eccube:generate:proxies)では、追加した項目をデータベースに保存するために、Entityを拡張します。

<?php
namespace Customize\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation\EntityExtension;

/**
 * @EntityExtension("Eccube\Entity\Order")
 */
trait OrderTrait
{
    /**
     * @var int
     * @ORM\Column(name="gift_wrapping", type="integer", nullable=true)
     */
    private $gift_wrapping;

    public function getGiftWrapping()
    {
        return $this->gift_wrapping;
    }

    public function setGiftWrapping($gift_wrapping)
    {
        $this->gift_wrapping = $gift_wrapping;
        return $this;
    }
}

Entityを追加・変更した後は、以下のコマンドを実行してプロキシクラスを生成します。

php bin/console eccube:generate:proxies
php bin/console cache:clear --no-warmup

バリデーション設定では、入力値の妥当性を検証するルールを追加します。

use Symfony\Component\Validator\Constraints as Assert;

$builder->add('gift_wrapping', ChoiceType::class, [
    'constraints' => [
        new Assert\Choice(['choices' => [0, 1, 2]]),
    ],
]);

Symfonyサービスを活用した高度なカスタマイズ

サービスコンテナでデータ取得を拡張

Symfonyのサービスコンテナを活用することで、複雑なビジネスロジックをカプセル化し、再利用性の高いコードを実装できます。ポップアップ表示に必要なデータ取得処理をサービスとして実装します。

カスタムサービスの登録方法では、app/config/services.yamlにサービス定義を追加します。

services:
    Customize\Service\PopupService:
        public: true
        arguments:
            - '@Eccube\Repository\ProductRepository'
            - '@Eccube\Repository\CustomerRepository'

リポジトリパターンの実装では、データベースアクセスをリポジトリクラスに集約します。

<?php
namespace Customize\Service;

use Eccube\Repository\ProductRepository;
use Eccube\Repository\CustomerRepository;

class PopupService
{
    private $productRepository;
    private $customerRepository;

    public function __construct(
        ProductRepository $productRepository,
        CustomerRepository $customerRepository
    ) {
        $this->productRepository = $productRepository;
        $this->customerRepository = $customerRepository;
    }

    public function getPersonalizedPopupData($customerId)
    {
        $customer = $this->customerRepository->find($customerId);
        
        // 顧客の購入履歴に基づいたおすすめ商品を取得
        $recommendedProducts = $this->productRepository->getRecommendedProducts($customer);
        
        return [
            'customer_name' => $customer->getName01(),
            'recommended_products' => $recommendedProducts,
        ];
    }
}

API連携によるリアルタイムデータ表示では、外部APIから取得したデータをポップアップに表示します。例えば、在庫状況や配送予定日などのリアルタイム情報を表示できます。

public function getRealTimeStockInfo($productId)
{
    $client = new \GuzzleHttp\Client();
    $response = $client->request('GET', 'https://api.example.com/stock/' . $productId);
    $data = json_decode($response->getBody(), true);
    
    return [
        'stock_quantity' => $data['quantity'],
        'estimated_delivery' => $data['delivery_date'],
    ];
}

イベントリスナーでポップアップ表示を制御

EC-CUBEのイベントシステムを活用することで、特定のタイミングでポップアップを動的に表示できます。ユーザー属性や行動履歴に基づいた出し分けが可能になります。

eccube.event.render系イベントの活用では、ページレンダリング時に発火するイベントをフックして、ポップアップのHTML を挿入します。

<?php
namespace Customize\EventListener;

use Eccube\Event\TemplateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PopupEventListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            'index.twig' => 'onRenderIndex',
        ];
    }

    public function onRenderIndex(TemplateEvent $event)
    {
        $parameters = $event->getParameters();
        
        // ログインユーザーのみにポップアップを表示
        if (isset($parameters['is_customer'])) {
            $popupHtml = $this->renderPopupHtml($parameters);
            $event->addSnippet($popupHtml);
        }
    }

    private function renderPopupHtml($parameters)
    {
        return '<div id="member-popup">会員様限定キャンペーン実施中!</div>';
    }
}

ユーザー属性による出し分けでは、会員ランクや購入履歴に基づいて、表示するポップアップの内容を変更します。

public function onRenderIndex(TemplateEvent $event)
{
    $customer = $event->getParameter('Customer');
    
    if ($customer) {
        $purchaseCount = $customer->getBuyTimes();
        
        if ($purchaseCount === 0) {
            // 初回購入者向けポップアップ
            $event->addSnippet($this->renderFirstTimeBuyerPopup());
        } elseif ($purchaseCount >= 5) {
            // VIP会員向けポップアップ
            $event->addSnippet($this->renderVipPopup());
        }
    }
}

A/Bテスト実装のベストプラクティスでは、ポップアップのデザインやコピーを複数パターン用意し、効果測定を行います。

public function onRenderIndex(TemplateEvent $event)
{
    $sessionId = session_id();
    $variant = (crc32($sessionId) % 2 === 0) ? 'A' : 'B';
    
    if ($variant === 'A') {
        $event->addSnippet($this->renderPopupVariantA());
    } else {
        $event->addSnippet($this->renderPopupVariantB());
    }
    
    // Google Analyticsにバリアント情報を送信
    $event->addSnippet("<script>gtag('event', 'popup_variant', {'variant': '{$variant}'});</script>");
}

ポップアップのデザイン・UX最適化

レスポンシブ対応のCSS設計

ポップアップのデザインは、モバイル・タブレット・PCなど、あらゆるデバイスで適切に表示される必要があります。レスポンシブ対応のCSS設計を行うことで、ユーザー体験を損なわないポップアップを実装できます。

モバイル・タブレット・PCでの表示調整では、メディアクエリを使ってデバイスごとにサイズや配置を変更します。

.popup-content {
    background: #fff;
    padding: 30px;
    border-radius: 8px;
    max-width: 500px;
    width: 90%;
}

/* タブレット */
@media (max-width: 768px) {
    .popup-content {
        max-width: 90%;
        padding: 20px;
    }
}

/* スマートフォン */
@media (max-width: 480px) {
    .popup-content {
        max-width: 95%;
        padding: 15px;
        font-size: 14px;
    }
    
    .popup-content h2 {
        font-size: 18px;
    }
}

背景オーバーレイの実装(opacity設定)では、ポップアップ背景に半透明の黒いオーバーレイを配置します。

.popup-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.popup-overlay.show {
    opacity: 1;
}

閉じるボタンのアクセシビリティ対応では、キーボード操作やスクリーンリーダーでも使いやすいようにします。

<button class="popup-close" aria-label="ポップアップを閉じる" role="button" tabindex="0">
    <span aria-hidden="true">&times;</span>
</button>
// ESCキーで閉じる機能
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        document.getElementById('popup-modal').style.display = 'none';
    }
});

ユーザビリティを損なわないポップアップ設計

ポップアップは適切に設計しないと、ユーザーの閲覧体験を著しく損なう可能性があります。ユーザビリティを重視した設計が重要です。

表示頻度の適切な設定では、同じユーザーに何度も表示しないようにします。一般的には、1日1回または1週間に1回程度が適切です。

const DISPLAY_INTERVAL = 24 * 60 * 60 * 1000; // 24時間(ミリ秒)
const STORAGE_KEY = 'lastPopupDisplayTime';

function shouldShowPopup() {
    const lastDisplayTime = localStorage.getItem(STORAGE_KEY);
    
    if (!lastDisplayTime) {
        return true;
    }
    
    const elapsed = Date.now() - parseInt(lastDisplayTime);
    return elapsed > DISPLAY_INTERVAL;
}

if (shouldShowPopup()) {
    showPopup();
    localStorage.setItem(STORAGE_KEY, String(Date.now()));
}

cookieを使った表示制御では、LocalStorageが使えない環境でもポップアップの表示制御が可能です。

function setCookie(name, value, days) {
    const expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
    document.cookie = `${name}=${value}; expires=${expires}; path=/`;
}

function getCookie(name) {
    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
    return match ? match[2] : null;
}

if (!getCookie('popupShown')) {
    showPopup();
    setCookie('popupShown', 'true', 7); // 7日間有効
}

SEOへの影響を最小化する実装方法では、ポップアップがGoogleのガイドライン違反にならないよう注意します。具体的には、コンテンツを覆い隠さない、簡単に閉じられる、モバイルでは控えめに表示するなどの配慮が必要です。

/* モバイルではポップアップを画面下部に小さく表示 */
@media (max-width: 480px) {
    .popup-overlay {
        align-items: flex-end;
    }
    
    .popup-content {
        max-height: 40vh;
        border-radius: 8px 8px 0 0;
    }
}

トラブルシューティングと注意点

よくあるエラーと解決方法

EC-CUBEのポップアップ実装では、プラグイン認識エラー、Twigテンプレートエラー、JavaScriptの競合など、様々なトラブルが発生する可能性があります。代表的なエラーと解決方法を紹介します。

プラグインが認識されない場合の対処では、まずcomposer.jsonの記述が正しいか確認します。特に、名前空間やバージョン指定が間違っているケースが多いです。

チェックポイント

  • composer.jsonの「name」が正しく設定されているか
  • 「extra.code」がプラグインフォルダ名と一致しているか
  • キャッシュをクリアしたか(bin/console cache:clear –no-warmup)
  • プラグインのインストール・有効化を行ったか

Twigテンプレートエラーのデバッグでは、デバッグモードを有効にしてエラー詳細を確認します。.envファイルで APP_ENV=dev と APP_DEBUG=1 を設定すると、詳細なエラーメッセージが表示されます。

# .env
APP_ENV=dev
APP_DEBUG=1

よくあるTwigエラー

エラーメッセージ原因解決方法
Unknown “xxx” function未定義の関数を使用TwigExtensionに関数を登録
Variable “xxx” does not exist変数が渡されていないControllerで変数を渡す
Unable to find templateテンプレートファイルが見つからないファイルパスを確認

JavaScriptの競合問題の解決では、EC-CUBEがデフォルトで読み込んでいるjQueryと競合するケースがあります。

// jQueryの競合を避ける
(function($) {
    'use strict';
    
    $(document).ready(function() {
        // ポップアップの処理
    });
})(jQuery);

セキュリティ・パフォーマンスの考慮事項

ポップアップ実装時は、セキュリティとパフォーマンスの両面に配慮する必要があります。脆弱性を作り込まないよう、適切なコーディングを心がけましょう。

XSS脆弱性の回避方法では、ユーザー入力をそのまま出力せず、エスケープ処理を行います。Twigテンプレートでは、デフォルトで自動エスケープされますが、rawフィルタを使う場合は注意が必要です。

{# 安全:自動エスケープされる #}
<p>{{ user_input }}</p>

{# 危険:rawフィルタは信頼できるデータのみに使用 #}
<p>{{ admin_content|raw }}</p>

本体ファイル直接編集のリスクは、EC-CUBEのバージョンアップ時に上書きされる点です。カスタマイズはプラグインまたは/app/Customizeフォルダで行い、本体ファイルは編集しないようにしましょう。

本体編集のリスク

  • バージョンアップ時に変更が消える
  • セキュリティパッチが適用できない
  • サポートが受けられない可能性

ページ読み込み速度への影響最小化では、ポップアップのJavaScriptとCSSを最適化します。

// 遅延読み込み:ページ読み込み完了後にポップアップスクリプトを実行
window.addEventListener('load', function() {
    // ポップアップの初期化処理
    initPopup();
});

// 非同期読み込み
const script = document.createElement('script');
script.src = '/path/to/popup.js';
script.async = true;
document.body.appendChild(script);

バージョンアップ時の互換性確認

EC-CUBEのバージョンアップ時には、プラグインの互換性を必ず確認しましょう。特に4.0系から4.1系、4.2系から4.3系への移行では、APIの変更があります。

EC-CUBE 4.0→4.1、4.2→4.3の移行対応では、主に以下の点を確認します。

バージョンアップ時のチェックリスト

  • composer.jsonの「require」セクションでEC-CUBEバージョン指定を更新
  • 非推奨となったAPIやメソッドを使用していないか確認
  • Symfony関連パッケージのバージョン互換性を確認
  • データベーススキーマの変更に対応

プラグイン互換性チェックリストでは、以下の項目を確認します。

# composer.json
{
    "require": {
        "ec-cube/plugin-installer": "~2.0",
        "ec-cube/eccube": "~4.3.0"  # ← バージョンを適切に指定
    }
}

アップデート後の動作検証手順は、テスト環境で十分に確認してから本番環境に適用します。

検証手順

  1. テスト環境でEC-CUBEをアップデート
  2. プラグインを再インストール・有効化
  3. 全ページでポップアップが正常に表示されるか確認
  4. ブラウザの開発者ツールでJavaScriptエラーを確認
  5. 本番環境へ適用

まとめ:EC-CUBEポップアップ実装のベストプラクティス

EC-CUBEでポップアップを実装する際は、目的に応じた実装方法を選択し、ユーザビリティとセキュリティに配慮することが重要です。推奨実装方法は、プラグイン開発方式です。プラグインとして実装することで、EC-CUBEの本体を改変せず、バージョンアップにも対応しやすくなります。小規模なカスタマイズであれば、/app/Customizeフォルダを活用する方法も有効です。

実装時の優先順位チェックリストは以下の通りです。まず、ポップアップの目的を明確にし、表示タイミングや頻度を適切に設定します。次に、レスポンシブデザインを実装し、全デバイスで快適に表示されるようにします。セキュリティ対策としてXSS対策を行い、パフォーマンス最適化として非同期読み込みを実装します。最後に、A/Bテストで効果測定を行い、継続的に改善します。

実装時のベストプラクティス

  • プラグイン開発方式で実装する(再利用性・メンテナンス性が高い)
  • LocalStorageやCookieで表示頻度を制御する
  • レスポンシブデザインで全デバイスに対応
  • XSS対策を徹底し、ユーザー入力を適切にエスケープ
  • パフォーマンスを考慮し、非同期読み込みを実装
  • A/Bテストで効果測定を行い、継続的に改善

参考リソース・公式ドキュメントへのリンク

より簡単にポップアップ機能を導入したい場合は、コーディング不要のポップアップツールも検討する価値があります。DataPushは、EC-CUBEに簡単に導入できる離脱防止ポップアップサービスです。ノーコードで様々なポップアップを実装でき、A/Bテストや効果測定機能も標準搭載しています。

離脱防止ポップアップならのDataPush

✓ 設定わずか5分で完了
✓ クレジットカード登録不要
✓ シンプルなUIとシンプル管理が可能
✓ A/Bテストで計測可能

無料でも利用可能でシンプルな料金体系
DataPushを無料で試す →


よくある質問

EC-CUBEのポップアップ実装で初心者が最も注意すべき点は何ですか?

EC-CUBEのポップアップ実装で初心者が最も注意すべき点は、本体ファイルを直接編集しないことです。EC-CUBEはSymfonyフレームワークをベースにしており、本体ファイルを直接編集すると、バージョンアップ時に変更が上書きされたり、セキュリティパッチが適用できなくなるリスクがあります。

正しい実装方法は、プラグイン開発方式または/app/Customizeフォルダを使った拡張です。プラグインとして実装すれば、本体を改変せずに機能を追加でき、アップデート時も安全です。小規模なカスタマイズであれば、/app/Customizeフォルダに必要なファイルを配置する方法も有効です。

また、セキュリティ対策も重要です。ユーザー入力をそのまま出力すると、XSS(クロスサイトスクリプティング)攻撃の脆弱性が生まれます。Twigテンプレートではデフォルトで自動エスケープされますが、rawフィルタを使う場合は信頼できるデータのみに限定しましょう。

さらに、デバッグモードの活用をお勧めします。.envファイルでAPP_ENV=devとAPP_DEBUG=1を設定すると、詳細なエラーメッセージが表示されるため、問題の特定が容易になります。

初心者向けチェックリスト

  • 本体ファイルを直接編集しない
  • プラグインまたは/app/Customizeで実装
  • XSS対策を徹底する
  • デバッグモードで開発する
  • 公式ドキュメントを参照しながら進める
プラグイン開発と/app/Customizeフォルダ、どちらを選ぶべきですか?

プラグイン開発と/app/Customizeフォルダのどちらを選ぶかは、カスタマイズの規模と目的によって判断します。大規模な機能追加や他サイトでも再利用したい場合はプラグイン開発、小規模で特定サイト専用のカスタマイズは/app/Customizeフォルダが適しています。

プラグイン開発のメリットは、パッケージ化して配布・再利用できる点です。複数のEC-CUBEサイトで同じ機能を使いたい場合や、オーナーズストアで公開したい場合に向いています。また、プラグインとして独立しているため、有効・無効の切り替えが管理画面から簡単に行えます。デメリットは、初期設定やフォルダ構成の理解に時間がかかること、言語ファイルの上書きができない点です。

/app/Customizeフォルダのメリットは、手軽に実装できる点です。プラグインのような複雑な設定が不要で、必要なファイルだけを配置すればよいため、開発工数を削減できます。また、言語ファイルの上書きが可能なため、管理画面やフロント画面の表示文言をカスタマイズしたい場合に便利です。デメリットは、他サイトへの展開が難しい点、プラグインのように管理画面から無効化できない点です。

ポップアップの表示頻度はどのように設定すればよいですか?

ポップアップの表示頻度は、ユーザー体験を損なわないよう慎重に設定する必要があります。一般的には、同じユーザーに対して1日1回または1週間に1回程度が適切です。頻繁に表示しすぎると、ユーザーがサイトから離脱する原因になります。

LocalStorageを使った実装では、最後に表示した日時を保存し、一定期間経過していない場合は表示しないようにします。以下は、24時間に1回だけ表示する実装例です。

const DISPLAY_INTERVAL = 24 * 60 * 60 * 1000; // 24時間
const STORAGE_KEY = 'lastPopupDisplayTime';

function shouldShowPopup() {
    const lastDisplayTime = localStorage.getItem(STORAGE_KEY);
    
    if (!lastDisplayTime) {
        return true; // 初回訪問は表示
    }
    
    const elapsed = Date.now() - parseInt(lastDisplayTime);
    return elapsed > DISPLAY_INTERVAL; // 24時間経過していれば表示
}

if (shouldShowPopup()) {
    showPopup();
    localStorage.setItem(STORAGE_KEY, String(Date.now()));
}

Cookieを使った実装では、ブラウザのCookieに表示履歴を保存します。LocalStorageが使えない環境でも動作します。

function setCookie(name, value, days) {
    const expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
    document.cookie = `${name}=${value}; expires=${expires}; path=/`;
}

function getCookie(name) {
    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
    return match ? match[2] : null;
}

if (!getCookie('popupShown')) {
    showPopup();
    setCookie('popupShown', 'true', 7); // 7日間表示しない
}

ポップアップの種類によっても表示頻度は変えるべきです。キャンペーン告知など重要度が高い場合は1日1回、カート離脱防止ポップアップは訪問ごとに表示、新規会員登録促進は初回訪問時のみ表示、といった具合に使い分けます。

また、ユーザーが「今後表示しない」ボタンをクリックした場合は、永続的に表示を停止する設定も実装しましょう。

表示頻度の推奨設定

  • キャンペーン告知:1日1回
  • カート離脱防止:訪問ごと(ただし最大3回まで)
  • 新規会員登録促進:初回訪問時のみ
  • 新着商品案内:1週間に1回
モバイルでのポップアップ表示はどのように最適化すればよいですか?

モバイルでのポップアップ表示は、画面サイズが小さいため特別な配慮が必要です。Googleのモバイルフレンドリーガイドラインに準拠し、ユーザー体験を損なわないよう最適化しましょう。

まず、ポップアップのサイズ調整が重要です。PCと同じサイズのポップアップをモバイルに表示すると、コンテンツが隠れてしまいます。メディアクエリを使って、モバイルでは画面下部に小さく表示するなどの工夫が必要です。

/* PC */
.popup-overlay {
    display: flex;
    justify-content: center;
    align-items: center;
}

.popup-content {
    max-width: 500px;
    padding: 30px;
}

/* モバイル */
@media (max-width: 480px) {
    .popup-overlay {
        align-items: flex-end; /* 画面下部に配置 */
    }
    
    .popup-content {
        max-width: 100%;
        max-height: 50vh; /* 画面の半分以下に */
        padding: 15px;
        border-radius: 8px 8px 0 0;
    }
}

閉じるボタンは大きくタップしやすいサイズにします。モバイルでは指で操作するため、最低44×44ピクセル以上のタップ領域を確保しましょう。

.popup-close {
    width: 44px;
    height: 44px;
    font-size: 24px;
    /* タップ領域を確保 */
}

表示タイミングも調整が必要です。モバイルユーザーは通信速度が遅い場合があるため、ページ読み込み完了後に表示するようにします。

// ページ完全読み込み後に表示
window.addEventListener('load', function() {
    setTimeout(showPopup, 2000); // 2秒後に表示
});

Googleのガイドラインでは、モバイルでのインタースティシャル広告(ページ全体を覆うポップアップ)に対してペナルティが課される場合があります。特に検索結果からのアクセス直後に表示すると、検索順位に悪影響を与える可能性があります。

モバイル最適化のポイント

  • 画面の50%以下のサイズにする
  • 画面下部に表示する(全画面を覆わない)
  • 閉じるボタンを44x44px以上にする
  • ページ読み込み完了後に表示
  • 検索流入直後の表示を避ける
EC-CUBEでA/Bテストを実装してポップアップの効果測定を行うにはどうすればよいですか?

ポップアップのA/Bテストを実装することで、どのデザインやコピーが最も効果的かを数値で判断できます。EC-CUBEでA/Bテストを行うには、イベントリスナーを活用してランダムにバリアントを振り分け、Google Analyticsなどの解析ツールで効果測定を行います。

まず、ユーザーを2つのグループ(AパターンとBパターン)にランダムに振り分けます。セッションIDを使った振り分け方法が一般的です。

<?php
namespace Customize\EventListener;

use Eccube\Event\TemplateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PopupAbTestListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            'index.twig' => 'onRenderIndex',
        ];
    }

    public function onRenderIndex(TemplateEvent $event)
    {
        $sessionId = session_id();
        $variant = (crc32($sessionId) % 2 === 0) ? 'A' : 'B';
        
        if ($variant === 'A') {
            $event->addSnippet($this->renderPopupVariantA());
        } else {
            $event->addSnippet($this->renderPopupVariantB());
        }
        
        // Google Analyticsにバリアント情報を送信
        $ga_script = "<script>
            gtag('event', 'popup_view', {
                'variant': '{$variant}'
            });
        </script>";
        $event->addSnippet($ga_script);
    }

    private function renderPopupVariantA()
    {
        return '<div id="popup-a" class="popup">パターンA: 今だけ10%OFF!</div>';
    }

    private function renderPopupVariantB()
    {
        return '<div id="popup-b" class="popup">パターンB: 期間限定セール開催中</div>';
    }
}

Google Analyticsでコンバージョンを計測するには、ポップアップ経由の購入を追跡します。

// ポップアップ内のボタンクリック時
document.querySelector('.popup-cta-button').addEventListener('click', function() {
    gtag('event', 'popup_click', {
        'variant': getVariant(), // AまたはB
        'event_category': 'Popup',
        'event_label': 'CTA Click'
    });
});

// 購入完了時(サンクスページ)
gtag('event', 'purchase', {
    'transaction_id': '{{ Order.order_no }}',
    'value': {{ Order.total }},
    'currency': 'JPY',
    'popup_variant': getVariantFromSession() // セッションからバリアントを取得
});

効果測定の指標は、以下の項目を計測します。

A/Bテストで計測すべき指標

  • クリック率(CTR):ポップアップが表示された回数に対するクリック数
  • コンバージョン率(CVR):ポップアップ経由の購入率
  • 離脱率:ポップアップ表示後のサイト離脱率
  • 滞在時間:ポップアップ表示前後の平均滞在時間

統計的有意差を確認するために、最低でも各バリアントで100〜200コンバージョンを取得することを推奨します。サンプル数が少ないと、偶然の結果に左右される可能性があります。

また、複数の要素を同時にテストするのではなく、1つの要素(見出し、ボタンの色、画像など)だけを変更してテストすることで、何が効果を生んだのかを明確に特定できます。

A/Bテスト実施のベストプラクティス

  • セッションIDでランダムに振り分ける
  • Google Analyticsでイベント計測する
  • 統計的有意差を確認する(最低100〜200CV)
  • 1つの要素だけを変更してテストする
  • 最低2週間はテストを継続する

外部参考・引用記事

EC-CUBE 4 開発者向けドキュメント

EC-CUBE 4 プラグイン開発ドキュメント

EC-CUBE カスタマイズ方法|初心者が知っておくべき知識と注意点 / スプレッドワークス

EC-CUBE:ポップアップウィンドウを中央に表示する | ITOBEN STYLE


関連記事

サイトの離脱率にお悩みですか?
適切なタイミングで適切な情報を届けるDataPush(データプッシュ)のポップアップで、離脱を防ぎコンバージョンを最大化。技術者不要で今日から導入できます。

解決策を試してみる(無料)

関連記事

ECサイトの売上を伸ばすポップアップ活用12施策

ECサイトの売上を30%向上させるポップアップツール完全ガイド|おすすめ比較15選と導入成功事例

カゴ落ち対策完全ガイド|ECサイトの売上を最大化する15の実践的手法

化粧品ECの初回限定ポップアップ設計術|CVR向上の5原則と成功事例10選

WooCommerceポップアップ完全ガイド【WordPress実装と売上アップ戦略】