今回ファイルを選択してあげている所をドラッグアンドドロップでファイルを指定して上げたいという要望を受けて対応しました。
jqueryさえあれば簡単に出来ちゃうので一番シンプルなサンプルを作ってみました。
続きを読む ドラッグアンドドロップでファイルをアップロードする
Say yes or die!
凄く過激なタイトルを付けてしまいました。
これはあるSQL文を見て、それを短く書き換えたときの感情です。
SELECT SUM(CASE WHEN condition > 100 THEN 0 ELSE 1 END) as positive FROM some_table
1行で書いてしまいましたが、もともとはCASE文が数層入れ子になっていて、複雑な条件式を最後にSUMで括っていた、というのが複数項目にも及んでいた感じのSQLでした。そして、CASE式で評価される結果は必ず0か1。つまるところ、CASE文をたくさん組み合わせて条件を作り、条件に合うものを数えるということをやっていたわけです。
最初に思ったのは「WHERE句で条件絞ってcountすればいいじゃん」ってことです
SELECT COUNT(condition) as positive FROM some_table WHERE condition > 100
そうできればよかったんですが…条件に合う件数を数える項目がほかにもたくさんあり、それぞれで
毎回SQLを発行するのもこれまた頭が悪い。
SQLの返答をphp側でforeach回してオレオレカウントする…というのも考えたのですが、
カウント部分で何をやっているかを把握しようとするとCASE文のピラミッドとどっこいどっこいだなとも。
「テーブル全体を検索しつつ、複数条件それぞれにおいて条件に合うものをcountするには…」と調べていて、
このタイトルの「Say yes or die!」にたどり着いたわけです。
MySQLの論理式にはTrue、FalseのほかにNullを叩き込むことができると言う話があって…
- True AND Null = Null
- False AND Null = False
- True OR Null = True
- False OR Null = Null
つまるところ、Nullの有無にかかわらずTrue/False断言できる式は断言してもらえるという結果になるわけです。
そして、count()はNullでないものを数える。
というわけで生まれるイディオムが count([何か条件式] OR Null) というわけです。
条件式がTrueならレコードはTrueと評価され、カウントの対象になる、
条件式がFalseまたはNullなら、レコードはFalseと評価され、カウントされない。
このイディオムを使って最初のSQLを書き直すと…
SELECT count((condition > 100) OR NULL)as positive FROM some_table
‘OR NULL’という書き方の意味さえ知っていれば、これが何かを「合計」しているわけではなく「数えている」ことが一目でわかりますし、何より、WHERE句を変えながら何度もSQL発行しなくて済みます。別のカウントをしたいときは条件を変えた検索フィールドを追加するだけですから。
「Say yes or die!」もとい「Be true or null!」使えると思います!
JSONで画像データを送信する方法
JSONで単純にファイル名を渡しても、当然ファイルのデータは送ることができません。
そのような場合は、base64_encodeを使って送りましょう。
このサンプルでは、JAVAScriptの時点でPHPのbase64_encodeを使用してデータ化した文字列を送り
受けて側で、base64_decodeしてファイルへ保存で渡すことが可能です。
続きを読む JSONで画像データを送信する方法
WebサイトをPDFに変換する
HTMLでシステムの画面のモックを作成していた時に
お客様が全ページ画面キャプチャするのが大変・・・
印刷して朱書きで要望等簡単に書けないか?ということがありました。
社内でも調整してほしい箇所をキャプチャするのは大変だし、何かいい方法ないか調べてみました!
compassからlibsassへ。gulpの設定を見直して大幅速度UP!
gulpを動かしてゴリゴリSCSSを書いています。
当社はPHPを主に扱っているので、PHPが動く環境でかつSCSSが書けてbrowsersyncが効く状態が一番ストレスなく開発が進められます。
そんなストレスフリーな環境を実現するには、gulpfile.jsのbrowsersyncの設定をproxyにすれば可能です。
gulp.task("default-toEccube", function() { browser({ proxy: "xxx.xxx.xxx/project/" }); gulp.watch("scss/**/*.scss",["sass"]); });
ストレスフリーにらくらく開発を進めていた所、SCSSコンパイルに何秒もかかっている事に気づきました。
よくよくgulpfilesのSCSSコンパイルの所を見直してみると、compassがかなり時間をかける原因になっていたようでした。
gulp.task("sass", function() { gulp.src("scss/**/*scss") .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(sourcemaps.init()) //.pipe(sass()) .pipe(compass({ config_file: './config.rb', css: css_path, sass: 'scss' })) .pipe(autoprefixer()) .pipe(sourcemaps.write(css_path)) .pipe(gulp.dest(css_path)) .pipe(browser.reload({stream:true})); });
このcompassはrubyで動かしていて、これがボトルネックの原因でした。
速くしたいならlibsassを使うような流れになってきているそうで、これまた導入しようと調べた所、実はコメントアウトしていた .pipe(sass()) がそのlibsassという事を知って二度驚きました…。
gulp.task("sass", function() { gulp.src("scss/**/*scss") .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(sourcemaps.init()) .pipe(sass()) .pipe(autoprefixer()) .pipe(sourcemaps.write(css_path)) .pipe(gulp.dest(css_path)) .pipe(browser.reload({stream:true})); });
すぐさま、compassを消去し、sassを復活させましたが、今度は自分が良く使うベースのscssファイル群にcompassの関数が使われているのがあって、コンパイルが通らなくなりました。
そこでcompassの関数をsassのみで使えるように開発された、compass-mixinsを導入してみました。
使用方法は、使いたいプロジェクトファイルのscssフォルダにダウンロードしたcompass-mixinsのcompassフォルダを入れて、インポートしたいscssファイル(例えば、styles.scss)の上の方に、
@import "compass/reset"; @import "compass";
で呼び出すだけで、エラーがなくなりcompassの関数が使えちゃいました!!
そして、コンパイルの速度は6秒から1秒未満に!ctrl-s押したらすぐ完了!みたいなレベルになりました。
スプライトなどをcompassで使っていた場合別の方法を取らないといけないですがそこまで使っていなかった方にはオススメです。
Conohaのオブジェクトストレージを試す
システムによって写真を大量に上げられる事があり他のシステムへの影響が出てきたのもあり今回プログラムから上げる画像やPDF等のファイルをシステムが動いているサーバーからオブジェクトストレージサーバーに置くことになりました。
Conohaのオブジェクトストレージを試した際につまずいた事等を書いていこうと思います。
続きを読む Conohaのオブジェクトストレージを試す
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が何をしている関数かピンとこないと読みづらい、みたいな。
他の関数についても、気になったら実験を進めます。
それがリファレンスを頭の片隅に置くために役立つ気がしています。
jQueryを用いた気持ちのよいインタラクションとフェードインを考える
気持ちの良いデザインを考える
Webデザインについて再考する時間があったので、インターフェースの心理学やUI GRAPHICS等の本を読んでどんな事がサイトをデザインする時に人を魅了できるかを勉強してみました。
その中で、能動的に選んでいる気でいても、実は反応であったり無意識的に動かされている事が多いという事を学びました。
では、何をもって人は無意識的に動かされるか、という点で考えた時に「ユーザーが何か起こした事によって得られる反応(=インタラクション)を気持ちの良いものにする」事で、自然と楽しい気持ちになったり、もっと触ってみようという気持ちになって、より能動的な働きとして変えていけるんではないかと考えるようになりました。
インタラクションとは
例えば、ソフトウェア、各種製品、携帯機器、環境、サービス、ウェアラブルコンピューティング、組織自体などのシステムに適用される。インタラクションデザインは、人工物やシステムのユーザーへの反応を振る舞い(インタラクション)として定義する。IaD または IxD と略記されることもある。システム開発においてユーザーの人力操作に対するシステムからの適切な反応を設計することで利用目的に合致した両面転移や、グラフィカルユーザーインターフェース(GUI)要素の肖然な振る舞いをデザインする専門的な作業である。
引用:wikipedia
前述の「ユーザーが何か起こした事によって得られる反応」をインタラクションといい、システムにおけるView部分、Webサイトでいうリンクやボタンで表現される入力部分のみならず、画像や文字などの表示部分(視覚的に入力する)をより適切なデザインを施す事で、そのシステム自体をより良いものに見せるという点で、現在注目されている分野であります。
イマドキのWebサイトは、ボタンにマウスをホバーするとふわっと色が変わる事や、画像ギャラリーで画像が、自動的にスライドして見られるスライダーなど動的である事が標準となってきております。
jQueryやJavaScriptが持てはやされているのは、この「当たり前に動く」仕組みを実現するのに不可欠なプログラミング言語で、昨今では、HTML + CSSに加えて、JavaScriptができる事がデザインという分野で求められる傾向にあります。
気持ちいい画像の表示方法を考えてみる
前置きが長くなりましたが、本題はここからです。
「画像を表示させる」というWebサイトでは当たり前の事象についても再考してみました。以下のDEMOはAJAXにて画像データを取得して表示するサンプルですが、表示のさせ方を4パターン用意してみました。
※Chrome、Firefoxでの動作の確認をしました。
See the Pen AJAXを使った画像読み込みの一工夫 by koka (@kokaben) on CodePen.
画像を表示させる方法
画像は画像サイズによって表示までの速度がマチマチになる事や、画像データを取得している時でも他の作業ができるように、バックグラウンド読み込み処理を行いながら用意ができたものから表示させる非同期処理を用いるのが現在の主流です。
当サンプルでは、画像を裏で読み込ませてメモリに展開された時点で、表示させるという方法を用いております。
「すぐ表示」ボタンは、別段処理を施さずに、用意できた順から表示するようになっています。
一番最速で表示させたい時はこの方法が良いですが、少し無骨に見えませんか?文字のみならばこの方法が一番良いかもしれませんがもう少し見せ方にこだわりたく思ってしまいます。
「フェードインする」ではどうでしょうか。
少し気持ちよくなった気がしませんか?少しずつ表示される事で少しワクワクするようなそんな作用が引き起こされていれば狙い通りです!
ただ、これが何百件だとどうでしょうか。「これはいつまで、どこまで表示されるんだろうか」と多少不安になったり、遅延処理を行っているので「少し遅いな」と感じてしまうんではないでしょうか。
表示する量や、場所を選べば気持ちよさのみの印象を与える事ができそうです。
表示する数と領域をあらかじめ見せる
「背景を付けて●●」のボタンが2つありますが、この手法は記事を書いている時期(2017年)によくみられる手法で、ボタンを押すとあらかじめ表示される領域の背景をグレーで表示され、そこに画像や文字を展開していく見せ方です。
この手法は、YouTubeのスマートフォン版を起動した時や、検索した時に見られたり、Googleの画像検索でスクロールしていった時に自動的に次の30件が表示される時に見られる手法をまねたものです。
「背景を付けてすぐ表示」ボタンの動きを見てみましょう。
タイミング的には「すぐ表示」で画像が表示されるタイミングで表示されて、そこから0.5秒後に画像が表示されるので、実質は「すぐ表示」の時より遅いのですが、先に領域が出る事で「ああ、ここに何か表示されるんだな」と頭が認識する時間が発生するので、そこまで遅いと感じないのではないでしょうか。
ただまだ、無骨に思えるので、それにフェードインを加えた「背景を付けてフェードイン」を試してみて下さい。
気持ちよくないでしょうか?
領域が決まって、そこから画像と文字のフェードインが起きながら、文字の領域はフェードアウトさせる事で、安定感のある面白くて気持ちの良い見せ方になったのではないかと思います。
感性に訴えたい
以上のサンプルは、必ずしもそんな気持ちになるかどうかは別の話で、私は「そういう風に狙った」というだけな話です。受け手の気持ちは千差万別と思われます。
併せて思うのは使いどころを見誤ったり、要らない所まで動きを付けまくっていると見づらいページになってしまうので、使いどころを見定める事は必要と思います。「コレが利いてて気持ちいいんだよなー」くらいがちょうど良いと思っています。
オーソリティーに受ける方法というのを模索しながら、より面白くてコストパフォーマンスに優れた見せ方をこれからも模索していくぞーと考えながら、日々勉強していこうと思います。
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は、特にふれていませんが、実行するには必要になります、ご注意ください。
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