[ECCUBE4] OrderTypeに項目追加したいんだけど…

ECCUBE4.0.3でこちらと同じ問題に遭遇してしまいました。

ShoppingController.php内の以下のコード部分でnullが入るようです。
(略)
public function checkout(Request $request)
(略)
var_dump($form->getViewData()); //☆この部分ではnullはなし
$form->handleRequest($request);
var_dump($form->getViewData());exit; //☆この部分でnullが代入される

注文手続き画面に項目追加したい

Customize\Form\Extension\Shopping\OrderTypeExtension で項目を追加したのに、そして注文確認画面まではちゃんとその追加項目を取り回しできているのに、checkout$form->handleRequestでnullが入っちゃって、結果、注文情報に保存されない…

どうにもわからなかったので力業で対応してみました

app/Customize/Form/Extension/Shopping/OrderTypeExtension.php
(ここではreceipt_nameという項目を追加しています)

<?php

namespace Customize\Form\Extension\Shopping;

use Eccube\Common\EccubeConfig;
use Eccube\Form\Type\Shopping\OrderType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Validator\Constraints as Assert;

class OrderTypeExtension extends AbstractTypeExtension
{
    private $eccubeConfig;
    
    public function __construct(EccubeConfig $eccubeConfig)
    {
        $this->eccubeConfig = $eccubeConfig;
    }
    
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $config = $this->eccubeConfig;
        
        
        $builder
            ->add('receipt_name', TextType::class, [
                'required' => false,
                'constraints' => [
                        new Assert\Length( ['min' => 0, 'max' => $config['eccube_name_len'] ] )
                ],
            ])
            ->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit'])
            ;
    }
    
    public function onPreSubmit(FormEvent $event)
    {
        // ShoppingControllerのcheckoutでhandleRequestの後にreceipt_nameが消える問題の対応
        $data = $event->getData();
        $receipt_name = $event->getForm()->get('receipt_name')->getData();
        if( !isset($data['receipt_name']) ){
            $data['receipt_name'] = $receipt_name ;
        }
        $event->setData($data);
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return OrderType::class;
    }
}

PRE_SUBMITというイベントでformの中身を確認して、receipt_nameがあれば入れ直しています

わざわざこんなソースを書かないといけないのは、きっと何かおかしい…
なにか大きな勘違いをしてるのかもしれないけど、でもとりあえずこれで問題は回避できました

[twig] twigで再帰呼び出し

twigで再帰呼び出しができると知りました
そのような処理が必要なときはできるだけPHPで前処理してテンプレートに渡すようにしてましたが、twigで処理できるならそのほうがスマートな場合も多そうです

きっかけはECCUBE4のソース
カテゴリを表示している場所で再帰呼び出しをしています

src/Eccube/Resource/template/default/Block/category_nav_pc.twig

{% set Categories = repository('Eccube\\Entity\\Category').getList() %}

{% macro tree(Category) %}
    {% from _self import tree %}
    <a href="{{ url('product_list') }}?category_id={{ Category.id }}">
        {{ Category.name }}
    </a>
    {% if Category.children|length > 0 %}
        <ul>
            {% for ChildCategory in Category.children %}
                <li>
                    {{ tree(ChildCategory) }}
                </li>
            {% endfor %}
        </ul>
    {% endif %}
{% endmacro %}

{# @see https://github.com/bolt/bolt/pull/2388 #}
{% from _self import tree %}

<div class="ec-categoryNaviRole">
    <div class="ec-itemNav">
        <ul class="ec-itemNav__nav">
            {% for Category in Categories %}
                <li>
                    {{ tree(Category) }}
                </li>
            {% endfor %}
        </ul>
    </div>
</div>

上記のソースでは前半部分でmacroを定義して、後半でそれを一回呼び出してます
呼びされたmacroの中でさらにまた自分自身を呼び出すことで、カテゴリの深さのぶんだけ再帰的に処理されるようになっています

[PHP] calcurate UPC-E check digit

最近バーコード絡みの案件があるのでちょくちょくバーコードネタが出てきます
今日はUPC-E(8桁短縮タイプ)のチェックデジットを計算する関数を作ってみました

そんなライブラリどこかに転がってるだろ、と思っていたのですが、これが意外に見つからない・・・
UPC-A対応ならあるのですが、8桁短縮コードは考慮されていない事が多かったです
探し方が悪かったのかな?
とにかく、見つけられなかった以上、自作します

基本的にはJAN(=EAN)であろうとUPCであろうとModulus10/Weight3と呼ばれる方法でチェックデジットは計算されています
これはざっくり言うと、奇数桁の合計×3 + 偶数桁の合計 を10で割った余りを10から引いたもの、です

日本語で書くよりソースコードのほうがわかりやすそうです

function calc_checkdigit($code)
{
    if( !ctype_digit($code) ) return false;
    $code = (string)$code;
    $odd_total  = 0;
    $even_total = 0;
    
    for($i=0; $i<strlen($code); $i++)
    {
        if((($i+1)%2) == 0) {
            // 偶数行合計
            $even_total += $code[$i];
        } else {
            // 奇数行合計
            $odd_total += $code[$i];
        }
    }
 
    // 奇数桁の合計×3 + 偶数桁の合計
    $sum = (3*$odd_total) + $even_total;
    // 10で割った余りを求める
    $remainder = $sum%10;
    // 求められた余りを10から引いた値がチェックデジット(10の場合は0)
    return( (10-$remainder)%10 );
}

で、問題はここから

UPC-Eの場合は一度UPC-A(12桁の通常タイプ)に変換してから計算するという仕様なのですが、この変換がちょっとややこしい
UPC-Eの8桁のうち先頭の1桁は0で固定されており、また末尾1桁はチェックデジットなので、意味のある文字列は2桁目から7桁目までの6文字になるのですが、その末尾(全体の7桁目)によって変換の方法が変わるという仕様です

まとめた資料がcanonさんにありました
UPC短縮バーコードチェックデジット計算方法

よく見るとそこまで複雑な話ではないのですが、ぱっと見じゃ理解できないですよね…

PHPで処理を書くとこうなります

function upce2upca($code)
{
    if( !ctype_digit($code) ) return false;
    $code = (string)$code;
    $len = strlen($code);
    if( $len != 7) return false;
    
    switch($code[6])
    {
        case '0':
        case '1':
        case '2':
            $ret = substr($code,0,3).$code[6].'0000'.substr($code,3,3);
            break;
        case '3':
            $ret = substr($code,0,4).'00000'.substr($code,4,2);
            break;
        case '4':
            $ret = substr($code,0,5).'00000'.$code[5];
            break;
        default:
            $ret = substr($code,0,6).'0000'.$code[6];
            break;
    }
    return $ret;
}

さきほどのModulus10/Weight3の計算と合わせて使うことでUPC-Eのチェックデジット計算ができます

// チェックデジット計算前の7桁
$raw_upce = '0123456'; 

// UPC-A(チェックデジット加える前の11桁)に変換
$raw_upca = upce2upca($raw_upce );

// チェックデジットを計算
$cd = calc_checkdigit($raw_upca); // 5

// 有効な8桁UPC-Eを作る
$valid_upce = $raw_upce.$cd; // 01234565

[PHP] PDFlibでImage_Barcode2で作ったJANのバーコードを貼り付ける

昨日の続きです。

Image_Barcode2で作ったバーコードをPDFに貼り付けたい。
この案件ではPDF作成にPDFlibを使っています。

// $this->pdflib はPDFlibオブジェクト

protected function pdf_set_barcode($jancode, $x, $y, $opt='')
{
    $barcode_height = 30;
    $barcode_width = 1;
    $barcode_with_num = true;
    $pvf_filename = $jancode;
    
    ob_start(); 
    $gd = Image_Barcode2::draw($jancode, 'ean13', 'png', false, $barcode_height, $barcode_width, $barcode_with_num);
    imagepng($gd, null);
    imagedestroy($gd);
    $this->pdflib->create_pvf($pvf_filename, ob_get_clean(), "");
    $barcode_image = $this->pdflib->load_image('png', $pvf_filename, '');
    $this->pdflib->fit_image($barcode_image, $x, $y, $opt);
    $this->pdflib->close_image($barcode_image);
    $this->pdflib->delete_pvf($pvf_filename);
}

load_imageがファイルを要求しているんだけどいちいちtmpファイル保存するのもなんか気持ち悪いな、って思っていたところ、pvf(PDFlib Virtual file)というのがあったのでそれを使っています。

GDの出力データをob_使わずに取得する方法ないのかな…

[PHP] JANコードをバーコード表示するためにcomposer経由でImage_Barcode2をインストールする

PDF上でJANコードをバーコードを表示したいという案件がありました。
バーコードfontというものがあるようだけど、JANコード=EAN13コードの場合コード変換などが必要でめんどくさそう。
それよりもImage_Barcode2で画像として作っちゃうのが良さそうだったので、これを採用することにしました。
composerを使っている環境だったのでそちらを使ってインストールします。

$ php composer.phar require pear/image_barcode2:dev-master

stableがないということで:dev-masterを付けないとだめだよっていうのが今回のTIPS

続きがあります