2020年7月22日水曜日

【近日公開】EC-CUBE4系 プラグイン ココから選択


systemkdです。


新作プラグインを近日公開予定と書いた続きです)


少しずつ情報をまとめようと思ってたのですが、投稿を分けるとわけが分からなくなる気がするので、この投稿を育てる(?)ことにします ^^;


ということで、新作プラグインのおさらいから順に。


過去最大のプラグイン(当社比)



今回のプラグインは、私が今まで作成したプラグインの中で最大サイズのプラグインとなります。


今までは、タイムセール機能を追加することができる、「タイムセールPro+」が1位

ついで、ポイント機能の拡張が行える「ポイント機能拡張」が2位

そして、「まとめ買い価格設定」が3位

(開発期間的には、2位と3位が逆転するかな)


だったのですが、超えてきました。


と言っても、サイズも開発期間も機能には関係ないので それは置いといて ^^;



プラグインの名前は 「ココから選択」 プラグイン


プラグインの名称は、「ココから選択」プラグインで、ココから選択して購入ができるプラグインになっている


プラグインの機能は、EC-CUBE4系へ「新しい販売方法を追加する」プラグインとなります。


新しい販売方法を追加ってどういうこととなりますが、それはロゴを見れば分かるかな、

ということでロゴ画像は、これです!







ロゴに機能の概要が書かれていて、分かりやすいですよね ^^;


選べるセット販売 が行えるようになるプラグイン


そうです。EC-CUBEで、選べるセット販売が行えるようになるプラグインです。


セット販売はいくつかプラグインがあるかと思いますが、選べるやつは無いと思います。

(私の検索ワードが悪いだけの可能性もありますが・・・^^;



詳細な機能・できることについは、また次回。

2020年7月21日火曜日

EC-CUBE4系向けプラグイン まとめ買い価格設定プラグイン

systemkdです。

まとめ買い価格設定プラグインの紹介です。

EC-CUBE3系向けにも作成していましたが、4系向けにも作成しています。

まとめ買い価格設定プラグイン


その名の通り、通常の商品価格とは別に、まとめて購入を行った場合の価格を設定できるようになるプラグインです。





具体的には、 商品を5点・10点・20点などまとめて購入を行ってもらえる場合に、販売価格を値引きした状態にすることができるプラグインとなります。

まとめ買い価格の設定は、SKU単位で行えるようになっており、お得感をアピールするための表示も行うことができるようになっています。


まとめ買い価格の設定


まとめ買い価格の設定は、管理画面の商品管理から行えるようになっており、

設定できる項目は、「まとめ買いの数量に対する価格」だけでなく「割引率」も設定できるようになっています。

[規格なし商品]



[規格あり商品]



まとめ買い価格は、数量ごとに複数設定できるようになっています。


これにより、3点まとめて購入であれば 100円引き、5点まとめて購入であれば 150円引きといった設定を行うこともできます。


まとめ買い設定商品の購入について


まとめ買い価格設定を行った商品の購入は通常の商品購入と変わらない流れで行うことができます。


まとめ買い価格が設定された商品の詳細ページを表示した際、自動で「数量」ごとの「単価(税込)」が表示されるようになっています。

3系の同プラグインにはなかったのですが、4系では販促的な部分も考え表示されるようになっています。




また、カート内でも「まとめ買い値引き」によってどのくらいお得になっているかが分かるようになっています。


ちなみに、上記表示が不要だと思う場合は、プラグインの設定よりOFFにすることもできるようになっています。



なお、まとめ買いでの値引き情報は、注文情報としては「値引き」として扱われるようになっております。

まとめ買いの表示は、購入の動線だけではなく、もちろんマイページの注文履歴にも表示sれるようになっています。


直接値引きモードも搭載


先程、まとめ買いでの値引き情報は、注文情報としては「値引き」として扱われると記載しましたが、値引き方式を切り替えることにより、直接値引きモードを利用することも可能です。





直越値引きモードでは、「販売価格 - 値引き」という形ではなく、販売価格を値引いた状態で動作します。






まとめ


EC-CUBE4系で、まとめ買い値引きによる販売が行えるようになるプラグイン



有料プラグインとなっていますが、ショップでボリュームディスカウントを導入したいという方は是非ご確認頂ければと思います。


不明点・気になる部分等ありましたら、お気軽にメールください。


なお、まとめ買い価格設定プラグインと合わせ使うと相乗効果を発揮するプラグインもありますので、それは次回紹介させて頂きたいと思います。


2020年7月20日月曜日

近日EC-CUBE4系の新作プラグインを公開予定

systemkdです。

ほぼ更新とまってます。はい。
まぁ、プラグインに全力投球しているということで m(_ _)m

ということで作ったプラグインの宣伝です。
(完全に更新とまるよりは良いはず!!)

新作プラグインまもなく公開


タイトルの通り近日中にEC-CUBE4系向けの新作プラグインを公開します!

既にストアへの申請は終わっているので、何事もなければ2週間程度で公開できるかと思います。


今回のプラグインは、私が今まで作成したプラグインの中でぶっちぎりで最大(容量)のプラグインになります。

それはそれは壮大なプラグインです!? ^^;


プラグインの名前は?


プラグインの名前は、「ココから選択」です。


そう、ココから選択して購入することができるプラグインです。

(って何それ。。)


結局どんなプラグインなの?


結局どんなプラグインかというと、

EC-CUBE4系へ「新しい販売方法を追加するプラグイン」です!

公開できるまでもう少し日にちがあるので、詳細は次回へ


2020年5月2日土曜日

EC-CUBE4系 オーナーズストアからインストールしたプラグインの独自アップデートを行う


SYSTEM_KDです。

久々のブログ更新です。

今回は、誰向けなのか不明な、EC-CUBE4系でオーナーズストアからインストールしたプラグインを独自アップデートを行う方法です。


オーナーズストアからインストールしたプラグインのアップデートは簡単には行えない


オーナーズストアからプラグインのインストールを行うと、基本的にはプラグインのアップデートはオーナーズストア経由でしか行なえません。

特に問題はないのですが、バグってて今すぐ修正できるパッチを用意しておきたい場合や、独自のカスタマイズを適用したいと言う場合には、差分ファイルを手動適用するということでしか対応できません。

(もしや、ec-cube.coは手動適用もできないんじゃないかな・・)


EC-CUBE3系までのプラグインは、オーナーズストアのマイページからダウンロードできるので、独自プラグインとしてインストールすれば、そっち側でアップデートが可能。 ってそんな使い方はやらないですかね。


まぁ、そんなこんなで4系はアップデートしたいときの回避策がないという不安を解消するために、アップデートする策を考えました。

その策とは・・・


独自プラグインをインストールし、そのプラグインでオーナーズストアからインストールしたプラグインをアップデート


ドン!(大した策じゃない


4系でも、独自プラグインのインストールできるので、そっち側から「オーナーズストアのプラグイン」の方を更新してしまうという作戦です。


と言っても、既にオーナーズストアでインストールされているものを、独自プラグインでインストールしようとしても、それは行えません。


流れとしては、アップデートしたいプラグインの、アップデート後のプラグイン(tar.gz)を含めたプラグインを、独自プラグインとして用意。
そのプラグインをインストールし、有効化する際の処理で、(保持しているプラグインファイルを用いて)独自アップデートしてしまうというものです。

(説明下手くそ)


まぁ、こんなイメージ。



で、コードの方は、

namespace Plugin\PlgPatch; // 環境に合わせて変更


use Eccube\Entity\Plugin;
use Eccube\Exception\PluginException;
use Eccube\Plugin\AbstractPluginManager;
use Eccube\Repository\PluginRepository;
use Eccube\Service\PluginService;
use Eccube\Util\CacheUtil;
use Eccube\Util\StringUtil;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;

class PluginManager extends AbstractPluginManager
{

    /** @var PluginRepository */
    protected $pluginRepository;

    /** @var CacheUtil */
    protected $cacheUtil;

    /** @var PluginService */
    protected $pluginService;

    private $upFileName;

    private $plgCode;

    private const PLG_PATH = __DIR__ . '/Resource/upfile/';

    /**
     * @param array $meta
     * @param ContainerInterface $container
     * @throws PluginException
     */
    public function enable(array $meta, ContainerInterface $container)
    {

        log_info('[PlgPatch]パッチ適用開始');

        $this->initialize($container);

        log_info('[PlgPatch]更新対象プラグインの存在チェック開始');

        $Plugin = $this->getPlugin();

        log_info('[PlgPatch]更新対象プラグインの存在チェック終了');

        // プラグインの手動アップデート
        $this->plgManualUpdate($Plugin);

        log_info('[PlgPatch]パッチ適用完了');
    }

    /**
     * @param ContainerInterface $container
     */
    private function initialize(ContainerInterface $container)
    {
        $this->pluginRepository = $container->get(PluginRepository::class);
        $this->cacheUtil = $container->get(CacheUtil::class);
        $this->pluginService = $container->get(PluginService::class);

        $finder = Finder::create()
            ->in(self::PLG_PATH)
            ->files();

        /** @var \SplFileInfo $item */
        foreach ($finder as $item) {
            $this->upFileName = $item->getFilename();

            $fileName = explode(".", $item->getFilename());
            $this->plgCode = $fileName[0];
        }
    }

    /**
     * @param string $plgCode
     * @return Plugin
     * @throws PluginException
     */
    private function getPlugin($plgCode = "")
    {

        if (empty($plgCode)) {
            $plgCode = $this->plgCode;
        }

        /** @var Plugin $Plugin */
        $Plugin = $this->pluginRepository->findOneBy(['code' => $plgCode]);

        if (!$Plugin) {
            log_error("[PlgPatch]更新対象プラグインがインストールされていません。");
            throw new PluginException("[PlgPatch]更新対象プラグインがインストールされていません。");
        }

        return $Plugin;
    }

    /**
     * @param Plugin $Plugin
     * @param string $upFileName
     * @throws PluginException
     */
    private function plgManualUpdate(Plugin $Plugin, $upFileName = "")
    {

        $fs = new Filesystem();

        if ($upFileName == "") {
            $upFileName = $this->upFileName;
        }

        $upFilePath = self::PLG_PATH . $upFileName;

        /** @var CacheUtil $cacheUtil */
        $cacheUtil = $this->cacheUtil;

        /** @var PluginService $pluginService */
        $pluginService = $this->pluginService;

        // ファイル配置
        $tmpDir = null;
        try {
            $cacheUtil->clearCache();

            $tmpDir = $pluginService->createTempDir();
            $tmpFile = sha1(StringUtil::random(32)) . '.tar.gz';

            log_info('[PlgPatch]プラグインファイルコピー開始', [
                'origin' => $upFilePath,
                'target' => $tmpDir . '/' . $tmpFile,
            ]);

            $fs->copy($upFilePath, $tmpDir . '/' . $tmpFile, true);

            log_info('[PlgPatch]プラグインファイルコピー完了');

            log_info('[PlgPatch]Update', [$Plugin]);

            $pluginService->update($Plugin, $tmpDir . '/' . $tmpFile);

            log_info('[PlgPatch]一時ディレクトリから削除', [$tmpDir]);

            $fs->remove($tmpDir);

            log_info('[PlgPatch]一時ディレクトリから削除完了');

            return;

        } catch (PluginException $e) {
            if (!empty($tmpDir) && file_exists($tmpDir)) {
                $fs = new Filesystem();
                $fs->remove($tmpDir);
            }
            $message = $e->getMessage();
            log_error('[PlgPatch]' . $message);

            throw $e;
        } catch (\Exception $er) {
            // Catch composer install error | Other error
            if (!empty($tmpDir) && file_exists($tmpDir)) {
                $fs = new Filesystem();
                $fs->remove($tmpDir);
            }
            log_error('plugin install failed.', ['original-message' => $er->getMessage()]);
            throw $er;
        }
    }
}

主要部分を全部はっつけてみた。

(わりと多かった・・・githubにするべきだったか)



普通にプラグインを作成する要領で、composer.json を用意して、上記コードをPluginManager.phpとして配置。

Resource/upfile にアップデートを行いたいプラグインを[プラグインコード].tar.tz のファイル名で配置すればOK.


独自プラグインとして、インストールした後、有効化を行うと Resource/upfile に格納したプラグインでアップデートされます。
(※複数は未対応)



注意点

上記で、そこそこ上手く更新できると思いますが、注意点がいくつかあります。

1. ファイルは上書きで配置される

もとのプラグインファイルに対して、更新用プラグインが上書きされます。
ファイルの置き換えのみであれば、問題ないのですがファイルを削除している場合は消えてくれないので注意。


2. スキーマがアップデートされない

次期バージョン(4.0.4)で解決されるはずですが、4.0.3までは独自プラグインのアップデートでは、スキーマ更新されないので、テーブルに変更を行った場合このアップデートだけでは反映されません。

残念・・・

といいつつ、これは対応策があります。

更新用に独自プラグインとしてインストールしたプラグインをアンインストールするだけです。

プラグインのアンインストールを実施すると、スキーマアップデートが流れてくれるので、その処理の巻き添え(良い意味で)でアップデートされます。

更新用プラグインは、アップデートが終わると必要ないですし、アンインストールまでが一連の更新処理ということで。


3.バージョンは変えない方が良い

アップデート対象のプラグインのバージョンを変えてしまうと、次のアップデートできちんとアップデートされない可能性があるので、アップデート対象プラグインのバージョン番号は変えずに、プラグイン名に、変更版である旨を追記するのが良いです。


以上。

2019年8月3日土曜日

Pluginで追加したEntityを別Pluginにて拡張

SYSTEM_KDです。




EC-CUBE4でのプラグイン開発時メモです。


EC-CUBE4系では、traitを用いてEntityを拡張できるようにEntityの拡張機構が用意されています。


※Entity拡張機構


これによって、プラグインから本体のEntityを拡張することができるのですが、プラグインで追加したEntityを別のプラグインから拡張しようとして、微妙にハマってしまったとメモになります。



プラグインで追加したEntityをプラグインで拡張


やり方としては、本体のEntityを拡張するときと同様。
アノテーションで適用するEntityを指定する際に、プラグインのEntityを指定するだけ。
/**  
 * Trait LabTrait 
 * @Eccube\EntityExtension("Plugin\TargetPlugin\Entity\TargetClass")  
 */
trait LabTrait  
{  
  public function getDummy()  
  {
    return "xxx";  
  }  
}

ただ、拡張される側のEntityを普通に用意しただけだと、Entity重複してるよと怒られるので、本体側のEntityと同じように、クラスが存在してるかチェックで囲むようにします。

※これを実施しておかないと、proxy生成は成功するけど生成されたファイルを削除しないと、それ以降どうしようもなくなってしまうので注意

if (!class_exists('\Plugin\TargetPlugin\Entity\TargetClass')) {
    /**
     * TargetClass
     *
     * [略]
     */
    class TargetClass extends \Eccube\Entity\AbstractEntity
    {
      ・・・
    }
}

これでOKっと思ったけど、ここがハマりポイントでした。

これだと、bin/console eccube:generate:proxies を実行した際にエラーが発生してしまいました。

プラグインのEntityは拡張できないのかなと思いつつ、EC-CUBEの拡張機構部分のコードを追ってみると、}の後に改行が欲しそうな雰囲気だったので、付け足してみたら解決しました。

(実は環境の問題とかって可能性もあるのかな・・・)

if (!class_exists('\Plugin\TargetPlugin\Entity\TargetClass')) {
    /**
     * TargetClass
     *
     * [略]
     */
    class TargetClass extends \Eccube\Entity\AbstractEntity
    {
      ・・・
    }
}
// ← 最後の空行部分

ということで、Pluginで追加したEntityを別Pluginにて拡張する際のメモでした。