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で画像データを送信する方法

Posted on

Conohaのオブジェクトストレージを試す

システムによって写真を大量に上げられる事があり他のシステムへの影響が出てきたのもあり今回プログラムから上げる画像やPDF等のファイルをシステムが動いているサーバーからオブジェクトストレージサーバーに置くことになりました。
Conohaのオブジェクトストレージを試した際につまずいた事等を書いていこうと思います。
続きを読む Conohaのオブジェクトストレージを試す

Posted on

array_filterの邪道?

まだまだ修行中です。最近は二度手間をできる限り抑えたいので
PHPのリファレンスを読み込んでみて、こんな関数既にあるんだなっていうのを見つけているところです。
しっかり覚えていなくても、頭の片隅に引っかかっていればいずれ使えるよな…という魂胆です。

そんな中でarray_filterの使い方がどうしても気になりました
http://php.net/manual/ja/function.array-filter.php

配列のキーや値を関数に通して、trueが返ってくるものだけを配列に詰め込んで返す、
使う関数は関数名を指定しても無名関数を渡してもいい、と。

ここで、手元のリファレンス書籍には「一次元配列のみに対応」と、
こちらのオンラインマニュアルには書いていない記述があったのがどうにも引っかかったんです。

「なぜ渡したらダメなのか、あるいはダメだったのか」

とりあえず、php5.4環境で渡してみてどうか調べてみました。

$order=array( 'Alice'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>true,
		),
	),
	'Bob'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>true,
		),
	),
	'Chalie'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false,
		),
		'cake'=>array('chocolate')
	),
	'Deborah'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>false
		),
	),
	'Eve'=>array(
		'doughnut'=>array(
			'plain','strawberry'
		),
	),
	'Fred'=>null,
	'George'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false
		)
	),
);

function order_filter($v){
	/* TODO:ここに検索条件を書く */
}

var_dump(array_filter($order,'order_filter'));

ここのorder_filter($v)をいくつか変えて出力を調べてみました。

//コーヒー注文
return isset($v['coffee']);

/* Output:
array (size=5)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Chalie' => 
    array (size=2)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
      'cake' => 
        array (size=1)
          0 => string 'chocolate' (length=9)
  'Deborah' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean false
  'George' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
*/
//砂糖有りコーヒーまたはコーヒー注文無し
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
Notice:Undefined index: coffee in (file) on line 43 //43行目=上の条件を書いた行

array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/
//砂糖有りコーヒーまたはコーヒー注文無し 改
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/

結論としては、「キーの存在」を追加して調べる必要がある(ない場合noticeが出る)…みたいなところです。
この試行だけでは結局「なぜ渡したらダメなのか」という理由はわかりませんでした。
もっと未定義みたいな挙動が出てくるかと思ったら、そういうわけでもなく。
あるいは、バージョンが上がったことで渡せるようになったとか、そういうことなのかもしれないです。
(手元の書籍は5.1ぐらいに対応するよう書いていた9年以上前のものだからあり得る話)

可読性で言えば使わずにforeachループ回すのと五分五分、ってところかなと思います。
array_filterが何をしている関数かピンとこないと読みづらい、みたいな。

他の関数についても、気になったら実験を進めます。
それがリファレンスを頭の片隅に置くために役立つ気がしています。

Posted on

JQueryにてJSONをPHPに送り、さらにベーシック認証の指定のURLにPOSTしてJSON形式のデータを受け取る方法

長いタイトルになってしまいましたが、ようするにJSON形式なら受け渡しが楽になりますよということです。
まず、JQueryからPHPにJSON形式のデータを渡す方法(www/index.php)




    
    
    
    Ajax Sample


Ajax sample

JQueryからJSON形式のデータを受け取りPHPで連想配列にして他のURLにポストする方法(www/ajax_api.php)
ちなみに、ベーシック認証がない場合は、define(‘DEMO’, true);をdefine(‘DEMO’, false);へ

 "{$memberId}"
    );
    $data = http_build_query($data, "", "&");

    if (DEMO) {
        //header
        $header = array(
            'User-Agent: My User Agent 1.0',    //ユーザエージェントの指定
            'Authorization: Basic ' . base64_encode('account:password'),//ベーシック認証
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    } else {
        //header
        $header = array(
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    }

    $context = array(
        "http" => array(
            "method"  => "POST",
            "header"  => implode("\r\n", $header),
            "content" => $data
        )
    );
    $res = file_get_contents(SAMPLE_API, false, stream_context_create($context));
    return $res;
}
?>

POSTデータを頂き、情報(連想配列のID指定にしていますが、ご利用の際には、DBから取得する方法へもバージョンアップできます。)
を取得JSON形式の返信を頂く方法(wwwapi/index.php)

 array("id" => 10000,'name' => 'Name10000','tel' => "000100010001"),
	"9999" => array("id" => 9999,'name' => 'Name9999','tel' => "000900090009"),
	"9998" => array("id" => 9998,'name' => 'Name9998','tel' => "000900090008"),
	"9997" => array("id" => 9997,'name' => 'Name9997','tel' => "000900090007"),
	"9996" => array("id" => 9996,'name' => 'Name9996','tel' => "000900090006"),
	"9995" => array("id" => 9995,'name' => 'Name9995','tel' => "000900090005"),
	"9994" => array("id" => 9994,'name' => 'Name9994','tel' => "000900090004"),
	"9993" => array("id" => 9993,'name' => 'Name9993','tel' => "000900090003"),
	"9992" => array("id" => 9992,'name' => 'Name9992','tel' => "000900090002"),
	);

$memberId = $_POST['member_id'];

if(empty($memberArray[$memberId])) {
	echo json_encode(array());
}
echo json_encode($memberArray[$memberId]);
?>

※jquery-1.11.2.min.jsは、特にふれていませんが、実行するには必要になります、ご注意ください。

Posted on

Cakeのpaginatorのソートを押して複数条件のソートを行う方法

あるシステムで日報の1日の予定を登録して頂いていて、
日付単位とその中の詳細の予定の2段階でのソートをする必要がありました。
第一ソート条件は日付で第二ソート条件が詳細予定の時間というソートを実現させる実装方法です。

まずviewでpaginatorのソートの記述を書きます。
Paginator->sort(‘report_date’, ‘訪問日時’); ?>

次にコントローラーで次のように書きます。

if (empty($this->passedArgs['sort'])) {
    $order = array('Report.report_date' => 'desc', 'Report.staff_id' => 'asc', 'ReportDetail.report_date' => 'asc');
}elseif ($this->passedArgs['sort'] == 'report_date'){
    $order = array('Report.report_date' => $this->passedArgs['direction'], 'Report.staff_id' => 'asc', 'ReportDetail.report_date' => 'asc');
}
$this->ReportDetail->setOrder($order);

最後にモデルで次のように書きます。

// デフォルトのソート条件は何もなし
var $order = array();

function setOrder($order) {
	// controllerから渡された$orderを変数に持っておく
	$this->order = $order;
}

function beforeFind($queryData) {
	// 実行中のfindの種別が'count'以外だった場合のみ、
	// ソート条件を追加し、$orderを初期化する
	if ($this->findQueryType != 'count' ) {
		array_unshift($queryData['order'], $this->order);
		$this->order = array();
	}
	return $queryData;
}

これでCakeのpaginatorのソートを押して複数条件のソートが行えました。
作成するに当たり下記サイトを参考にさせて頂きました。
http://d.hatena.ne.jp/atcorp/20100213/p1

Posted on

本日は曇天なり

前回の僕のブログが(フランス革命歴)時報だったから、電話番号似ているつながりと言うわけでもないですが、今回は天気予報を取ってくるという話があったので、そのときにやったことをメモしておきます。

天気予報情報はここ。OpenWeatherMap.orgを使いました。
https://openweathermap.org/

いろいろ英語で書いてあって苦手な人は大変ですが、とりあえず必要なことは

  • Sign Upから登録
  • API keysから文字列を持ってくる

だいたいこんなところです。Subscribeは今のところ必要ない感じです。(月額ですし)

こちら側でやる必要があることはだいたい、

  • 持ってきた文字列を含むHTTPリクエストをURLに投げる
  • JSONを受け取る

あとはお好きなように使う、といったところです。

/**
* 定数、配列定義
*/

//アクセス用APIキー
define('API_KEY','/*ここに取得したAPIキー*/');
//アクセス用URL
define('API_URL','http://api.openweathermap.org/data/2.5/forecast?units=metric&id=1853908&APPID='.API_KEY);//大阪のcity idを使用

/* HTTPリクエストを投げる感じのクラスHttpRequestも用意しておく */

/*ここから取得*/
$http = new HttpRequest(API_URL,HttpRequest::REQ_HTTP_GET);
$code = $http->sendRequest();

if($code!=200){
    echo 'Error:データの取得に失敗した';
    exit;
}

$rawWeather=$http->getResponseBody();

ここで、$rawWeatherの中身はこんな感じ

{
     "cod":"200",
     "message":0.021,
     "cnt":39,
     "list":[
         {
             "dt":1491793200,
             "main":{
                 "temp":12.43,
                 "temp_min":8.35,
                 "temp_max":12.43,
                 "pressure":892.44,
                 "sea_level":1032.22,
                 "grnd_level":892.44,
                 "humidity":78,
                 "temp_kf":4.08
             },
             "weather":[{
                 "id":500,
                 "main":"Rain",
                 "description":"light rain",
                 "icon":"10d"
             }],
             "clouds":{"all":68},
             "wind":{"speed":1.06,"deg":217.002},
             "rain":{"3h":0.155},
             "snow":{},
             "sys":{"pod":"d"},
             "dt_txt":"2017-04-10 03:00:00"
          },
          { ...以下、3時間ごと5日分の予報... }
     ]
     "city":{
         "id":1853908,
         "name":"Osaka",
         "coord":{"lat":35.95,"lon":137.2667},
         "country":"JP"
     }
 }

天気の内容はlist[n].weather.mainに、気温や気圧はlist[n].mainに出ています。list[n].dt_txtは世界標準時なので、日本時間では4月10日正午の天気ってことになります。URLにunits=metricをつけることで、気温の単位は摂氏として送られてきます。リクエスト文字列をいじると華氏にもできたりするそうです。
月額のないAPIだと1分間に60回までしか呼べないとかあったりしますけど、とりあえず試す分には十分でしょう。天気予報が必要になったら、こういう方法に頼れる、と言う話でした。

Posted on

PDF出力で時間がかかる場合に「処理中...」表示する

CAKEPHP2で、PDF作成時にその作成している間だけ、処理中...を表示するサンプルを記入します。
まずDBを追加します。

CREATE TABLE IF NOT EXISTS `sessions` (
  `session_id` text
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

custom.css

・
・
・
div#loading {
    position : absolute;
    top : 20%;
    left : 50%;
}
・
・
・

bootstrap.php

・
・
・
define('ROOT_URL', preg_replace('/index\.php/', '', env('SCRIPT_NAME')));
・
・
・

default.ctp

・
・
・
    
・
・
・


・
・
・


xxxxController.php

    function detail($id, $step){
	if (isset($this->request->data['out'])) {
            $info = PDF出力する情報を取得処理を記述する。
            if (!empty($info)) {
                $this->autoRender = false;
                $this->layout = false;
                $this->set('info', $info);
                $this->render('pdf_out');
            } else {
                $this->モデル名->invalidate('error', "データがありません。");
                $this->autoRender = true;
                $this->layout = 'default';
            }
        }	
    }

    /**
     * セッション保存
     *
     * @note
     */
    function ajax_session_save() {
        Configure::write('debug', 0);
        $this->autoRender = false;

        $id = $this->Session->id();
        $sql = "INSERT INTO sessions (`session_id`) VALUE (:id);";
        $sqlArr['id'] = $id;
        $jsonResult = array();
        if (false === $this->モデル名->query($sql,$sqlArr)) {
            $jsonResult['RES'] = -1;
        } else {
            $jsonResult['RES'] = 0;
        }
        echo json_encode($jsonResult);
        exit;
    }

    /**
     * セッション確認
     *
     * @note
     */
    function ajax_session_get() {
        Configure::write('debug', 0);
        $this->autoRender = false;

        $id = $this->Session->id();
        $sql = "select * from sessions WHERE session_id = :id;";
        $sqlArr['id'] = $id;

        $jsonResult = array();
        $res = $this->モデル名->query($sql,$sqlArr);
        if (!empty($res)) {
            $jsonResult['RES'] = 0;
        } else {
            $jsonResult['RES'] = -1;
        }
        echo json_encode($jsonResult);
        exit;
    }

    /**
     * render直後の処理
     *
     * @note
     */
    public function afterFilter() {
        $id = $this->Session->id();
        $sql = "delete from sessions WHERE session_id = :id;";
        $sqlArr['id'] = $id;
        $res = $this->モデル名->query($sql,$sqlArr);
    }

detail.ctp

・
・
・
    
・
・
・

PDF作成処理はこのブログでは触れませんが、
結論から言うとsession_idがDBに保存されている間は、PDF出力処理中になるようにします。
出力ボタンが押下された時点で、本当に実行してよいかを問い、実行してよい場合は、当該のsession_idをDBに追加します。
renderで呼ばれた処理が終了すると、PDF出力が終わったことになり、CAKEPHP2では、
afterFilter()が呼ばれます。そこで、DBから該当のsession_idを消します。
表示VIEWでは、session_idが存在する間、処理中...を表示としておき、
DBからsession_idが削除されれば、処理中...を消すことで完成となります。