- 投稿日:2020-06-04T23:28:24+09:00
WEBサーバのTCPコネクション数に上限はあるのか?
はじめに
アクセス数がすごい環境は大抵高負荷環境でもあるので対策としてApacheの設定やサーバ構成のセオリーをすぐ見つけられて実際試しても目に見えて良くなります。
アクセス数の多さで起こる問題は実際に負荷をかけてみないと表面化しません。
問題が分かったら設定やパラメータを調整して現状がましになるようにするだけです。
ですが限りあるリソースの中でTCPセッションを十分にコントロールしようとするとすぐ手詰まりです。
WEBサーバがしているやりとりの基礎が足りていないそんな気がしていたのでTCPに目を向けてみました。行き着いた結果は
待ち受け側とリクエスト側ではボトルネックが違う
リクエスト時はTCPのエフェメラルポートが上限
待ち受け時はTCPよりもファイルディスクリプタが上限
になりやすいという良く見かける設定を見直すということになりましたが、どうやってそうなっているのかが今回の成果だと思います。
TCPセッションはどうやって区別されるか?
1つのサーバでのTCPセッションは複数のクライアントと複数のセッションを組むことができてそれぞれを識別しています。
それをどう区別つけているのかというと
以下の5つの組み合わせで行われるとのこと
- プロトコル
- 送信側IP
- 送信側ポート
- 受信側IP
- 受信側ポート
そしてこの組み合わせの数がTCPセッションでの限界となります。
WEBサーバが直接待ち受ける場合netstatで状況を見ると例えばこうなります。{ServerIPaddress}:80 {ClientIPaddress}:60233 TIME_WAIT 192.168.11.1:80 10.0.0.1:60234 TIME_WAIT 192.168.11.1:80 10.0.0.2:60233 TIME_WAIT待ち受け側は一定のポートで待ち受けてリクエストを送信してきたクライアント側ではエフェメラルポートでセッションを組みます。
なのでとの時の組み合わせの数は
1 IP * 1 Port * {Global IP Address} * エフェメラルポート数
プロトコルはTCPで一定なのでグローバルIPとエフェメラルポート数の組み合わせで有限ではあるけれど他の性能で受けきれないくらいはありますね。
L4LB下でもWEBサーバ側から見ると同じようになるのですがこちらはIPアドレスやポートを書き換えているのでそこはLBの仕様が上限になりうるのかなと思います。
TCPセッションと一緒に増えるのはファイルディスクリプタ
ということで待ち受け側ではTCPセッションではさほど問題にならないのですが、TCPセッションを受け取るとそれがファイルのストリームを開けたことと同じ扱いになります。
Linuxではファイルをオープンできる数がプロセス毎に制限されています。
それがファイルディスクリプタです。
systemdではserviceのパラメータで増やすことができるので[Service] LimitNOFILE=65536と加えて制限を広げるあの設定です。
受けるセッション と セッション毎に実際にオープンするファイル数が消費されていきます。
それが1プロセスにつき最大65536個までになりますnginxだとすぐファイルディスクリプタの制限がきて
Apacheのpreforkでは問題にならなかったりするのはプロセスをたくさん作っておくpreforkのやり方の違いそのものだったんですね。TCPポートの枯渇
『TCPポートは枯渇する』
といきなり言われても良くわからない。
Linuxにはエフェメラルポートというのが設定されていてネットワーク接続の度に消費される。その空きがなくなると接続できなくなる。エフェメラルポートはデフォルトで32768-60999で数は28232ポート
だからサーバ1台で拡げないと大体28kが上限
でも監視しているWEBサーバのTCPステータスの統計では全部で50kとかいったりするのでなんか違う。しかし実際エフェメラルポートは枯渇するしただの怖い話ではなかったのです。
クライアント側のエフェメラルポート
ReverseProxy(L7LB)とWEBサーバ間の場合で
クライアント側になるReverseProxyでnetstatを見ると{ReverseProxy IPaddress}:60100 {WEB Server IPaddress}:80 TIME_WAIT 192.168.11.1::60100 192.168.20.1:80 TIME_WAIT 192.168.11.1::50100 192.168.20.1:80 TIME_WAITとなるのでReverseProxyが送信する際相手WEBサーバが複数台あっても自分のIPアドレスは一つでポートは開放されるまで割当られません。
リクエストするIPアドレスでのセッションはエフェメラルポート数分までなのです。
良くありがちなことにこれがTIME_WAITで埋め尽くされてしまうと新たなセッションを作れないことになります。しかしReverseProxyではよくbackend側の接続にはHTTPのKeepAliveを有効にするという設定があります。接続相手先もReverseProxyに対するWEBサーバということで特定の台数に限定されるのでTCPの再利用の機会が多くなります。そのおかげで大量のHTTPリクエストを渡せるようになるのです。
keepalive_timeout 60
AWS ALBのドキュメントのベストプラティクスでは
Keepalive timeout 60
くらいを推奨していますがそれはALBがなるべく同じReverseProxyから送るようにしているおかげで分散型のシステムなのにTCPセッションの再利用を高められるのです。リクエスト(問合せ)と言えばDBですが
WEBサーバでは静的ファイルをレスポンスしているだけではなく大抵CGIなどのプログラムからDBへ問合せしてその結果をレスポンスしています。
そのDBへの問合せもTCPを使用していればエフェメラルポート数を消費しています。
アクセス数が多いサイトでは大抵Memcached や Redisも使っていたりするのでそちらへのリクエストも発生します。
そうなるとレスポンスに至る過程でファイルディスクリプタも消費しそうな場面でありますがわりとWEBサーバではエフェメラルポートの需要が高いので気をつけなければいけない。
これが『TCPポートが枯渇する』という話で大事な部分でした。大量の新規TCP接続はコストが積み重なっていく
大量のコネクションが発生する箇所で都度接続してすぐ廃棄しているとTIME_WAITですぐ埋まりCPUもそれなりに使うようになります。
そこで予め数本接続しておき再利用できるように管理しておけば手続きも省略され多くのリクエストが可能になるでしょう。需要に応じて接続を増減してくれれば便利です。
これを『コネクションプーリング』と呼ぶのですがこの言葉がJava界隈以外ではまずあやふやになっていてこの機能を謳っていても実装がどうなっているか気にする必要があるようです。最近ではMySQLProxyのマネージドサービスがAWSで登場するなどしていてコネクションプーリングとそれ以外の目的もあると思いますがDBでもProxy需要が高まっていそうですね。
そんなDBProxyを利用してTCPセッションを再利用しつつ他の問合せとエフェメラルポートを共有していく方法は選択肢に入れておいて良いのかもしれません。参考にさせていただきました
- 投稿日:2020-06-04T22:46:22+09:00
PHPでcsvファイルから商品一覧ページと商品詳細ページを作る
希望内容
自社商品の情報はデータで管理してる。
ホームページがで自社商品を紹介したい。
でもネットショップを作るほどでもない。
簡単でいいのでホームページ上で自社商品を紹介できないか?
上記希望をPHPを使って実現する方法。PHPコード
index.php<?php //csvデータを一行ずつ読み込みhtmlとして書き出す。 //画像は、imagesフォルダに「商品ID + .jpg」で保存しているとする。 //商品画像をクリックで、GETにてprev.phpにIDを渡す。 if (($handle = fopen("product.csv", "r")) !== FALSE) { while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { echo <<<eof <a href="prev.php?id={$data[0]}"> <img src="images/{$data[0]}.jpg" /> </a> eof; } fclose($handle); } ?>prev.php<?php //商品idをGETで取得し整数化する $product_id = intval($_GET["id"]); //csvデータを読み込み、idが一致したら商品を書き出し終了する //csvにはid、商品タイトル、商品説明文の順番で入っているとする if (($handle = fopen("product.csv", "r")) !== FALSE) { while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { if($product_id == $data[0]){ echo <<<eof ■商品ID<br /> {$data[0]}<br /><br /> ■商品タイトル<br /> {$data[1]}<br /><br /> ■商品画像<br /> <img src="images/{$data[0]}.jpg"><br /><br /> ■商品説明文<br /> {$data[3]}<br /><br /> eof; break; } } fclose($handle); } ?>データベースは使わずテキストファイルなので編集も簡単。
レンタルサーバーからのバックアップも簡単。疑問点
1.エラーページは作っておいた方が良い?
2.
$product_id = intval($_GET["id"]);
これだけで無害化できている?
- 投稿日:2020-06-04T22:07:08+09:00
PHPでsudoコマンドを実行する方法
OSコマンドを実行する関数
shell_exec
shell_exec ( string $cmd ) : string
返り値
実行されたコマンドからの出力を返します。 エラーが発生した場合やコマンドが何も出力しなかった場合は NULL を返します。
例
<?php $output = shell_exec('ls -lart'); echo "<pre>$output</pre>"; ?>exec
exec ( string \$command [, array &$output [, int &\$return_var ]] ) : string
返り値
コマンド結果の最後の行を返します。コマンドを実行し、 一切干渉を受けずに直接コマンドから全てのデータを受けとる必要が あるならば、PassThru() 関数を使ってください。
実行されたコマンドの出力を取得するには、必ず output パラメータを設定・使用してください。例
<?php // ("whoami" コマンドをパスに有するシステム上で) // 実行中のphp/httpdプロセスを所有するユーザーの名前を出力 echo exec('whoami'); ?>passthru
passthru ( string \$command [, int \$return_var ] ) : void
引数 return_var を指定した場合、 Unix コマンドのステータスで置換されます。返り値は有りません。
\「`」演算子
shell_exec()と同じ。
それぞれの違い
shell_execとexecの違い
返り値について
コマンド名 返り値 shell_exec すべて文字列 exec 文字列、配列 実行結果について
コマンド名 実行結果 shell_exec すべての実行結果を返す exec 最後の行しか返さない(output引数を入れればすべて取得できる。) passthruについて
返り値がないので、実行結果を確かめることができないので汎用性に書けていると思われます。
sudoコマンドの実行方法
本題です。
任意のユーザにsudo権限を付与
$ sudo visudo上記コマンドを入力すると/etc/sudoersの設定ファイルが開くので、最後の行に以下の文を追記
任意のユーザ名 ALL=(ALL) NOPASSWD:実行したいコマンド「実行したいコマンド」の部分では「ALL」も指定できますが危険なので避けます。
- 投稿日:2020-06-04T20:03:18+09:00
PocketMine-MP4.0を構築する
現在PMMP4.0は安定版ではありません。通常のPMMPを使いたい方は3.xを使用してください。
環境
- Ubuntu 18.04.4
作業用ディレクトリを作る
ディレクトリ名はなんでも大丈夫です。ここではpmmp4とします。
$ mkdir pmmp4 $ cd pmmp4バイナリをコンパイルする
php-build-scriptsのmasterブランチを使います。compile.sh実行後はかなり時間がかかるので気長に待ちましょう。
$ git clone --branch=master https://github.com/pmmp/php-build-scripts $ cd php-build-scripts $ ./compile.shエラーが出た場合はインストールするよう表示されたものをインストール後、再度compile.shを実行してください。
$ mv bin ../ $ cd ../ $ rm -rf php-build-scriptsPMMPをダウンロードする
PMMP用のディレクトリを作り、GitHubからPMMPのmasterブランチをcloneした後、必要な物だけを取り出します。
$ git clone --recursive --branch=master https://github.com/pmmp/PocketMine-MP $ cd PocketMine-MP $ mv src resources composer.json start.sh ../ $ cd ../ $ rm -rf PocketMine-MP依存ライブラリをインストール
まずcomposer.pharをダウンロードします。
$ curl -sS https://getcomposer.org/installer | phpcomposer.pharを用いてインストールします。
$ bin/php7/bin/php composer.phar installPMMPを起動する
$ ./start.sh -f src/PocketMine.php成功すると次のように表示されるため、指示に従って設定を行います。
[*] PocketMine-MP set-up wizard [*] Please select a language ...設定後、初回では開発中のバージョンで起動できない設定になっているため、自動でPMMPが停止します。
そこで、pocketmine.ymlを編集して起動できるように設定します。pocketmine.ymlsettings: ... enable-dev-builds: true23行目のenable-dev-buildsをfalseからtrueに変更し、保存後再度start.shを実行すると起動できます。
おわりに
何か間違い等あれば連絡ください!
良きPMMPライフを!
- 投稿日:2020-06-04T19:04:13+09:00
PHPの便利Tips備忘録(時々更新)
改行
"\n"
が使えなかったら"<br/>"
エラー表示
「ブラウザでPHPの処理したいんだけど何も表示されない!!」
「でも、レンタルサーバー使ってるからphp.ini
とかの編集の仕方わからないし…」
そんな時は↓ini_set("display_errors", "on"); error_reporting(E_ALL);
ini_set
でphp.ini
を編集しなくてもファイルからエラーを確認できるようになる。
error_reporting
で出力するエラーのレベルを設定、E_ALL
でほとんどのエラーが検出される。var_dumpを見やすく
データの確認をするのに便利な
var_dump
少量のデータなら良いけど、大量のデータを確認するとなると、ブラウザ上で区切られて表示されないから見づらい。
そんな時は↓echo "<pre>"; var_dump(確認したいデータ); echo "</pre>";htmlの
<pre>
タグでvar_dump()
の出力の際に使用されている改行などがそのままブラウザに反映されるようになる。
- 投稿日:2020-06-04T18:08:59+09:00
【WordPress】ショートコードでurlを動的に生成
WordPressの投稿画面でAdd Quick Tagを使って画像挿入をする際に
リンク先URLを動的に変えたかったので作ってみました。今回の概要
- 会員限定コンテンツがあり、ログイン者にのみリンク先のコンテンツを見せるようにしたい
- ログインしていなければ会員登録画面へ
- 投稿記事/固定ページ中にはphp記述が書けない
(⇒プラグイン使えば出来るけど使いたくない)- 登録する画像が多いのでいちいちaタグでフルパスurlなんて書きたくない
そんな理由で作ったのがこちら。functions.phpfunction get_url_shortcode($atts) :string { // set default value $baseUrl = "https://hogehoge.com/"; $pairs = ['url' => ""]; $atts = shortcode_atts($pairs, $atts); // trim string $url = preg_replace("/( | )/", "", $atts['url']); if( is_user_logged_in() ){ if(!($url === "")) { return $baseUrl.$url; } return $baseUrl; }else{ return $baseUrl.'regist/'; } return $baseUrl; } add_shortcode('getUrl', 'get_url_shortcode');shortcode[getUrl] // もしくは [getUrl url='hoge']【解説とか】
・\$baseUrlでサイトのURLを設定してます
・short_codeatts関数でショートコードの初期値を設定
shortcode_atts(\$pairs , \$atts)
\$pairs : ショートコードで使用する全ての属性名とデフォルト値
\$atts : ショートコードの属性これで、ショートコードが引数を持ってなかったとしても大丈夫
もしユーザーがログインしていれば、ショートコードの引数(url='hoge')で設定したUrlが返ります
(https://hogehoge.com/hoge/)
ログインしていなければ、会員登録画面のurlが返ります(ここでは例としてhttps://hogehoge.com/regist/)ショートコードに引数を持たせてなかった場合は、
ログインしていなければ変わらず会員登録画面のurl、していれば\$baseUrlで設定したurlが返りますこういうurl生成のショートコードは色々応用効きそうだし、いろんなところで使えそう。
あ、トレイリングスラッシュとかは環境に応じて変えないとですね。
入ってたら表示されない、入ってないと表示されるとか環境の違いであるので。。。
- 投稿日:2020-06-04T18:08:32+09:00
wordpress URL判定で分岐→出力するためのちょっとしたお話
初歩的内容ですがおさらいとしてメモ。
別に、wordpressに関して話ではなくPHP全般のお話です。
今回は、マルチサイトのサブディレクトリ型を例に取り上げます。こんな時
例えば、マルチサイトで多言語サイトを構築する際に、言語ごとのテーマは同じだが、サイトごとに出力を分岐したいことがあると思います。
ヘッダーH1タグのロゴにサイトホームをリンク設定する際には、
echo home_url();
としたら、自動で各サイトのホームリンクを取得してくれるのですが、
仮に、h1ロゴの横にテキストがあったとして、各言語ごとに出力を変えなければいけないとします。解決策1
各言語サイトにカスタムフィールドを設けて、それぞれ出力させる。
複雑な出力だったらこれでいいのでしょうが、短いテキスト飲み出力の場合、フィールドをつくったり複製したり(修正したり)と管理上面倒ばところがあります。解決策2
本題ははこちらです。
結論から言いますとexplode('', '')
を使用します。
文字列を分割して、分割したものを配列にします。
なので、配列から判定に欲しい値を取ってきます。以下、
domain.jp/jp/
domain.jp/en/
の2サイトで分岐したいとします。test.php// 現在のURLを取得して、「/」ごとに分割します。 $urls = explode('/', $_SERVER['REQUEST_URI']); // [0]には、ドメインが入ります。 if ($urls[1] === 'jp') { echo '日本語サイト'; } else { echo '外国語サイト'; }あとは、関数化しておくと便利かも〜
test2.phpfunction is_site_page($lang) { $urls = explode('/', $_SERVER['REQUEST_URI']); if ($urls[1] === $lang) { return true; } else { return false; } }どんない切れ味のいいハサミを持っていても、ハサミの使い道をしらなければ意味がありませんね。
プログラミングってそういうことなのかも〜
- 投稿日:2020-06-04T18:08:32+09:00
wordpress スラッグ判定で分岐→出力するためのちょっとしたお話
初歩的内容ですがおさらいとしてメモ。
別に、wordpressに関して話ではなくPHP全般のお話です。
今回は、マルチサイトのサブディレクトリ型を例に取り上げます。こんな時
例えば、マルチサイトで多言語サイトを構築する際に、言語ごとのテーマは同じだが、サイトごとに出力を分岐したいことがあると思います。
ヘッダーH1タグのロゴにサイトホームをリンク設定する際には、
echo home_url();
としたら、自動で各サイトのホームリンクを取得してくれるのですが、
仮に、h1ロゴの横にテキストがあったとして、各言語ごとに出力を変えなければいけないとします。解決策1
各言語サイトにカスタムフィールドを設けて、それぞれ出力させる。
複雑な出力だったらこれでいいのでしょうが、短いテキスト飲み出力の場合、フィールドをつくったり複製したり(修正したり)と管理上面倒ばところがあります。解決策2
本題ははこちらです。
結論から言いますとexplode('', '')
を使用します。
文字列を分割して、分割したものを配列にします。
なので、配列から判定に欲しい値を取ってきます。以下、
domain.jp/jp/
domain.jp/en/
の2サイトで分岐したいとします。test.php// 現在のURLを取得して、「/」ごとに分割します。 $urls = explode('/', $_SERVER['REQUEST_URI']); // [0]には、ドメインが入ります。 if ($urls[1] === 'jp') { echo '日本語サイト'; } else { echo '外国語サイト'; }あとは、関数化しておくと便利かも〜
test2.phpfunction is_site_page($lang) { $urls = explode('/', $_SERVER['REQUEST_URI']); if ($urls[1] === $lang) { return true; } else { return false; } }どんない切れ味のいいハサミを持っていても、ハサミの使い道をしらなければ意味がありませんね。
プログラミングってそういうことなのかも〜
- 投稿日:2020-06-04T17:53:36+09:00
php incファイル設置方(備忘)
必要な箇所を切り出す
- < header >~< /header >を切り出して別ファイルに保存
- ⇒ /common/include/header.php
①パスの読込み
- 読み出し箇所に、以下を記述。
<?php include( ’ファイルのパス’ ); ?>例えば、2階層上のcommon配下に設置。
<?php include('../../common/include/header.php'); ?>
- ・・・が、このままだと、同じ階層のhtmlにしか設置できない。 (違う階層から読み出した際に、パス(../../)が変わってしまう)。。。
②ファイルパスの関数化
設置側のhtmlに、rootからの階層を認識させてやる。
- htmlファイル冒頭に、以下を記述。
<?php $path = "../../"; ?>または、
<?php $path = "../"; ?>等々、そのhtmlの、ルートからのパスを設定。
※これをしておくと、ほかのrelで読み込むcss/js等のパスの記述についても、
<?=$path?>
で簡素化することもできる。関数化したインクルードファイルを読み込み。
<?php include($path . 'common/include/header.php'); ?>以上。
①と②はセットで運用必至。これで、いろんな階層のhtmlファイルに、共通パーツを設置することが可能に!
- 投稿日:2020-06-04T17:37:56+09:00
【Laravel】ミューテタを一時的に無効にする。
メモとして残します。
以前ミューテタについての記事を投稿しました。
非常に便利なミューテタですが時には、一時的に無効にして生の値をセットしてほしいということもあります。
いろいろ調べてはみましたが、どうやらEloquentORMで一時的にミューテタを無効にするメソッドは用意されていないようです。そのため、疑似的にミューテタを無効にする方法を記述します。
■やり方
ミューテタについての例に付け加えます。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Hoge extends Model { public $isIgnoreMutator = false; public function setカラム名Attribute($value) { //isIgnoreMutatorをTrueにすれば値をそのままセット if( $this->isIgnoreMutator ){ $this->attributes['カラム名'] = $value; }else{ $this->attributes['カラム名'] = $value ?? 'hogehoge'; } } }コントローラ等で生成したこのモデルのオブジェクトの
メンバ変数$isIgnoreMutator
にtrueをセットすることで、以降の処理でfillだろうが、なんだろうが、値が、そのままセットされるようになります。■おわりに
見ての通り、ミューテタそのものは無効になっておりません(;^ω^)
あくまで疑似的に無効化しているだけのため、少し不格好です。。。
- 投稿日:2020-06-04T17:03:52+09:00
AWSで割とトレンドの技術使う大学生日記
環境構築を雑に設計
現在(2020/06/04)で公開しているサイトではAWS EC2のみを使用した設計となっているため、
それをよりトレンド風に修正していく記事です。
ちなみに作成したサイトは
http://ec2-18-180-131-224.ap-northeast-1.compute.amazonaws.com/
です。なるべくお金を掛けたくなかったのでドメインも取得せず、ロードバランサーも通信の暗号化もしておりません。
Dockerすらも使用していないので、そのあたりからやっていくつもりです。
あと、日記ですのであまり期待しないでください、よわよわなエンジニアの卵ですので。笑サービス内容
GitHubのレポジトリはhttps://github.com/maaaaakoto35/UniFood
ですのでよければスターつけてください笑ReadMeも雑に作成したのでサービス内容を記述していきます。
当初、京都産業大学の口コミサイト等は調べても出てこなかったので
当サイトの目的は、学食に対してレビューができる、ということです。この記事が完成する頃には拡張機能も追加していくつもりです。
中身のロジックはものすごく簡単なんですが、ご要望があれば
当サイトを作成した際のつまづいた点なども投稿しようかなと思っております。参考にしているサイト
当記事は下記のサイトを参考にしております。
https://dev.classmethod.jp/articles/aws-eaws-ecs-fetch-run-shell/
これから記事の追加していくつもりです。(2020/06/04)
- 投稿日:2020-06-04T17:03:52+09:00
AWSで割とモダンな技術使う大学生日記
環境構築を雑に設計
現在(2020/06/04)で公開しているサイトではAWS EC2のみを使用した設計となっているため、
それをよりトレンド風に修正していく記事です。
ちなみに作成したサイトは
http://ec2-18-180-131-224.ap-northeast-1.compute.amazonaws.com/
です。なるべくお金を掛けたくなかったのでドメインも取得せず、ロードバランサーも通信の暗号化もしておりません。
Dockerすらも使用していないので、そのあたりからやっていくつもりです。
あと、日記ですのであまり期待しないでください、よわよわなエンジニアの卵ですので。笑サービス内容
GitHubのレポジトリはhttps://github.com/maaaaakoto35/UniFood
ですのでよければスターつけてください笑ReadMeも雑に作成したのでサービス内容を記述していきます。
当初、京都産業大学の口コミサイト等は調べても出てこなかったので
当サイトの目的は、学食に対してレビューができる、ということです。
また、UI設計がスマートフォン向けになっております。この記事が完成する頃には拡張機能も追加していくつもりです。
中身のロジックはものすごく簡単なんですが、ご要望があれば
当サイトを作成した際のつまづいた点なども投稿しようかなと思っております。参考にしているサイト
当記事は下記のサイトを参考にしております。
https://dev.classmethod.jp/articles/aws-eaws-ecs-fetch-run-shell/
これから記事の追加していくつもりです。(2020/06/04)
- 投稿日:2020-06-04T16:01:33+09:00
Ubuntu18.04でPHP5.6とPHP7.4を使いたかった時のメモ
やりたいこと
- Ubuntu18.04にphp5.6とphp7.4が動く環境を用意したい
- phpenvやらphpbrew使えばなんとかなるっしょ!
- Dockerは使わないものとする
phpenvで試してみる。
anyenvでインストールされたphpenvで試した。
# phpenv install 5.6.40記録を取っていないが色々コケる。
先人達の歴史を調査
お、ちょうど良い記事見つけた。
[php][ubuntu]php7とphp5とphpenv - 釣り。IT屋。毎日爆釣。(2019-07-29)結論:phpenvなんてものはなかった。 ローカルでPHP7を使っててPHP5.xも切り替えて使いたいとかいう場合はdockerでカレントをマウントして使え。
ワロタ。
phpbrew を使ってみる
参考になる資料
https://gist.github.com/marulitua/f8932064ec5bfe6a5be9fadac7c5a141それでも色々コケる
phpbrewでPHP 7.4を入れるのにいろいろつまづいた
私の環境は
$ brew install openssl $ brew install libxml2以外にも
$ brew install curl $ brew install libxslt $ brew install libzipが必要だった。
$ brew ls <パッケージ名>でbrew installのインストール先を調べられるので
各位、PATHは変えて下さい。$ export PATH="/home/linuxbrew/.linuxbrew/Cellar/openssl@1.1/1.1.1g/bin:$PATH" $ export PATH="/home/linuxbrew/.linuxbrew/Cellar/libxml2/2.9.10_1/bin:$PATH" $ export PATH="/home/linuxbrew/.linuxbrew/Cellar/curl/7.70.0/bin:$PATH" $ export PATH="/home/linuxbrew/.linuxbrew/Cellar/libxslt/1.1.34_1/bin:$PATH" $ export PATH="/home/linuxbrew/.linuxbrew/Cellar/libzip/1.6.1_1/bin:$PATH" $ export PKG_CONFIG_PATH="/home/linuxbrew/.linuxbrew/Cellar/openssl@1.1/lib/pkgconfig" $ export LDFLAGS="-L/home/linuxbrew/.linuxbrew/Cellar/openssl@1.1/lib" $ export CPPFLAGS="-I/home/linuxbrew/.linuxbrew/Cellar/openssl@1.1/include" $ export PHP_BUILD_CONFIGURE_OPTS="--with-openssl=$(brew --prefix openssl) --with-libxml-dir=$(brew --prefix libxml2) --with-curl=$(brew --prefix curl) --with-libxslt-config=$(brew --prefix libxslt) --with-libzip=$(brew --prefix libzip)"Enjoy!よし!
PHP5.6とPHP7.4が同居できているのかチェック
無事動作完了!
- 投稿日:2020-06-04T14:18:02+09:00
Node.jsをPHPで書き換えてみた
N予備校のプログラミング入門コース3章で作成した「秘密の匿名掲示板」をPHPで書き換えてみたので,その記録です。
きっかけ
スラスラわかるPHP(志田 仁美 著 翔泳社 刊)をやってみて,
「この間作った匿名掲示板,書き換えられるのでは?」
と思ったので。
スラスラわかるPHPをやってみた所感はこちら早速書き換える
仕様を変更したところ
- 認証の方式
おそらくN予備校ではBasic認証(alartみたいに上に出てくるやつ)を使っていたと思いますが, 書き換えの際にはスラスラわかるPHPのように,DBを使用する方式としました。
- TrackingID
N予備校で作成した際はCookieを利用してTrackingIDを実装していたと思いますが,セッションを利用する方式としました。その他細かい変更点はあると思うのですが,大きく変更したのはこちら2点です。
書き換えで苦労したところ
記事とユーザーの情報をどう関連付けるか
Laravelでは
userテーブル->id = articleテーブル->userid
としておけば情報を結びつけてくれていましたが,
今回はそれができない…ということで,とても苦労しました。
結局,セッションに該当するユーザー情報をもたせることで実現しました。
今回の場合,ユーザーのidとユーザー名が該当しました。TrackingIDをどう実現するか
Cookieを使うべきか?それともセッションを使うべきか?…という,なんとも根本的なところから悩みました。
こればかりは一人で解決できず,友人にも意見を求めたところ,セッションで実現でいいのでは?という意見をもらったので,結局セッションを使うということで,決着。
ざっくりとした実装方法は下記の通りです。ログイン時にランダムな文字列を予めセッションにセットしておく(仮にsecretidとします)
→書き込み時にpostテーブルに格納
→IDとして表示するのはsecretidの値所感
思ったよりも難しかった気がする
これはただ単に私の実力不足に起因するものだと思いますが,思ったよりも難しかった気がします。
勉強になった
セッションか?Cookieか?を考えたり,どうやってuserテーブルの情報をpostテーブルに入れるか…?を考えたり。
今まではmigrationに頼っていた部分がSQLになるので,SQLの勉強をしてみたり。
どうやって実現していたか?のロジックを一から考え直してみたり…
簡単に実現することはできませんでしたが,自分の実力不足を痛感するとともに,勉強になったことは多かったように思います。
- 投稿日:2020-06-04T14:15:30+09:00
LaravelさんはどうやってAJAXかどうか区別してResponseでJSON返してくるの?
我らがBOOM TECH CAFEの若きエース、 @miriwo さんの最新記事「Laravel API データポスト時のバリデーションで弾かれた後、http://127.0.0.1:8000/にリダイレクトしてしまう」を読んで、「おや? ワイのLaravelちゃんはAJAXでアクセスすると普通にエラーのJSON返してくるんやが……?」と思って、調べてみました。
そもそもAJAXかどうかどうやって判定しているの?
Laravelの公式ドキュメント日本語訳にはこんなふうに書かれてるわけですよ。
https://readouble.com/laravel/5.5/ja/validation.html※一応、Laravel5.5での話をしていますが、後方バージョンでもだいたい同じだと思います。
伝統的なHTTPリクエストの場合は、リダイレクトレスポンスが生成され、一方でAJAXリクエストにはJSONレスポンスが返されます。
ほうほう、そうなんすね。
んで、AJAXリクエストか伝統的なHTTPリクエストかどうかはどこで判別してるねんと言うと、ここなわけです。vendor/laravel/framework/src/Illuminate/Http/Request.php(226-234)/** * Determine if the request is the result of an AJAX call. * * @return bool */ public function ajax() { return $this->isXmlHttpRequest(); }vendor/symfony/http-foundation/Request.php(1820-1833)/** * Returns true if the request is a XMLHttpRequest. * * It works if your JavaScript library sets an X-Requested-With HTTP header. * It is known to work with common JavaScript frameworks: * * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript * * @return bool true if the request is an XMLHttpRequest, false otherwise */ public function isXmlHttpRequest() { return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); }なるほど、つまりRequest Headerに
X-Requested-With: XMLHttpRequest
ってのが入ってたらAJAXリクエストとして扱うってわけね……え、そんなHeaderどこで入れてたっけ……?そして我々は
X-Requested-With
を求めて旅に出たソースグレップするとこんなところにいるのを見つけます。
resources/assets/js/bootstrap.js(16-24)/** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';なるほど、axiosのHeaderにデフォルトで入るようにしてるのね……え、でもBootstrap使ってないんやが……
そこで、肝心のAJAXしてるjsをのぞいてみると……require('./bootstrap');バッチリrequireしてたーーーーーー!!
やー、無意識って怖いっすね。今回はいい勉強になりました。
- 投稿日:2020-06-04T14:15:30+09:00
LaravelさんはどうやってAJAXかどうか区別してバリデーションエラーレスポンスでJSONを返してくるの?
我らがBOOM TECH CAFEの若きエース、 @miriwo さんの最新記事「Laravel API データポスト時のバリデーションで弾かれた後、http://127.0.0.1:8000/にリダイレクトしてしまう」を読んで、「おや? ワイのLaravelちゃんはAJAXでアクセスすると普通にバリデーションエラーのJSON返してくるんやが……?」と思って、調べてみました。
そもそもAJAXかどうかどうやって判定しているの?
Laravelの公式ドキュメント日本語訳にはこんなふうに書かれてるわけですよ。
https://readouble.com/laravel/5.5/ja/validation.html※一応、Laravel5.5での話をしていますが、後方バージョンでもだいたい同じだと思います。
伝統的なHTTPリクエストの場合は、リダイレクトレスポンスが生成され、一方でAJAXリクエストにはJSONレスポンスが返されます。
ほうほう、そうなんすね。
んで、AJAXリクエストか伝統的なHTTPリクエストかどうかはどこで判別してるねんと言うと、ここなわけです。vendor/laravel/framework/src/Illuminate/Http/Request.php(226-234)/** * Determine if the request is the result of an AJAX call. * * @return bool */ public function ajax() { return $this->isXmlHttpRequest(); }vendor/symfony/http-foundation/Request.php(1820-1833)/** * Returns true if the request is a XMLHttpRequest. * * It works if your JavaScript library sets an X-Requested-With HTTP header. * It is known to work with common JavaScript frameworks: * * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript * * @return bool true if the request is an XMLHttpRequest, false otherwise */ public function isXmlHttpRequest() { return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); }なるほど、つまりRequest Headerに
X-Requested-With: XMLHttpRequest
ってのが入ってたらAJAXリクエストとして扱うってわけね……え、そんなHeaderどこで入れてたっけ……?そして我々は
X-Requested-With
を求めて旅に出たソースグレップするとこんなところにいるのを見つけます。
resources/assets/js/bootstrap.js(16-24)/** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';なるほど、axiosのHeaderにデフォルトで入るようにしてるのね……え、でもBootstrap使ってないんやが……
そこで、肝心のAJAXしてるjsをのぞいてみると……require('./bootstrap');バッチリrequireしてたーーーーーー!!
やー、無意識って怖いっすね。今回はいい勉強になりました。
- 投稿日:2020-06-04T12:20:53+09:00
【エラー解決備忘録】[Laravel6.0]migrate時のエラー解決方法の巻き
本記事の目的
Laravel勉強中の筆者がmigrate時に経験したエラーと解決方法をまとめました。
筆者と同様に勉強中の方やエラーで悩まれている方の参考になればと思い投稿いたしました。
なお勉強中のため内容が足りなかったり、誤った記載があるかもしれません。
その際は指摘をビシバシしてもらえると嬉しいです!
宜しくお願いいたします。環境
- macOS Catalina(バージョン10.15.4)
- Laravel Framework 6.18.18
- MAMP
遭遇したエラー
事前にphpMyAdminで作成したデータベースにファイルを追加するため、
ターミナルで%php artisan migrate
を実行すると
「DBサーバーに接続できないよ!」「そのようなファイルまたはディレクトリはないよ!」
下記エラーメッセージで指摘を受けました。Illuminate\Database\QueryException: SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from information_schema.tables where table_schema = laravel_task and table_name = migrations and table_type = 'BASE TABLE') at・・・・/vendor/laravel/framework/src/Illuminate/Database/Connection.php:669 665| // If an exception occurs when attempting to run a query, we'll format the error 666| // message to include the bindings with SQL, which will make this exception a 667| // lot more helpful to the developer instead of just the database's errors. 668| catch (Exception $e) { > 669| throw new QueryException( 670| $query, $this->prepareBindings($bindings), $e 671| ); 672| } 673| Exception trace: 1 PDOException::("SQLSTATE[HY000] [2002] No such file or directory") ・・・・/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:unix_socket=/Applications/MAMP/tmp/mysql/mysql.sock;dbname=〜省略〜[]) ・・・・/vendorvendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.解決方法
結論、DB_HOSTを「127.0.0.1」から「localhost」に修正するとエラーが解消されマイグレーションができました。
※ターミナル %php artisan migrate Migrating: 2020_06_04_****_create_名称_table Migrated: 2020_06_04_****_create_名称_table (0.04 seconds)(before)config>database.php〜省略〜 'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 〜省略〜(after)config>database.php〜省略〜 'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', '3306'), 〜省略〜解決に至るまでのステップ
今回、下記ステップで作業を行いエラーの解消とphpMyAdminへのデータ登録が実現できました。
①.evファイルの確認
まず、.evファイルの中身を確認してみると以下の記載でした。〜省略〜 DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=データベースの名前 DB_USERNAME=ユーザーネーム DB_PASSWORD=パスワード 〜以下省略〜②次に、phpMyAdmiでMySQLの設定を確認
Hostの指定が「localhost」であることを確認。MySQL can be administered with phpMyAdmin. To connect to the MySQL server from your own scripts use the following connection parameters: Host localhost Port 3306 〜省略〜③config>database.phpを確認
(before)config>database.php'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 〜以下省略とHostの指定が.evとphpMyAdminで異なっていることがわかりました。
エラーの原因としてHostの指定が異なっていたと考え、
Hostの指定を統一するとエラーが解消されmigrateを無事に行うことができました。※ターミナル %php artisan migrate Migrating: 2020_06_04_****_create_名称_table Migrated: 2020_06_04_****_create_名称_table (0.04 seconds)参考サイト
- 投稿日:2020-06-04T12:16:03+09:00
【Laravel】EloquentORMで任意のカラム(要素)にセットした際に自動的に加工&セットする。(ミューテタ)
メモとして残します。
LaravelのEloquentORMを用いてデータの取得、保存をすると思います。
今回は保存する際に任意のカラムをセットしたら、その際に自動的に値の加工してもらうようにプログラムします。(=ミューテタの定義)■やり方
任意のカラム(要素)にミューテタを仕込みたいモデルに下記の例のようにメソッドを追記。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Hoge extends Model { public function setカラム名Attribute($value) { //$valueにセットしたときの値が入っている。 $this->attributes['カラム名'] = $value ?? 'hogehoge'; } }上記の例では要素にセットされた値がNullの場合は、
hogehoge
という文字列をセットしました。ミューテタを定義する利点としては、
モデル呼び出し元で同じような処理を記述する必要がなくなります。
どんな場合も共通して加工してセットしたいという場合に役に立ちます。
- 投稿日:2020-06-04T12:16:03+09:00
【Laravel】eroquentORMで任意のカラム(要素)にセットした際に自動的に加工&セットする。(ミューテタ)
メモとして残します。
LaravelのEroquentORMを用いてデータの取得、保存をすると思います。
今回は保存する際に任意のカラムをセットしたら、その際に自動的に値の加工してもらうようにプログラムします。(=ミューテタの定義)■やり方
任意のカラム(要素)にミューテタを仕込みたいモデルに下記の例のようにメソッドを追記。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Hoge extends Model { public function setカラム名Attribute($value) { //$valueにセットしたときの値が入っている。 $this->attributes['カラム名'] = $value ?? 'hogehoge'; } }上記の例では要素にセットされた値がNullの場合は、
hogehoge
という文字列をセットしました。ミューテタを定義する利点としては、
モデル呼び出し元で同じような処理を記述する必要がなくなります。
どんな場合も共通して加工してセットしたいという場合に役に立ちます。
- 投稿日:2020-06-04T10:53:39+09:00
macにLaravelを導入
はじめに
現在通っているプログラミングスクールTECH CAMPでは、Ruby on Railsを用いたアプリケーションの開発を行ってきました。スクールも終盤になり、新しいプログラミング言語、フレームワークを使用してみたいと思って今回「laravel」を導入してみました。
PHPとLaravel
PHPとLaravelについては、現在、書籍やドキュメントで勉強中です。見聞きした範囲で書くとPHPはプログラミング言語の一つで、サーバーサイドで動的なウェブページを作成することが得意なので、Webアプリケーションに特化している、という感じ。
そしてLaravelは、PHPを使用して作成するWebアプリケーション用のフレームワークの一つ。PHPのフレームワークにはコレ!!という定番はまだなく、いくつか選択肢があるようです。Laravelは、他に比べて歴史は浅いものの人気のフレームワークの一つで、コードをスッキリ書けるらしい。Rubyに対してRails、PHPに対してLaravel、そういう認識です。達成したいこと
Laravelで新しいプロジェクトを立ち上げ、ローカルから確認すると以下のようなトップ画像が表示されます。Railsで言うところの「Yay! You're on Rails!」に相当する画面です。このおしゃれな画面を表示させることを達成したい。
手順
プログラミングスクールに通っているので、「Homebrew」のインストールなどは完了しています。よって、あくまで私の環境での手順になりますが、以下のように進めます。
- brewのバーション確認
- Composerのインストール
- PHPのインストール
- Laravelのインストール
- Laravelコマンドを利用するためにPATHを通す
- Laravelプロジェクト用のディレクトリを作成
- Laravelプロジェクトの新規作成
- ローカルサーバーで確認
参考にさせていただいた記事
Laravel公式ドキュメント
Mac用 Laravelをインストールする @S-Masakatsu様
Mac Laravelの環境構築時にエラーが出た話 vol.1 @miriwo様
ターミナル上で「laravel」コマンドを使えるようにパスを通す @m24様brewのバーション確認
スクールのカリキュラムでHomebrewはすでにインストール済みですが、念のため確認を行いました。インストールできているか確認するのには、バーション確認のコマンドを打つのが定番のようですね。私の場合は、バージョンが表示されたのでOKということになりました。
ターミナル$ brew -vComposerのインストール
ComposerはPHPのパッケージ管理ツールと呼ばれるもので、必要なライブラリを一括でインストールできるもののようです。RubyのライブラリはGemと呼ばれ、それらをまとめてインストールするためにBundlerを使用しました。ターミナルで「bundle install」したやつです。実際にBundlerとComposerでは挙動が異なるようですが、大まかな認識としてはそういったものになります。
つまり、Laravelでアプリケーション開発をするのに必須!というわけです。ターミナル$ brew install composer正しくインストールが完了すれば、以下のコマンドでComposerのバージョン確認を行えます。
ターミナル$ composer -vPHPのインストール
PHPはmacには標準で入っているので、この手順は飛ばしてLaravelをインストールしてもOKだとは思います。
しかし、私のmacに入っていたPHPのバージョンが古かったのか、Laravelをインストールしようとすると以下のようなエラーが発生しました。ターミナルこちらのエラー文はイメージです。実際のエラー文は残し忘れてしまいました。。。 $ composer global require laravel/installer Changed current directory to /Users/shun/.composer Using version ^3.0 for laravel/installer ./composer.json has been created Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages. Problem 1 - laravel/installer v3.0.1 requires ext-zip * -> the requested PHP extension zip is missing from your system. - laravel/installer v3.0.0 requires ext-zip * -> the requested PHP extension zip is missing from your system. - Installation request for laravel/installer ^3.0 -> satisfiable by laravel/installer[v3.0.0, v3.0.1]. Installation failed, deleting ./composer.json.そこで、参考記事を元にPHP7.4をインストールすることにしました。
ターミナル$ brew install php@7.4同じく正しくインストールされているか、バージョン確認を行っておきます。
ターミナル$ php -vLaravelのインストール
本命であるLaravelをインストールします。
ターミナル$ composer global require "laravel/installer"同じくダウンロードできたかバージョン確認を行うとLaravelについては以下のように表示されてうまくバージョン確認ができません。
ターミナル$ laravel -v zsh: command not found: laravelこれは、Laravelコマンドを使用するためのPATHが通っていないことを表しているようです。ComposerやPHPと違って、インストールしただけでは動いてくれません。これまでバージョン確認はインストールが完了したかの確認という認識でしたが、コマンドが扱えるようになっているかの確認でもあったのですね。
Laravelコマンドを利用するためにPATHを通す
PATHを通すのもターミナル操作となります。
ターミナル$ export PATH=$HOME/.composer/vendor/bin:$PATH上のようにターミナルに入力するだけで「.bash_profile」に書き込まれた状態になるようです。スクールでは、「vim -i」で直接編集する方法を学んでいましたが、ターミナルに打ち込むだけで完了するのは便利ですね。とはいえ変更状態を反映させるには以下のコマンドも必要になります。
ターミナル$ source ~/.bash_profileLaravelプロジェクト用のディレクトリを作成
スクールでは、「Projects」というディレクトリにRailsアプリケーションを保存していました。そこでLaravelアプリケーション用のローカルディレクトリを作ります。これは人によってディレクトリの場所や名前は異なると思います。
私は、Userディレクトリ内に「laravel」という名前のディレクトリを作りました。Laravelプロジェクトの新規作成
Laravelアプリケーションを新規作成します。まずはアプリケーションの保存先へディレクトリを移動します。私の場合は、先ほど作成した「Laravel」ディレクトリへ移動します。
ターミナル$ cd laravel新規作成コマンドは、Railsとよく似ています。今回はオプション等は付けずに名前だけ付けます。laravel newに続けて任意のアプリケーション名を書きますが、今回は「sample_laravel」としました。
ターミナル$ laravel new laravel_sampleローカルサーバーで確認
作成が完了したら、ローカルサーバーを起動してブラウザからトップ画面を確認します。ローカルサーバーの起動コマンドはRailsとは異なります。
ターミナル$ php artisan serve Laravel development server started: <http://127.0.0.1:8000>ローカルサーバー起動時にターミナルに出力されているURL「http://127.0.0.1:8000」 にアクセスします。以下の画面が表示されれば成功です。
VSコードでもアプリケーションディレクトリを開くことができます。
次に達成したいこと
laravelの導入が完了したので、次はGit管理やPHPの記述が楽になりそうなVSコードのライブラリを探そうと思います。いつでもコードが書ける状態になったら本格的にPHPの勉強と開発に着手します。ここまで読んでいただきありがとうございました。
- 投稿日:2020-06-04T07:32:26+09:00
[mysql,laravel]あるカラムで重複を持つレコードだけをまとめずに全て呼び出す方法
何をした?
operationsテーブルから"第三工種"というカラムで重複するレコードに付随しているデータの塊を作りたいと考えました。
具体的には"第三工種"に"土工"というレコードが複数ある場合、それ以降の”第四工種”以降が"埋め戻し"のものや”掘削"のものなどに分岐します。
それを”第三工種に土工を持つもの全て"で一括りの配列にして、呼び出したいと考えました。失敗した方法
ひとことで言うと、クエリによる処理でなく配列処理で対応しようとしたらドツボにはまりました。
※詳細(分かりにくいので、お急ぎの方は読まなくて大丈夫です)
まずテーブルから全レコードを呼び出し、
distinct()
で重複を絞った後、その"id"を持つ"第三工種"を抜き出します。その"第三工種"のレコードを持つものをforeach
とarray_keys()
により、distinct()
してないものと比較することで新しい1次配列を作り、そこからviewに呼び出そうとしました。→
array_key()
はオブジェクトに対して使えないというエラーで挫折しました。
コレクションを使えばいけるかもしれなかったですが、1次配列にしてしまうと呼び出しも厄介なので中断しました。→代案で"id"を抽出した後、全て入ったデータから
array_column()
でそれぞれのカラムを抜き出して、呼び出しの時に該当のidを使って呼び出すことを考えましたが、array_column()
もオブジェクトに対して使えないというエラーが出たので中断しました。解決方法
下記のsqlをコントローラで書き出して、”第三工種”が同じものだけの配列にまとめることで対応しました。
sequelproのクエリで実行した記述SELECT operations.id, operations.third_operation_class FROM -- サブクエリで件数を取得 ( SELECT third_operation_class, COUNT(third_operation_class) AS CNT FROM operations GROUP BY third_operation_class ) AS third_operation_class_CNT -- 自己結合 INNER JOIN operations ON third_operation_class_CNT.third_operation_class = operations.third_operation_class AND third_operation_class_CNT.CNT > 0;#上記の結果 id third_operation_class 8 お試し3 9 河川土工 16 土工 17 土工 18 土工 19 土工 20 土工 21 土工 22 土工その後
この記述で他のカラムも呼び出して、"third_operation_class"が同じものだけでまとめることで、欲しい値だけの配列ができるので、そこにforeachでインデックス番号をふって、そのインデックス番号でページを分けて表示しました。
参考
- 投稿日:2020-06-04T02:42:33+09:00
Call to undefined method Illuminate\Database\Eloquent\Builder::toArray() の解決例
エラー文の様子
どんなエラー?
app/Http/Controllers/RecordTimingControllerpublic function syousai_matome($id) { $third_operation_class_id = Operation::query()->where('record_timing_id',$id)->distinct()->select('third_operation_class'); return $third_operation_class_id->toArray(); }上記のように、コントローラで構文的に問題なくデータベースから情報を取得しようとしたら出てきたものです。
原因
データベースの情報をクエリ(今回は抽出)した状態で止まっており、その抽出したものをコントローラに呼び出していなかったため起きました。
※クエリとは:データベース上で特定の条件に合致したデータを検索したり、置換や削除などを行ったりすること。
解決策
下記のように、クエリ(今回は抽出)した状態のものを
->get()
で取得すればエラーが解消されました。app/Http/Controllers/RecordTimingControllerpublic function syousai_matome($id) { $third_operation_class_id = Operation::query()->where('record_timing_id',$id)->distinct()->select('third_operation_class')->get(); return $third_operation_class_id->toArray(); }
- 投稿日:2020-06-04T02:01:05+09:00
コーディング規約チェッカーを運用してみた使用感 (C#(Unity) JavaScript(CocosCreator), PHP(FuelPHP))
はじめに
コーディング規約を自動的にチェックしてくれるコーディング規約チェッカーは、
複数人で開発する際の可読性向上や各人の書き方の癖を平準化できる便利なものです。コードレビュー等で生産性のない指摘をし合わないよう、
チェックはシステムにやってもらうというのはとても合理的。一方で、規約に縛られた書き方を窮屈に感じたりすることもあると思います。
そこで、各開発環境における規約チェッカーを使ってみた所感を書き残します。C# (Visual Studio)
規約チェッカーであるStyleCopは、
・プロジェクトにNuGetパッケージとしてインストール
・Visual Studioの拡張機能としてインストールという2種類の方法があります。
前者が適用できるならそちらのほうがベターなようです。・プロジェクトにNuGetパッケージとしてインストール
NuGetでインストールするとプロジェクト内に管理される形となり、
自動でチェックが走るため扱いやすいです。また、NuGetではStyleCopの後継であるStyleCop.Analyzersが使用できるため、
その点でもオススメできる方法です。規約のカスタマイズはソリューションにファイルを追加することで可能です。
https://anderson02.com/cs/cs-rules/cs-rules19/・Visual Studioの拡張機能としてインストール
拡張機能としてインストールした場合はVisualStuioの右クリックメニューから、
「Run StyleCop」を選択することでファイルのチェックを走らせることができます。規約のルール設定は右クリックメニューの「StyleCop Settings」からGUIで行うことができ、
編集結果はXMLファイル「Settings.StyleCop」として保存されるため、
このファイルをGitで共有するなどすれば、
チームでカスタマイズされたルールを共有できます。Unityでの利用
Unityのプロジェクトで利用する場合、NuGetによる導入はやや煩雑です。
https://t-tutiya.hatenablog.com/entry/2019/11/07/200330拡張機能のほうはテキストファイルを検査するだけなので、
Unityプロジェクトであっても、通常のC#プロジェクトと同様の使い方ができます。Unityの場合は、とりあえず拡張機能のほうを使うのが現状は無難かと思います。
JavaScript (Visual Studio Code)
C#と違ってフレームワーク等により様々な書き方の流儀が存在するJavaScriptにおいては、
チェッカーもいくつかあり、それを適用するIDEも選択肢が多いため、
今回はVisual Studio Code + ESLintという組み合わせを選択しました。npmでESLintパッケージをプロジェクトにインストールし、
Visual Studio Codeの拡張機能としてのESLintをインストールする、という流れです。
https://qiita.com/yohei_nakamura/items/4cf4876b3e36a46f3750設定は「.eslintrc.json」というファイルに記述します。
npmのパッケージ設定とルールファイルをGitで共有することでチーム内でルールが共通化できる点については、StyleCopとほぼ同じです。チェッカー本体とIDEの連携機能を別々にインストールする必要がある、という点がStyleCopとは異なります。
上手く動作しないときの原因切り分けが少々厄介です。Cocos Creatorでの利用
Cocos Creatorで利用する場合であっても、C#のようにプログラム側にプロジェクトの概念がないため(単なるJavaScriptファイル群)、
Webサービス等でJavaScriptを使用する場合と同じ使い方ができます。ただし、Cocos Creatorの形式(クラス定義型とか)とルールがマッチしない部分もあるので、
必要に応じてルールをカスタマイズする等の対応は必要になります。PHP (Visual Studio Code)
PHPの場合もJavaScriptのように、複数のポリシーとIDEの組み合わせが想定されます。
今回はPHPの標準っぽさがあるPSR-2を使ってみました。導入等は他記事を参照してください。
https://mseeeen.msen.jp/php-codesniffer-with-vscode/PHPの場合、使用するフレームワークそのものが規約のような縛りがあったりするので、
ルールを調整して運用する必要がありそうです。FuelPHPでの利用
FuelPHPでも、フレームワークでクラスの書き方(スネークケースで書くなど)等が決まっているので、パスカルケースを使うようなルールになっているとそれだけで正しくかけないといった問題が発生します。
FuelPHP準拠のルールを使用するか、PSR-2のようなルールをベースにフレームワークに合わせてカスタマイズしていくか、
いずれにしろ運用に工夫が必要になります。
おわりに 複数言語の比較
複数言語を比較してみると、言語やチェッカーの特性などが見えてきます。
C#は.NET Frameworkの統一感があるため、規約チェッカーを導入する場合もあれこれ悩む必要が
少なかったです。Unityで公式に使えるようになると、もっといいですね。
JavaScriptとPHPは言語仕様が緩いことに加え、フレームワーク毎にまったく違う書き方があり、またエディタのデファクトも決定打がないため、様々な選択肢を試行錯誤する必要がありました。
カッコで改行するかどうか、など、細かいところも差異がでてくるため、しっかりと統一したルールを策定し、共有し、運用することが重要だと思います。
- 投稿日:2020-06-04T02:01:05+09:00
プログラミング言語毎のコーディング規約チェッカー使用感 (C#(Unity) JavaScript(CocosCreator), PHP(FuelPHP))
はじめに
コーディング規約を自動的にチェックしてくれるコーディング規約チェッカーは、
複数人で開発する際の可読性向上や各人の書き方の癖を平準化できる便利なものです。コードレビュー等で生産性のない指摘をし合わないよう、
チェックはシステムにやってもらうというのはとても合理的。一方で、規約に縛られた書き方を窮屈に感じたりすることもあると思います。
そこで、各開発環境における規約チェッカーを使ってみた所感を書き残します。C# (Visual Studio)
規約チェッカーであるStyleCopは、
・プロジェクトにNuGetパッケージとしてインストール
・Visual Studioの拡張機能としてインストールという2種類の方法があります。
前者が適用できるならそちらのほうがベターなようです。・プロジェクトにNuGetパッケージとしてインストール
NuGetでインストールするとプロジェクト内に管理される形となり、
自動でチェックが走るため扱いやすいです。また、NuGetではStyleCopの後継であるStyleCop.Analyzersが使用できるため、
その点でもオススメできる方法です。規約のカスタマイズはソリューションにファイルを追加することで可能です。
https://anderson02.com/cs/cs-rules/cs-rules19/・Visual Studioの拡張機能としてインストール
拡張機能としてインストールした場合はVisualStuioの右クリックメニューから、
「Run StyleCop」を選択することでファイルのチェックを走らせることができます。規約のルール設定は右クリックメニューの「StyleCop Settings」からGUIで行うことができ、
編集結果はXMLファイル「Settings.StyleCop」として保存されるため、
このファイルをGitで共有するなどすれば、
チームでカスタマイズされたルールを共有できます。Unityでの利用
Unityのプロジェクトで利用する場合、NuGetによる導入はやや煩雑です。
https://t-tutiya.hatenablog.com/entry/2019/11/07/200330拡張機能のほうはテキストファイルを検査するだけなので、
Unityプロジェクトであっても、通常のC#プロジェクトと同様の使い方ができます。Unityの場合は、とりあえず拡張機能のほうを使うのが現状は無難かと思います。
JavaScript (Visual Studio Code)
C#と違ってフレームワーク等により様々な書き方の流儀が存在するJavaScriptにおいては、
チェッカーもいくつかあり、それを適用するIDEも選択肢が多いため、
今回はVisual Studio Code + ESLintという組み合わせを選択しました。npmでESLintパッケージをプロジェクトにインストールし、
Visual Studio Codeの拡張機能としてのESLintをインストールする、という流れです。
https://qiita.com/yohei_nakamura/items/4cf4876b3e36a46f3750設定は「.eslintrc.json」というファイルに記述します。
npmのパッケージ設定とルールファイルをGitで共有することでチーム内でルールが共通化できる点については、StyleCopとほぼ同じです。チェッカー本体とIDEの連携機能を別々にインストールする必要がある、という点がStyleCopとは異なります。
上手く動作しないときの原因切り分けが少々厄介です。Cocos Creatorでの利用
Cocos Creatorで利用する場合であっても、C#のようにプログラム側にプロジェクトの概念がないため(単なるJavaScriptファイル群)、
Webサービス等でJavaScriptを使用する場合と同じ使い方ができます。ただし、Cocos Creatorの形式(クラス定義型とか)とルールがマッチしない部分もあるので、
必要に応じてルールをカスタマイズする等の対応は必要になります。PHP (Visual Studio Code)
PHPの場合もJavaScriptのように、複数のポリシーとIDEの組み合わせが想定されます。
今回はPHPの標準っぽさがあるPSR-2を使ってみました。導入等は他記事を参照してください。
https://mseeeen.msen.jp/php-codesniffer-with-vscode/PHPの場合、使用するフレームワークそのものが規約のような縛りがあったりするので、
ルールを調整して運用する必要がありそうです。FuelPHPでの利用
FuelPHPでも、フレームワークでクラスの書き方(スネークケースで書くなど)等が決まっているので、パスカルケースを使うようなルールになっているとそれだけで正しくかけないといった問題が発生します。
FuelPHP準拠のルールを使用するか、PSR-2のようなルールをベースにフレームワークに合わせてカスタマイズしていくか、
いずれにしろ運用に工夫が必要になります。
おわりに 複数言語の比較
複数言語を比較してみると、言語やチェッカーの特性などが見えてきます。
C#は.NET Frameworkの統一感があるため、規約チェッカーを導入する場合もあれこれ悩む必要が
少なかったです。Unityで公式に使えるようになると、もっといいですね。
JavaScriptとPHPは言語仕様が緩いことに加え、フレームワーク毎にまったく違う書き方があり、またエディタのデファクトも決定打がないため、様々な選択肢を試行錯誤する必要がありました。
カッコで改行するかどうか、など、細かいところも差異がでてくるため、しっかりと統一したルールを策定し、共有し、運用することが重要だと思います。
- 投稿日:2020-06-04T01:10:08+09:00
初学者が躓くreturnの概念
プログラミング初心者が躓くreturn(返り値)
なんとか理解することができたので
忘れないようにアウトプットも兼ねて投稿しておくreturnの意味は大きく2つ
・処理を終わらせる
・値を返す
値を返すという概念はなかなか理解しづらいですが
私は関数で処理して得た値を使いまわしたい時はretrunで返り値を持たせる!と覚えている試しに2パターン関数を書いてみる
returnなし$num = 10; function a($i) { $i * $i; } echo a($num); //結果は出力なしreturnあり$num = 10; function a($i) { return $i * $i; } echo a($num); //100結果returnをつけてやらないと、全く使いまわすことのできない関数となってしまう
$num = 10; function a($i) { echo $i * $i; } a($num); //100上記の場合は関数内で処理が完結しているため、そのまま出力しても問題ないが
処理結果を別の処理では使いまわせないため使い方は限定的だ
なぜreturnで躓くか
これは私の勝手な妄想である
プログラミングを勉強する方々はまずはじめにHTML・CSSを勉強するとおもう
そこからJavaScriptやphpを学習する人がほとんどのため、どの教材でも「HTMLに出力してみよう」
ここからプログラミングを試していることがほとんどである
HTMLに出力するこということは、出力して終わりである
関数の値を他で使いまわさないのだ実際にプログラミングを使って開発をする場合、ほとんどの関数には返り値をもたし他で使いまわせるようにするらしい
まとめ
WEB制作からプログラミングを勉強した方は躓きやすい
プログラミング言語から勉強した方は、理解しやすい私は最初WEB制作を勉強したのでガッツリ躓いた
- 投稿日:2020-06-04T00:20:13+09:00
【Laravel】外部制約キーを設定しているデータを論理削除してみた
はじめに
外部制約キーを設定しているデータは、通常テーブルから削除する事ができない。
ただ、DBのテーブルからデータを削除には論理削除と物理削除の2種類ある。
先ほど挙げた通常の削除とは物理削除の事を指します。
では、物理削除の場合はどうなるのか、検証していくぅ!マイグレーション作成
外部制約キーを使用するために、まずテーブルを2つ用意しましょう。
親テーブルには論理削除の為に、deleted_atカラムを用意しましょう。CreateSchoolsTable/** * Run the migrations. * * @return void */ public function up() { Schema::create('schools', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->softDeletes('deleted_at', 0); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('parent'); }子テーブルにはForeignKeyを指定しましょう。
CreateStudentsTable/** * Run the migrations. * * @return void */ public function up() { Schema::create('students', function (Blueprint $table) { $table->id(); $table->foreignId('school_id')->constrained(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('child'); }はいこれでおけい。
Model作成
はい次ぃー、Modelの作成をしていきましょう。
親テーブルのモデルは論理削除を行いたいので、SoftDeleteを使用します。Schooluse Illuminate\Database\Eloquent\SoftDeletes; class School extends Model { use SoftDeletes; }次に子テーブルのモデルのカラムは親のテーブルのIDだけですので、これだけ。
Studentclass Student extends Model { protected $fillable = [ 'school_id' ]; }削除してみる
まずは、外部制約キーが動いているか確認する為に物理削除してみる。
まずはSchoolモデルのSoftDeleteをコメントアウトして、削除してみた。
英語で何て書いてあるかよく分かりませんが、外部制約キーの関係で削除できませんと書いてあるのでしょう。次にSchoolモデルのSoftDeleteをコメントアウトを解錠して論理削除して見ましょう。
School{ "id":1, "created_at":null, "updated_at":"2020-06-03T14:59:17.000000Z", "deleted_at":"2020-06-03T14:59:17.000000Z" }deleted_atに論理削除された時刻が挿入されています、論理削除成功ですね。
ちなみに、Schoolのidが1のデータはDB上には残っていますが、取得をしようとするとnullで返ってきます。
しっかりしていますね。結果
DBからデータが無くなっている訳ではないんだから論理削除できて当たり前だよね。
また気になることがあれば検証していきますぅ!
ではまた。
- 投稿日:2020-06-04T00:18:25+09:00
Laravel API データポスト時のバリデーションで弾かれた後、http://127.0.0.1:8000/にリダイレクトしてしまう
目的
- Laravel APIのバリデーションでpostデータが弾かれた時に初期画面にリダイレクトしてしまう問題を解決した話をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.3) ハードウェア MacBook Pro (16-inch ,2019) プロセッサ 2.6 GHz 6コアIntel Core i7 メモリ 16 GB 2667 MHz DDR4 グラフィックス AMD Radeon Pro 5300M 4 GB Intel UHD Graphics 630 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いて導入 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いて導入 前提情報
- LaravelをAPIサーバとして使用してる。
http://127.0.0.1:8000/api/infomation
にDBに格納して欲しいデータを持たせてpostした時にバリデーションチェックを行う実装をした。バリデーションにより弾かれた時はJSONで下記の様なレスポンスが欲しい。
{ "summary": "バリデーションによりこのリクエストは拒否されました", "errors": { "infomation_title": [ "infomation title は必須です" ] } }問題
バリデーションで弾かれた際に
http://127.0.0.1:8000/
のhtmlがレスポンスとして帰ってきてしまう。原因
- バリデーションルールが記載されているファイルの継承元である
アプリ名ディレクトリ/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php
のpublic function validateResolved()
らへんが悪さをしている様である。- このためバリデーションルールが記載されているファイルは
FormRequest::failedValidation()
でバリデーションで弾かれた際にリダイレクトを行っているらしいのでこの部分の設定をバリデーションルールが記載されているファイルで上書き(オーバーライド)してあげる。解決方法
関数のオーバーライド
下記コマンドを実行してバリデーションルールが記載されたファイルを開く。
$ vi アプリ名ディレクトリ/app/Http/Requests/StoreContentPost.php下記の内容を
class StoreContentPost extends FormRequest
内に記載する。アプリ名ディレクトリ/app/Http/Requests/StoreContentPost.phppublic function failedValidation(Validator $validator) { $response['summary'] = 'バリデーションによりこのリクエストは拒否されました'; $response['errors'] = $validator->errors()->toArray(); throw new HttpResponseException( response()->json($response) ); }確認
付録
バリデーションルールが記載されたファイルの全体を下記に記載する。
アプリ名ディレクトリ/app/Http/Requests/StoreContentPost.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; //下記を追加する use Illuminate\Contracts\Validation\Validator; use Illuminate\Http\Exceptions\HttpResponseException; //上記までを追加する j class StoreContentPost extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'infomation_title' => 'required', 'infomation_content' => 'required', 'infomation_period_start_date' => 'required|max:8', 'infomation_period_end_date' => 'required|max:8', ]; } public function failedValidation(Validator $validator) { $response['summary'] = 'バリデーションによりこのリクエストは拒否されました'; $response['errors'] = $validator->errors()->toArray(); throw new HttpResponseException( response()->json($response) ); } }参考文献
- 投稿日:2020-06-04T00:12:43+09:00
PHPの覚書
PHP学習の覚書
連想配列について
連想配列は何層にもできる
test.php$array_promise_member = [ 'human'=>[ 'Emma' =>[ 'sex' =>'woman', 'plant' => 'three', 'age' => 11 ], 'Ray' =>[ 'sex' =>'men', 'plant' => 'three', 'age' => 12 ], 'Noman' =>[ 'sex' =>'woman', 'plant' => 'three', 'age' => 11 ], ], 'monster' =>[ 'Sonju' =>[ 'sex' =>'men', 'age' => null, ], 'anokata' =>[ 'sex' =>'men', 'age' => null, ] ] ]; //連想配列の呼び出し方 echo $array_promise_member['human']['Emma']['age']; //11Var_dump()
変数の内容を確認できる
echo '<pre>'; var_dump($array_promise_member); echo '<pre>'; //echo '<pre>';を上下につけると見やすくなる改行方法
. PHP_EOL ,EOT,eho '<br>'
ifを使うときのテクニック
elseを使うと可読性が下がるので
$here = 'farm'; if($here ==='farm'){ echo '脱獄'; }else{ echo'stay ease'; }こう書き換える
if($here === 'farm'){ echo '脱獄'; } if($here !=='farm'){ echo 'stay ease'; }データが入ってるかどうかの判定
isset / empty / is_null
データベースからデータを取ってきたときにデータが入っているかどうかを確かめる
$test='1'; //empty構文を使った場合 if(!empty($test)){ echo '変数は空ではありません'; } //変数は空ではありません三項演算子
if else -> 条件 ? true : false
$rank = '特上'; $command = $rank === '特上' ? 'catch! Don't kiII' : 'KiII them'; echo $command;foreachについて
多次元配列のforeach
$members = [ 'Emma' =>[ 'kind' =>'human', 'plant' => 'three', 'age' => 11 ], 'Ray' =>[ 'kind' =>'human', 'plant' => 'three', 'age' => 12 ], 'Noman' =>[ 'kind' =>'human', 'plant' => 'three', 'age' => 11 ] ]; // foreach ($members as $member1) { foreach ($member1 as $member) { echo $member; echo '<br>'; //キーも取りたい時は foreach ($members as $member1) { foreach ($member1 as $member => $value) { echo $member . 'は' . $value . 'です。' ; echo '<br>'; } }等号記号について
===か==か
PHPはどちらも使えるが、
== 型が違っても等しい
=== 型も等しいとなる。
例えば$string_num = '10';//文字列 if($string_num == 10){//数値 echo '等しい'; } //等しいとなってしまう。
$string_num = '10';//文字列 if($string_num === 10){//数値 echo '等しい'; } //error※switchの場合は、デフォルトだと==になってしまうので
$string_num = '10' switch($string_num){ case $string_num === 10: echo '等しい'; case....と書くことで厳密なチェックができるが
書き方が複雑になってしまうので
なるベくswitchを使わずifを使うよく使う関数
文字列関数
strlen
文字列の長さ(バイト数)を調べる関数
※日本語の平仮名は1文字3バイト
https://www.php.net/manual/ja/function.strlen.phpmd_strlen
文字列の長さを調べる関数
※日本語も1文字として計算される
https://www.php.net/manual/ja/function.mb-strlen.phpstr_replace
文字列を置換する関数
https://www.php.net/manual/ja/function.str-replace.phpexplode
文字列を分割する関数
https://www.php.net/manual/ja/function.explodeimplode
文字列を結合する関数
https://www.php.net/manual/ja/function.implodepreg_match
正規表現に使用
→文字か数字か、郵便番号やメールアドレスが正しく入力されているかを確認する
https://www.php.net/manual/ja/function.preg-matchmb_substr
指定文字列から文字列を取得する
https://www.php.net/manual/ja/function.mb-substr参考:図解でわかるPHP関数一覧
http://html2php.starrypages.net/php/array-funcs外部ファイルの読み込み
requireとinclude
require・・・ファイルの読み込みに失敗するとエラーになる
include・・・ファイルの読み込みに失敗すると警告が出る<?php require 'ファイル名'; //外部ファイルの関数や変数が使える現在のファイルの絶対パスを表示させるマジック定数
main.phpecho __DIR__; // ~/~/~/main.php外部ファイルを絶対パスで読み込みたい時は
main.phprequire __DIR__ . '~/~/~/○○.php';として読み込むことが出来る