Posted on

PHPのちょっとしたパッケージを作ろうと思ったら「ちょっとした」規模にならなかった件

# 長すぎる前置き

LaravelなりCakePHPなり、バニラなものにいろいろと「これはどこでも使うやろ」という基本的な機能を追加したのを「アプリケーション側」に置いたような状態のリポジトリを引き出してから開発を始めてたんです。この長年のやり方に疑問を覚えるような出来事がありまして。

🤔

たとえば、Laravelで使っていた基礎ビューテンプレートがありました。したら、誰かがあるプロジェクトで「なんか埋め込んでたjsが思ったように動かん」ってなって、ビュー本体のほうに追記しちゃった。誰もそんなことすぐにわかるもんじゃないから、あちらが立てばこちらが立たぬ状況が訪れてしまい、基本機能を開発した私に「どうにかして」と話が回ってきたんです。

😱

その時は基礎ビューをもとに戻して、アプリケーション側に必要な変更は基礎ビューを継承する形で対処したんですが、構造的な限界を感じたんです。どこをいじったらどこにどんな影響が出るか、誰もはっきりわからないんです。そりゃそうです。基礎機能を置いた継承元ファイルがほぼプロジェクトで追加するファイルと同じ層にずらりですから、境界もあやふやになる。

😇

ここに「パッケージ」という天啓が降りるまで時間はかかりませんでした。境界がはっきりすれば、誰も「こっから先をいじると他に支障が出そう」ってわかるんじゃない?

🤯

最初は基本機能を丸ごと抜き取ってパッケージにできないかと思ってたんですが、これが異様なほど分量多いのと、「基本機能」と一口で言っても、実態として関係性が薄いいくつかの機能の集まりだったので、一度断念しました。とりあえず間に合わせに「このファイルいじるな」リストだけ書いて共有したんですが…

🙁

そんでもあきらめきれなくて、「とりあえずこの都道府県クラスだけ簡単に抜き出してみよう、練習にはちょうどいいよね」という気持ちで始めたのが…険しい道のりの始まりでした。

# 前置きここまで

今まで使ってた都道府県クラス(PrefDef)、やってることはとてもシンプルで、

  • 都道府県Enum的なもの
  • 地域区分Enum的なもの
  • 都道府県リストを地域区分でフィルタリングしたりしなかったりして配列返す

このぐらいのことだったんです。1クラス。
パッケージ化したらどうなったかって…本体10ファイル+単体テスト6ファイル+αにばらすことになりました。

🤣

パッケージ化するにあたってすぐ、単純かつ異様にめんどいことに気づいたんです。

「デフォルトで中部地方というひとまとまり区分を、甲信越と北陸と東海に分けるシステムがあるかもしれない」

アプリケーションに置きっぱなしのPrefDefなら、中の地域区分を勝手に書き換えてもらってよろしかったんです。
でもパッケージにするなら、さすがに都道府県までいじれるようにしておくのはよろしくない。都道府県だけだったらほぼ絶対変わらないし、もし万が一統廃合があったとして、それはもうパッケージ本体が責任持つべきことですから。
しかし地域区分に関してはそうもいかない。都道府県の変更を阻止しつつ、地域区分だけアプリケーションの都合に合わせられるようにするにはどうしたらいいんだろうってなったんです。

「地域区分だけ設定ファイルを公開して、それを読み込めるようにしよう」

というわけで、こんなのをrequireしてもらったら何とかなるかと思ったんです。

use Okushin\PrefDef\Core\PrefDefStatic as Core; //Core::都道府県という定数がずらりという感覚のクラスを使ってます
return [
    'regions' => [
        'Hokkaido' => [
            'name' => '北海道',
            'prefectures' => [
                Core::HOKKAIDO
            ]
        ],
        //云々
    ]
];

甘かった。実に甘かった。設定ファイルのひな形を作っているときにすら2~3か所”prefectures”を”prefecture”と単数形で書いていたことに気づかなかったから、この設定ファイルをシステムの都合に合わせて調整する人が同じ失敗をしないわけがない!

「そんな時に『やばいよやばいよ』と教えてくれる誰かが要る…」

というわけで、設定ファイルのバリデータもご用意。

use InvalidArgumentException;

class ConfigValidator
{
    public static function validate(array $settings):void{

        //regions:配列必須
        if(!isset($settings['regions']) || !is_array($settings['regions'])){
            throw new InvalidArgumentException("'regions' を配列で指定してください");
        }

        foreach($settings['regions'] as $base_key => $region_settings){
            //name:文字列必須
            if(!isset($region_settings['name']) || !is_string($region_settings['name'])){
                throw new InvalidArgumentException("地域{$base_key}:'name' を文字列で指定してください");
            }
            //prefectures:配列必須
            if(!isset($region_settings['prefectures']) || !is_array($region_settings['prefectures'])){
                throw new InvalidArgumentException("地域{$base_key}:'prefectures' を配列で指定してください");
            }
            //prefecturesの中身:都道府県定数、かつ重複不可
            foreach($region_settings['prefectures'] as $pref_code){
                if(!in_array($pref_code,range(1,47))){
                    throw new InvalidArgumentException("地域{$base_key}:{$pref_code}は正しい都道府県コードではありません");
                }
            }
        }
    }
}

「このパッケージをLaravelだけで使うのもったいないな…素PHPレベルで動けばうれしい…うれしいね…」

Laravelのときは設定ファイルの内容をconfigに流して、それ以外の時はbootstrap.php的なもので設定ファイルを読み込んで、専用の設定掘り出し用クラスを使う…ような流れを考えることになります。

とあるAIに訊いたら、こういう時に『アダプタ』と『ファサード』を作るのだって言ってました。何が言いたいかというと、”設定用クラスのふりをしたもの(ファサード)”を用意して置いて、その実態には環境に応じて”configへの横流し屋(アダプタ)”か”専用の設定掘り出し用クラス”を埋め込んでもらう、みたいなやり口を使うってことです。

// laravelを使っている場合に使う、configへの横流し屋 ~pref_defを添えて~
class LaravelConfigAdapter
{
    public function __construct(){
        ConfigValidator::validate(\config('pref_def'));
    }

    public function get(string $key,$default=null){
        return \config("pref_def.{$key}",$default);
    }
}
//設定用クラスもどき(ファサード) 
namespace Okushin\PrefDef\Facades //ファサード階層にまとめてツッコむことにした
class Config
{
    protected static $instance;

    public static function set($instance):void
    {
        static::$instance = $instance;
    }

    public static function get(string $key, $default = null){
        if (!static::$instance) {
            throw new \RuntimeException('ConfigFacade instance not set.');
        }

        return static::$instance->get($key, $default);
    }
}

この「もどき」を都道府県パッケージ本体に飲ませて、同じget()ながら異なるルートから設定を使えるようにするわけです。

「で、この『もどき』の設定をどこですればええのん?」

サービスプロバイダでした。このパッケージにLaravel向けのサービスプロバイダ書いてあげたら、(そしてcomposer.jsonによしなに書いてあげたら)、
そこに書いてあることを初期化でやってくれるようなのです。(まだここよくわかってないので今後書きます)

//パッケージ側のcomposer.jsonに書くこと
{
    //云々
    "extra":{
        "laravel": {
            "providers": [
                "Okushin\\PrefDef\\Laravel\\PrefDefServiceProvider" //このサービスプロバイダ使えよォォォ!とアピールする
            ]
        }
    }
}
//肝心のサービスプロバイダ
use Okushin\PrefDef;
class PrefDefServiceProvider extends \Illuminate\Support\ServiceProvider
{
    public function boot()
    {
        //やってること1⃣:「artisan君、うちの設定ファイルここにあるねんけど、vendor:publishでここに持ってってくれん?」
        $this->publishes([
            __DIR__ . '/../Config/default.php' => config_path('pref_def.php'),
        ],'pref_def-config');

        //やってること2⃣:「pref_def.php」の内容をconfig()で読めるよう統合しなくちゃ
        $this->mergeConfigFrom(
            __DIR__.'/../../config/pref_def.php','pref_def'
        );

        //やってること3⃣:Configもどきに横流し屋をあてがっておく
        PrefDef\Facades\Config::set(new PrefDef\Config\LaravelConfigAdapter());
    }

    public function register(){

    }
}

素PHPの時は、プロジェクトごとに初期処理で走らすようなbootstrap.phpみたいなものを用意してもらってと

require 'vendor/autoload.php';

use Okushin\PrefDef\Facades\Config;
use Okushin\PrefDef\Config\Config as CoreConfig; //別名を使えばどってことない

$pref_def_config = require __DIR__."../config/pref_def.php"; //pref_def.phpを実際に置いたパスに合わせるべし
Config::set(new CoreConfig($pref_def_config));//Configもどきに実のConfigクラスを飲ませる

こればっかりは自動的に作るわけにもいかんので、READMEにそういう記述を入れとくことにしました。

他にも単体テスト作って通したり、やっぱりPrefDef本体もファサードあるといいよね、みたいになったりしたけれど、本質的に「プラグインを作った」って話はこんなところです。
あとは、アプリケーション側からこのプラグインを取り込む、というところで composer.jsonにいろいろ書いて、composer updateするし、php artisan vendor:publishも忘れずに。

「長い道のりだった…」

もともと1ファイル1クラスだったものも、プラグインとして本気で組んでみると「ユーザーに公開する部分」と「しまい込んで置く部分」の切り分け、その連携についてより解像度を高くする必要がありました。
あと、composerの使い方も。プラグイン側とアプリケーション側、両方の情報がかみ合っていないとcomposerの力が使えないので、phpでパッケージ作るならしっかり書けるようにしたいですね。
とりあえずこの2点だけでも学んだだけ収穫です。

今後も分割できそうな部分プラグイン化して、知見を書いてみたいですが、今回は他の仕事もあるので、とりあえずここまで。

Posted on

Laravelの豪快な丸呑み:ルートモデル結合

ここ1年程、CakePHPからLaravelへの移行が進んでいってたとこだったんです。

私、先発隊としてLaravelの調査とかテンプレートシステムとか作成する命を受けてまして、色々作っていました。(CakePHP4の時もそうだったんですが。)その中で一つ感動したLaraveの仕組みが「ルートモデル結合」というものです。早い話が「そのid数値をテーブルのレコードidとみなしてEloquentモデルにしたやつ先に取ってきとくよ」という気の利いた仕組みです。

そりゃそうです。何かレコードのidを指定してアクションを組んでいるときに、そのアクションでそのidのレコードと関係ない作業しかしないはずはないわけで、取ってきて欲しいし、なかったら適当に404立てておいて欲しいわけです。ルーティングでは結構CakePHPがよしなにやってくれることに対してぶっきらぼうだったのに、こういうところで気が利くのが面白いところです。

ここでやることは

1:コントローラの引数にEloquentモデルのタイプヒントを付ける

namespace App\Http\Controllers;
use App\Models\User;

class UsersController extends Controller{
    //略
    /**
    * ユーザー編集
    */
    public function edit(User $user){
         //なんか処理、既に$user取得済
    }
    //略
}

…たったこれだけ!暗黙の結合とはいうものの、これだけでほとんどのパターンがカバーできてるのが偉すぎる!

これだけのことで、毎回idから今回の主題となるレコードとその関連レコードをむにゃむにゃ…ということを全部Laravelがやってくれてる状態からスタートするんです。urlの見た目は引き続きidと同じだから、今までのソースを邪魔することもあんまりない。
ルートに->missing()を追加すれば、ないときに404じゃなくてリダイレクト返すというのも簡単。idに対応するレコードが存在しているというだけでは不十分で、何らかの条件を満たしたレコードだけアクセスできるっていう条件が必要なら、それはポリシーの出番やね、というところです。もちろんポリシーにも既に手配したレコードが流れてきてくれてるので、チェックもしやすい。いいことづくめ。

「urlが整数値で入ってきてるから、コントローラも整数値だけを受け取っとるやろな」という常識が、職場でも早く覆ってほしい今日この頃です。

Posted on

Hashはいいぞ Hash::map編

気が付いたら前の記事から1年半以上空いてしまいました。それだけみんな忙しかったといえばそうなのですが、ようやっと時間ができたので、何を書こうかなと思ったとき、そういえば近くの人にHash::map教えてないなって思い出したんです。

これ、最初のうちはかなり読みにくい印象あると思うんです。ラムダ関数慣れが要るという感じで…

ここまでくるとforeachと大差ないっちゃ大差ないところもあります。
Hash::reduceともなるとさらに差がなくて、正直どっちで書けばいいか自分でも迷います。

それでも、配列を「ループで一つ一つ確認する」というforeachの見え方と、
配列を「各要素にまとめて適用する」というmapの見え方ってやっぱり違うと思うんです。

// Model->find('all') したときありがちな配列
$data = [
    o => [
        'Color' => [
            'id'=>'1',
            'name'=>ゴールド',
            'red'=>'255',
            'green'=>'215',
            'blue'=>'0',
        ]
    ],
    1 => [
        'Color'=>[
            'id'=>'2',
            'name'=>'シルバー',
            'red'=>'192',
            'green'=>'192',
            'blue'=>'192',
        ]
    ],
    2 => [
        'Color'=>[
            'id'=>'3',
            'name'=>'ブロンズ',
            'red'=>'154',
            'green'=>'98',
            'blue'=>'41',
        ]
    ]
];

//色名と16進コードの配列を作る
$result = Hash::map($data,'{n}.Color',function($item){
    return [ 
        'name' => $item['name'],
        'hex_code' => sprintf("#%02x%02x%02x",$item['red'],$item['green'],$item['blue'])
    ];
});

/**

$result = [
    0 => [
        'name' => 'ゴールド',
        'hex_code' => '#ffd700',
    ],
    1 => [
       'name' => 'シルバー',
       'hex_code' => '#c0c0c0',
    ],
    2 => [
       'name' => 'ブロンズ',
       'hex_code' => '#9a6229',
    ],
];

*/

//やってることは以下のforeachとあんまり差はない
$result_foreach = [];
foreach($data as $color){
    $result_foreach[] = [
        'name' => $color['Color']['name'],
        'hex_code' =>  sprintf("#%02x%02x%02x",$item['Color']['red'],$item['Color']['green'],$item['Color']['blue'])
    ];
}

foreachとあんまり変わらないなら、なぜこういう書き方をするかと考察したとき、いくつか強みっぽいところはあります

深い階層からもパスで引っ張ってきてくれる
深い深い配列の奥にあっても、Hashのパス記法を使えば、何重foreachを使わずとも。
引っ張ってくる個数と結果の配列は要素個数が同じで、キーは0始まり連番
foreachだとソースを読まないとそういう結果の予想は付かない
ラムダ関数の中は独自スコープ
外側と変数名がかぶることを気にしなくていい。外側の変数を使いたいときはfunction($item)use($outside_var)みたいな感じで

なんだかんだ、使ってると手になじんでくるものです。

Posted on

Hashはいいぞ Hash::combine編

去年からだいぶHash::なんとかを使い続けて、だいぶ手に馴染んできました。foreachのほうが分かりやすい場合は、素直にforeachを回したらいいんですが、select用のリストをfind結果の複数のカラムから作る、みたいなことにHash::combineを使えると非常に見通しが良いです。なんてったって「配列のこれをキーとして、これを値とする配列を作る」ですもの。

// Model->find('all') したときありがちな配列
$data = [
    o => [
        'Color'=>[
            'id'=>'960018',
            'name'=>'カーマイン',
            'red'=>'150',
            'green'=>'2',
            'blue'=>'24',
        ]
    ],
    1 => [
        'Color'=>[
            'id'=>'00896B',
            'name'=>'ビリジアン',
            'red'=>'0',
            'green'=>'136',
            'blue'=>'53',
        ]
    ],
    2 => [
        'Color'=>[
            'id'=>'434DA2',
            'name'=>'ウルトラマリン',
            'red'=>'67',
            'green'=>'77',
            'blue'=>'162',
        ]
    ]
];

// [id => 色名]
$result_1 = Hash::combine($data,'{n}.Color.id','{n}.Color.name');
/**
$result_1 = [
    '960018'=>'カーマイン',
    '00896B'=>'ビリジアン',
    '434DA2'=>'ウルトラマリン',
];
*/

// [id => 'Rスペース詰め3桁,Gスペース詰め3桁,Bスペース詰め3桁']
$result_2 = Hash::combine($data,'{n}.Color.id',['%3d,%3d,%3d','{n}.Color.red','{n}.Color.green','{n}.Color.blue']);
/**
$result_2 = [
    '960018'=>'150,  2, 24',
    '00896B'=>'  0,136, 53',
    '434DA2'=>' 67, 77,162',
];
※ 配列のキー側引数も配列でsprintf記法指定できる
*/

どっちも、フォームの選択肢を作るとき凄い役に立ちます。
パス記法に慣れると、いくつも組み合わせてprintfの指定をすれば配列のキーも値も自在に組めます。
もうforeachでぐるぐる検索結果を回しては文字列をこねくり回して…みたいなことはしなくていい!

単純に「これとこれとこれを取り出して配列を作りたい!」とき、使ってください。

Posted on

「Effective JavaScript」かいつまみ -基礎編-

翔泳社の「Effective JavaScript」(David Herman著 / 吉川邦夫訳)の一章から、
重要そうな部分をピックアップして箇条書きで要約しました。
太字の部分は要約者の勝手な感想(?)です。
普段開発で使用しているPHPに関して主に追記しています。

第一章 JavaScriptに慣れ親しむ

項目2 JavaScriptの浮動小数点数を理解しよう

・JavaScriptでは、すべての数が倍精度浮動小数点数(いわゆるdouble)である。
・-9,007,199,254,740,992~9,007,199,254,740,992までの整数は全てdoubleで表現する事が可能。
・当然のように、浮動小数点数演算特有の問題が存在。

console.log(0.1 + 0.2); //0.30000000000000004

・少数を扱う際は一度整数にしてから演算して、結果を調整するほうがよさそう。

console.log((0.1 * 10 + 0.2 * 10)/10); //0.3

項目3 暗黙の型変換に注意しよう

・JavaScriptはほとんどの型エラーをスルーして実行してしまう。
・数値演算地の-、*、/、%は、計算を行う前に引数を数値に変換しようとする。
・ただ+だけは例外的な動きをする。
・以下例

console.log(3 + true); //4
console.log("2" + 3); //"23"

・真偽値の型強制。
・ifや||や&&のような演算子は、受け取った値を真偽値に型変換して解釈する。
・JavaScriptでは偽となる値は、false 0 -0 “” NaN null undefined の7つだけ。
・PHPの場合は、”0″ や [] もfalseになるので要注意。

項目5 型が異なるときに==を使わない

・==で値を比較する場合、項目3で既出の暗黙の強制によって両者が数値に変換されて比較される。
・===は型を考慮して比較。
・==のほうが便利な場合もあるのでついつい使ってしまう事もありますが、基本的には===で統一したほうがよさそう。
特にPHPの場合 var_dump(“hoge” == 0)  //結果:true などと直感に反するケースがある(最初が数字でない文字列は0に変換されるため)ので要注意。

項目7 文字列は16ビットの符号単位を並べたシーケンスとして考えよう

・Unicodeが16ビット内でおさまると考えられていた時代にJavaScriptは生まれたため、JavaScriptの文字列の要素は16ビットの符号単位である。(UTF-16形式)
・実際にはUnicodeには16ビットを超える文字も存在し(例えば辰吉じょう一郎のじょう・丈の右上に点)、それらは16ビットの要素2つで表現される。
・そのためUnicodeで16ビットを超える文字の場合は、length や charAt などで文字数と結果が一致しないことになる。

Posted on

Imagickを利用するにあたって

PDFファイルの1ページ目をイメージファイルjpgに変換するサンプルです。
imagickのサーバーへのインストールは、このブログからは除きます。
当初

$im = new Imagick();
//画像を生成したいPDFを読み込む
$im->readImage('hoge.pdf');
//特定のPDFのページ 0が表紙
$im->setImageIndex(0);
//サムネイルサイズ 640pxに収める
$im->thumbnailImage(640, 640, true);
//シャープ
$im->sharpenImage(0, 1);
//生成
$im->writeImage('out.jpg');

$im->destroy();

を元に関数を作成したのですが、
この記述では、階層が深いPDFを指定した場合は、エラーになるという致命的な問題が
潜んでいます。
そこで、ソースを

$im = new Imagick();
//画像を生成したいPDFを読み込む
$image = file_get_contents('/var/www/xxxx/xxxx/hoge.pdf');
$im->readImageBlob($image);
//特定のPDFのページ 0が表紙
$im->setImageIndex(0);
//サムネイルサイズ 640pxに収める
$im->thumbnailImage(640, 640, true);
//シャープ
$im->sharpenImage(0, 1);
//生成
$im->writeImage('/var/www/xxxx/xxxx/out.jpg');

$im->destroy();

で動くようになりました。
しかし、透明の箇所が真っ黒になるという不具合がありなおかつ
複数ページあるPDFと1ページのみのPDFでは動きが違うという問題にも直面
この難題を解決するのに、3時間程度要しました。
解決済みのサンプルがこちらです。

        $im = new Imagick();
        //画像を生成したいPDFを読み込む
        $image = file_get_contents('/var/www/xxxx/xxxx/hoge.pdf');
        $im->readImageBlob($image);
        $totalPage = $im->getImageScene();

        //サムネイルサイズ 640pxに収める
        $im->thumbnailImage(640, 640, true);
        //シャープ
        $im->sharpenImage(0, 1);
        // バックグラウンドを白にする。
        $im->setImageBackgroundColor('white');
        //特定のPDFのページ 0が表紙
        if ($totalPage != 0) {  // 複数ページの場合
            $im->flattenImages()->setImageIndex(0);
        } else {    // 単一ページの場合
            $im = $im->flattenImages();
        }
        //生成
        $im->writeImage('/var/www/xxxx/xxxx/out.jpg');
        $im->destroy();

以上です。

Posted on

公開フォルダーにないPDFファイルを表示する方法

PDFファイルアップロードの機能を作成する場合、公開フォルダでない場所にアップロードして、
そのPDFファイルをaタグで別タブに表示したい場合があります。
そこで、その場合のサンプルを記載します。

ファイル名やIPアドレスはご使用の環境にあわせてください。
main.php(表示したいファイルをフルパスでGETで渡す。)




download

表示したいフォルダのファイルをオープンしてPDFとして表示させるためのphpファイル
pdfdisp.php


以上です。試してみてください。

Posted on

アウトオブ眼中

ボタンをクリックしたら隠しdivの中のフォームをコピーして、
通し番号を発行して表に追加する、みたいな処理をよく書きます。
通し番号の入る場所は仮にダミー文字列(今回はQQQQ)を置いておいて、ボタンが押されたときの処理でまとめて書き換える感じです

何度か書いているうちにdata-属性に行番号を持たせることで、:lastから現在の最大行番号を把握し、
次の行番号を探る、なんてことをやるようになりました。
隠しフィールドよりずいぶんすっきり見えます。attrで持たせると文字列で保存されるので、取得時に0を引いて数値に解釈しなおすことをお忘れなく。 …【1】
追加するときだけじゃなくて削除にも役立ちます。$(“tr[data-no=’行番号’]”,”#fueruTable”).remove() で行ごと削れます。

ところで、これはこれで面倒なことが一つあって、これを実際submitしたとき。

QQQQはダミーフィールドなので使わないはずだし、何よりdataに関してforeachとか回すなんてことになると邪魔。
サーバー側にunset($_POST[‘data’][‘QQQQ’])を噛ませて長いことやってました。

ある時、formの閉じタグ位置を間違えて、隠しフィールドをformの外に追いやってしまったんです。
それでsubmitしたらどうなったかというと

QQQQがいない。
これはこれで何か問題がある気もするけれど、ただテンプレートとして置いておきたいだけで、そもそもsubmitする気のないフィールドは
そもそもform内に置かなければよかった!

考えてみれば当たり前のことなのに、長いこと気づかなかったのでついブログに書き出してしまいました。
submitしないフォームはformタグの外、アウトオブ眼中!他の事例でも使えたら使うつもりでいます。

Posted on

phpexcelのEXCEL列表記のテーブル作成

phpExcelにて列を指定して値を表示する際に、
EXCEL列の配列になったものをインデックスで指定して表示できれば、
横に長い表を作成する際に役に立つと思いこのテーブルを作成しました。


// EXCELの横座標を設定する。
$col1 = array("","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col2 = array("","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col3 = array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col4 = array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$colArr = array();
$idx = 0;
foreach ($col1 as $val1) {
    if ($val1 == "") {
        foreach ($col2 as $val2) {
            foreach ($col3 as $val3) {
                $colArr[$idx] = $val1 . $val2 . $val3;
                $idx++;
            }
        }
    } else {
        foreach ($col4 as $val2) {
            foreach ($col3 as $val3) {
                $colArr[$idx] = $val1 . $val2 . $val3;
                $idx++;
            }
        }
    }
}
$col = 0;
$row = 1;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // A1に「日付」表示
$col = 26;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // AA1に「日付」表示
$col = 702;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // AAA1に「日付」表示
$col = 1378;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // BAA1に「日付」表示
$col = 16383;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // XFD1に「日付」表示 ここがEXCEL横の最大値です。

PHPEXCELにて、列表記をインデックスで扱いたい時の配列です。

Posted on

JSONで画像データを送信する方法

JSONで単純にファイル名を渡しても、当然ファイルのデータは送ることができません。
そのような場合は、base64_encodeを使って送りましょう。
このサンプルでは、JAVAScriptの時点でPHPのbase64_encodeを使用してデータ化した文字列を送り
受けて側で、base64_decodeしてファイルへ保存で渡すことが可能です。
続きを読む JSONで画像データを送信する方法