- 投稿日:2020-06-28T23:33:24+09:00
PHP基礎<変数と定数>
前書き
PHPを基礎から学習しています。
積み重ねのアウトプットとして更新していきます。変数とは
データを入れる「箱」のようなものです。 この「箱」の中には色々なものを出し入れすることができます。
「変数」は変わる数で、「定数」は変わらない数を示します。index.php<?php $name = '山田太郎'; $age = 20; echo '私の名前は'; echo $name; echo 'です。'; echo '私の年齢は'; echo $age; echo '歳です。';ここでの $name や $age が変数です。
変数名
変数名には以下のようなルールがあります。
- 先頭には $ が必要
- 英数字およびアンダースコア(_)を利用可能
- 数字は先頭の文字には使えない
- 定数は一般的には全て大文字
また、変数名は内容が分かりやすい名前をつけます。
index.php?php // 良い例 $name = '山田太郎'; $age = 40; // 良くない例 $var = '山田太郎'; // どんな変数なのかが分かりづらい $nenrei = 40; // 基本的に英単語を元につけたほうが良いさらに、長い変数名の場合は、「スネークケース」「キャメルケース」などを利用します。
index.php<?php // スネークケース // スネークケースでは、アンダースコアで単語間をつなげ、基本的には全て小文字となる $user_name = '山田太郎'; // キャメルケース // キャメルケースでは、2番目の単語の初めを大文字とする $userName = '山田太郎';変数の上書き
変数は、値を変更することができます。
index.php<?php $name = '山田太郎'; echo $name; $name = '鈴木花子'; // こちらが上書きされる echo $name;あとがき
初回は変数と定数についてまとめました。
間違い等あればご指摘ご指導お願いいたします!
- 投稿日:2020-06-28T22:24:26+09:00
Docker で PHP 8.0.0 Alpha 1 環境構築
概要
2020年6月25日に PHP 8.0.0 Alpha 1 がリリースされました。
Docker イメージ も公開されていたので最低限の環境を構築してみます。構成
$ tree . ├── docker-compose.yml ├── nginx │ └── default.conf └── public └── index.phpファイル
docker-compose.ymlversion: "3.8" services: nginx: image: nginx ports: - "80:80" volumes: - ./public:/var/www/html - ./nginx/default.conf:/etc/nginx/conf.d/default.conf php: image: php:8.0.0alpha1-fpm volumes: - ./public:/var/www/htmlpublic/index.php<?php phpinfo();nginx/default.confserver { index index.php index.html; root /var/www/html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { try_files $uri = 404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } }コードは SnowCait/php8-docker-compose-example にも置いてあります。
さくっと試したい方はgit clone
して使ってください。起動
docker-compose up -d
ブラウザでアクセスする
http://localhost/ にアクセスすると
phpinfo()
の内容が表示されます。
ポートを変えたい場合は"80:80"
の部分を"8080:80"
にすると http://localhost:8080/ でアクセスできます。
コマンドラインから実行する
docker-compose exec php php -v PHP 8.0.0alpha1 (cli) (built: Jun 26 2020 19:56:42) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.0-dev, Copyright (c) Zend Technologiesコマンドラインだけあれば良い方は nginx の設定は不要です。
停止
docker-compose down拡張
実際にアプリを動かそうとすると今回紹介したコードだけでは足りないと思います。
データーベースや KVS が必要だったり Composer が必要だったり public/ 以外のディレクトリが必要だったり。
Docker Compose であればコンテナや設定を追加するだけなので色々と拡張しやすいです。Composer を使いたい方は 環境を汚さずに Composer を使いたい - Qiita に書いていますのでもし興味があれば。
docker-compose.yml に 4 ~ 5 行追加するだけで使えるようになります。
- 投稿日:2020-06-28T22:03:30+09:00
ラズパイでcronがうまく設定できないときのTips
概要
ラズパイを買って、なにかしら定期処理させようとしたときにちょっとハマった。
そもそもcron触ったことない場合のcronの注意と
ラズパイだとコマンドちょっと違うよっていう話。僕と同レベルの初学者向けに書いてます。
ラズパイ編
cronの起動、再起動
/etc/init.d/cron start /etc/init.d/cron restart↓は検索するとよく出てくるけど
service crond restartdebian(ラズパイのOS)だとそんなものないよってなるので注意。
ログ
設定
/etc/rsyslog.conf(抜粋)############### #### RULES #### ############### # # First some standard log files. Log by facility. # auth,authpriv.* /var/log/auth.log *.*;auth,authpriv.none -/var/log/syslog #cron.* /var/log/cron.log daemon.* -/var/log/daemon.log kern.* -/var/log/kern.log lpr.* -/var/log/lpr.log mail.* -/var/log/mail.log user.* -/var/log/user.log以下の記述がデフォルトでは、
コメントアウトされてるので、解除。#cron.* /var/log/cron.log ↓ cron.* /var/log/cron.log確認方法
当たり前だけど、これでいけます。
cat /var/log/cron.logcron
設定方法
以下のコマンドでエディターが開きます。
色々書いてるけど、一番下に実行したいコマンド書いたら実行されます。crontab -e※PHPを実行したいけどエラーが出たとき
フルパスで書くのが重要
例えば、
/var/www/html/cron.php
を実行したいとき。このファイル内でなにかしらインクルードしてる場合は、
cron.phpinclude("./hogehoge.php")↑こう書くとエラーが出る。
cron.log(CRON) error (grandchild #***** failed with exit status 255)なのでフルパスで書いてあげる。
cron.phpinclude("/var/www/html/hogehoge.php")また、実行したいファイルでなく、PHPの場所も書いたほうがいい。
僕は書いたら動きました。* * * * * /usr/bin/php /var/www/html/cron.php終わりに
間違ってたら指摘ください
- 投稿日:2020-06-28T21:13:39+09:00
最後のおさらい!PHP8で削除される&動かなくなる機能 ~レガシーシステムでありそうなものピックアップ~
まえがき
PHP8が2020/12にリリースされる予定で、先日2020/06/25にアルファ版が公開されました。
https://twitter.com/official_php/status/1276198231714783232?s=20
PHP7.2~7.4では既に非推奨になっている機能が廃止され、エラーで止まったりするということで、今一度PHP8で動かなくなる機能を調べ、そのなかでもレガシーシステムで使われてそう・古いFWで使われてそうで万人に身近そうなものを独断でピックアップして記載したいと思います。
PHP8で削除される&動かなくなる機能
PHP7.2から非推奨→廃止
$errcontext argument of error handler
エラーハンドラのコールバック関数のシグニチャに指定できる
$errcontext
が廃止されます。
この変数にはエラーが起きたときのローカル変数の値をすべて保持しています。が、現在はデバッガで確認するような内容で実運用では使用頻度があまりなさそうなものにこれからもこの変数を実現するために保守し続けるのは割に合わない...とのことで廃止されます。
この機能に関しては、deprecationのワーニングスローがされていません。 もしこの引数を利用していればエラー処理がうまく作動しなくなるかもしれませんので今一度注意したほうがよいと思います。
Proposed action: Throw deprecation notice if error handler has five or more arguments. Otherwise, do not pass the $errcontext. This prevents circumvention with func_get_args().
Update: Due to technical issues, this does not throw a deprecation warning. It is a documentation-only deprecation.
PHP7.3から非推奨→廃止
String search functions with integer needle
文字列内検索に関する関数の振る舞いが一部変わります。
検索文字列が数値だった場合、falseを返していたのを、与えられた数値を文字列に変換してくれるようになります。...今までfalseになって分岐に入っていなかった処理が実行される..いいことであるはずですが、もしかするとなにか"トラブル"にもなるかもしれません。
~PHP7.4:
$str = "There are 10 apples"; var_dump(strpos($str, "10")); // int(10) var_dump(strpos($str, 10)); // bool(false)PHP8:
$str = "There are 10 apples"; var_dump(strpos($str, "10")); // int(10) var_dump(strpos($str, 10)); // int(10)こちらからそのままソースを引用
PHP7.4から非推奨→廃止
The 'real' type
type
float
のエイリアスとしてdouble
とreal
が存在するのですが、ほぼreal
が利用されていないため廃止されます。(既にsettype($type)
関数には'real'と入れても効果がないようになっているとのこと)お金の計算などを行うドメインを扱うシステムでは利用されているかもしれません。
公式の対処法では、単に'real'となっている箇所を'float'に置き換えれば同じふるまいになるそうなのでそこまで難しくはなさそうです。
array_key_exists() with objects
関数名の通り、配列を渡すための関数ですが、オブジェクトを渡しても問題ないように見えています。オブジェクトのプロパティが存在するかは別関数があるのでそちらに差し替えるようにする必要があります。
過去との互換性を保つため、key が仮に array で指定したオブジェクトのプロパティであっても array_key_exists() は TRUE を返します。 しかし、この挙動に頼ってはいけません。 array にはオブジェクトではなく配列を渡すようにしましょう。
オブジェクトのプロパティが存在するかどうかを調べるには、 property_exists() を使いましょう。
https://www.php.net/manual/ja/function.array-key-exists.phpmb_strrpos() with encoding as 3rd argument
公式ドキュメントにそのままのっていました。現在は
encoding
は第4引数になっているのですが、移行時間を設けていままで第3引数でも通るようになっていました。が、ついにPHP8で削除されます。注意: encoding パラメータは、 PHP 5.2.0 以降は三番目のパラメータではなく四番目のパラメータに変わりました。 過去との互換性を保つために encoding を三番目の引数で指定することもできますが、これは推奨されません。 将来は削除される予定です。
https://www.php.net/manual/ja/function.mb-strrpos.phpimplode() parameter order mix
公式ドキュメントに注意書きの非推奨文言がありました。ついに歴史に終止符を打つようです。
注意:
implode()は、歴史的な理由により、引数をどちら の順番でも受けつけることが可能です。しかし、 explode() との統一性の観点からは、 ドキュメントに記述された引数の順番を使用しないことは推奨されません。
https://www.php.net/manual/ja/function.implode.php以下のシグニチャに書き換える必要があります。
implode ( string $glue , array $pieces ) : string implode ( array $pieces ) : string所感
記載したもの以外にも非推奨コマンドはあるのと、業務ドメインによってはささるようなメソッドもあったのでいまいちど確認したほうがいいなと感じました。PHPの、コミュニティとともに変化していくのが楽しいと思いつつ、実システムの考慮はやや大変だなと思いつつ。。これからのPHP8を楽しみに待ちたいです。
https://wiki.php.net/rfc/deprecations_php_7_2
https://wiki.php.net/rfc/deprecations_php_7_3
https://wiki.php.net/rfc/deprecations_php_7_4
- 投稿日:2020-06-28T20:53:17+09:00
PHP: $HTTP_GET_VARS [非推奨]の書き換え
$HTTP_GET_VARSは非推奨になっています。
しかし、古いプログラムでは使われています。
例えば、test_args.php<?php // ----------------------------------------------------------------- echo "<html>"; echo '<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8" />'; echo "<body>"; echo "test_args.php<p />"; echo "<h2>テスト</h2>"; $in_vars = ""; while (list($key, $val) = each($HTTP_GET_VARS)) { $in_vars .= $key."=".$val. "<p />"; } echo $in_vars; echo "Jun/28/2020<p />"; echo "</body>"; echo "</html>"; // ----------------------------------------------------------------- ?>このプログラムを走らせると、次のようなログが出ます。
/var/log/apache2/error.log[Sun Jun 28 20:40:57.834991 2020] [php7:warn] [pid 8012] [client 127.0.0.1:41472] PHP Warning: Variable passed to each() is not an array or object in /home/uchida/html/data_base_language/test_dir/php/test_args.php on line 10, referer: http://localhost/html/data_base/test_dir/php/次のように修正すれば、正常に動きます。
<?php // ----------------------------------------------------------------- echo "<html>"; echo '<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8" />'; echo "<body>"; echo "test_args.php<p />"; echo "<h2>テスト</h2>"; $in_vars = ""; // while (list($key, $val) = each($HTTP_GET_VARS)) { while (list($key, $val) = each($_GET)) { $in_vars .= $key."=".$val. "<p />"; } echo $in_vars; echo "Jun/28/2020<p />"; echo "</body>"; echo "</html>"; // ----------------------------------------------------------------- ?>次のように引数を与えて実行した結果です。
test_args.php?aa=12&bb=34&cc=56
- 投稿日:2020-06-28T19:23:21+09:00
【Laravel】idごとの情報を表示する
ユーザーのマイページのような、特定のidの情報を表示する方法のメモ。
Route
web.phpRoute::group(['prefix'=>'~~~~~~','middleware'=>'auth'], function () { Route::get('index', '*******Controller@index')->name('~~~~~~.index'); Route::get('create', '*******Controller@create')->name('~~~~~~.create'); Route::post('store', '*******Controller@store')->name('~~~~~~.store'); Route::get('show/{id}', '*******Controller@show')->name('~~~~~~.show');//この部分 });Controller
****Controller.phppublic function show($id) { $***** = モデル名::find($id); //idを見つける //条件分岐の処理 if($*****->gender === 0){ $gender = '男性'; }elseif ($*****->gender === 1) { $gender = '女性'; } return view('*****.show',compact('*****','gender')); }条件分岐する必要がある場合は、Controller内で処理させてその変数をcompactの引数に入れる。
View
show.blade.php<div> {{$contact->name}} {{$gender}} //Controllerでcompactの引数にセットしているので変数がそのまま使える。 </div>表示できてたらおk。
おまけ
indexページからshowに飛ばす時のボタン。
index.blade.php<button onclick="location.href='{{route('*****.show',['id' => $*****->id])}}'">詳細</button>
- 投稿日:2020-06-28T18:20:48+09:00
【Laravel基礎】DBにデータを保存・保存内容を表示
LaravelでDBにデータを保存する方法と、保存した内容を表示する方法のメモ。
前提
resourceでControllerを作っています。
注意
コード部分の「****」「&&&&」みたいなのは代替文字です。
DBに保存
Controller,Route,Viewのそれぞれに書いていきます。
Route
web.phpRoute::group(['prefix'=>'~~~~~~','middleware'=>'auth'], function () { Route::get('index', '*******Controller@index')->name('~~~~~~.index'); Route::get('create', '*******Controller@create')->name('~~~~~~.create'); Route::post('store', '*******Controller@store')->name('~~~~~~.store'); //この部分 Route::get('show/{id}', '*******Controller@show')->name('~~~~~~.show'); });postでviewで入力されたパラメータを受け取る。
View
create.blade.php<form method="POST" action="{{route('~~~~~.store')}}"> @csrf <input class="input-group" type="text" name="name" placeholder="Name"> <input class="input-group" type="text" name="email" placeholder="Mail"> 男性:<input type="radio" name="gender" value="0"> 女性:<input type="radio" name="gender" value="1"> <input class="input-group" type="text" name="title" placeholder="Title"> <textarea class="input-group" name="contact" placeholder="Contact"></textarea> <input class="btn btn-primary" type="submit" value="Submit"> </form>methodをpostに設定してパラメータをstoreに送信する。
Controller
~~~~~Controller.phpuse App\Models\モデル名; public function store(Request $request) { //Modelをインスタンス化 $***** = new ContactForm; $*****->name = $request->input('name'); $*****->email = $request->input('email'); $*****->title = $request->input('title'); $*****->gender = $request->input('gender'); $*****->contact = $request->input('contact'); $*****->save(); //保存 return redirect('*****/index'); //indexページにリダイレクト }Modelファイルを利用して送られてきたパラメータをDBに保存します。
保存できてるかどうか一度DBで確認してください。
できてたらおk。Viewに表示
Controller,Viewのそれぞれに書いていきます。
Controller
*****Controller.phpuse Illuminate\Support\Facades\DB; //クエリビルダを利用 public function index() { // クエリビルダ $&&&&&& = DB::table('テーブル名') ->select('id','name','gender') //表示したい情報のカラム名を記述 ->get(); //取得 return view('******.index',compact('&&&&&')); }必要なデータだけ欲しい時はクエリビルダを使った方が処理速度が遅くならなくて済む。
View
index.blade.php@foreach($****** as $×××××) {{$×××××->id}} {{$×××××->name}} @if($×××××->gender===0) 男性 @else 女性 @endif @endforeachデータをforeachで回してあげる。
カラム名を指定して必要な情報を表示する。Viewで表示できてるのが確認できたらおk。
まとめ
基本的にはController,View,Routeをいじればできます。
本当に基礎の基礎なので多分もっと良い書き方とか他にあるかと思いますが、基礎学習中の人間のメモなのでご容赦を。
- 投稿日:2020-06-28T14:11:28+09:00
PHP-Parserで、phpコードを拡張・作成するメタプログラミング入門
背景
仕事で、レガシーなソースコードに名前空間やPHPDocを機械的に付与するために調べました。
基本的に、nikic/PHP-Parserの公式docの和訳まとめです。
英語に抵抗がなく、急いでない方は公式を読まれると良いと思います。https://github.com/nikic/PHP-Parser/tree/master/doc
要約
- PHP-Parserで既存のファイルを最小限の変更で拡張できる
- 名前空間を付与したり、特定の関数の書き換え、PHPDocの追加なども可能
- かなり自由なPHPのメタプログラミングができる
nikic/PHP-Parser
https://github.com/nikic/PHP-Parser
phpで書かれたphpパーサーです。phpだけで動くので、使いやすいです。
PHP 5.2からPHP 7.4のコードを解析でき、ヒューマンリーダブルなphpファイルに出力できます。
安定版のv4.0以上の実行環境は、php7.1以上です。
静的解析ライブラリのphpstan/phpstanの、ベースのPHPの解釈にも採用されていたり、安心感があります。
インストール
composer でインストールできます。
composer require nikic/php-parser基本編
構文木に変換、ヒューマンリーダブルなコードに戻す流れを追います。
phpコードの構文木への変換
parserにコード文字列を渡すと、変換してくれます。
パーサーは、細かくカスタマイズすることができ、詳しくは後述します。<?php use PhpParser\Error; use PhpParser\NodeDumper; use PhpParser\ParserFactory; $code = <<<'CODE' <?php function test($foo) { var_dump($foo); } CODE; $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $dumper = new NodeDumper; echo $dumper->dump($ast) . "\n";パースした結果、下記のようなオブジェクトの配列構造になっています。
この配列の中身をチェックし、任意のものに書き換えたり、新しいオブジェクトを追加することで、新しいPHPを作成します。array( 0: Stmt_Function( byRef: false name: Identifier( name: test ) params: array( 0: Param( type: null byRef: false variadic: false var: Expr_Variable( name: foo ) default: null ) ) returnType: null stmts: array( 0: Stmt_Expression( expr: Expr_FuncCall( name: Name( parts: array( 0: var_dump ) ) args: array( 0: Arg( value: Expr_Variable( name: foo ) byRef: false unpack: false ) ) ) ) ) ) )構文木の変更
PHP-Parser には
NodeVisitor
という interface があり、実装したclassをNodeTraverser
に追加すると、所定のイベントで呼び出されて構文木を書き換えたり値を書き換えられます。interface NodeVisitor { public function beforeTraverse(array $nodes); public function enterNode(Node $node); public function leaveNode(Node $node); public function afterTraverse(array $nodes); }
beforeTraverse()
およびafterTraverse()
は、トラバーサルの前と後に呼ばれ、全体のASTを渡されます。これらを使用して、必要な状態のセットアップまたはクリーンアップを実行できます。
enterNode()
メソッドは、ノードが最初に検出されたときに、その子が処理される前に呼び出されます。一方で、leaveNode()
メソッドは、すべての子が訪問された後に呼び出されます。実際には、これを実装した
NodeVisitorAbstract
を継承して、使うことが多いと思います。
今回は、var_dump
をuse PhpParser\Node; use PhpParser\NodeVisitorAbstract; class VarDumpConvertPrintVisitor extends NodeVisitorAbstract { public function leaveNode(Node $node) { if ($node instanceof Node\Expr\FuncCall && $node->name->parts->getLast() == 'var_dump') { $node->name->parts = ['print']; } } } $traverser = new NodeTraverser; $traverser->addVisitor(new VarDumpConvertPrintVisitor); $stmts = $traverser->traverse($stmts);わかりづらいですが、下記のように
Expr_FuncCall
の中身のname->parts
が0: Stmt_Expression( expr: Expr_FuncCall( name: Name( parts: array( 0: print ) ) args: array( 0: Arg( value: Expr_Variable( name: foo ) byRef: false unpack: false ) ) )phpコードへの出力
一番シンプルな出力方法は、下記です。
$prettyPrinter = new PhpParser\PrettyPrinter\Standard(); $newCode = $prettyPrinter->prettyPrintFile($stmts);<?php function test($foo) { print($foo); }しかしこの方法だと、「既存のコードのリファクタリングでは、改行などに差分がでる」という問題があります。
構文木に解体する際に、改行コードが読み捨てられてしまうためです。改行を維持した出力
PHP-Parser v4.0以降、コードのフォーマット(変更されていないASTノード)を保持し、変更または新しく挿入されたコードのみをフォーマットするモードが利用できます。
ちょっと記述が増えますが、必要最低限の変更に抑え上記の問題を回避できます。
※まだ実験段階の機能なので、変更があるかもしれません。
https://github.com/nikic/PHP-Parser/blob/master/doc/component/Pretty_printing.markdown
use PhpParser\{Lexer, NodeTraverser, NodeVisitor, Parser, PrettyPrinter}; $lexer = new Lexer\Emulative([ 'usedAttributes' => [ 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', ], ]); $parser = new Parser\Php7($lexer); $traverser = new NodeTraverser(); $traverser->addVisitor(new NodeVisitor\CloningVisitor()); $printer = new PrettyPrinter\Standard(); $oldStmts = $parser->parse($code); $oldTokens = $lexer->getTokens(); $newStmts = $traverser->traverse($oldStmts); // Nodeを組み替える $newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);コード全文
改行読み捨て
https://gist.github.com/komtaki/514f13fa07f4e8bdd9bd0d4fa61e0719
改行保持
https://gist.github.com/komtaki/7e2163a958440e99b630bbbe1512d368
応用編 細かいNodeの歩き方
Visitorにわたってくる
Node
オブジェクトはとても多様です。
NodeDumper
を使って、「自分が拡張したいオブジェクトは、どんな形で渡ってくるのか」確認することから始めるのがよいと思います。ここでは、一部特殊な機能について紹介します。
名前解決
NameResolver
を使用することで、基本的なclassの名前解決ができます。しかし、名前空間内の修飾されていない関数と定数名は解決できません。
例えば、
Foo
名前空間内のstoren()
は、名前空間\Foo\strlen()
またはグローバル\strlen()
のいずれかを参照できます。しかし、PHP-Parserにはこれを決定するために必要な情報がないためです。https://github.com/nikic/PHP-Parser/blob/master/doc/component/Name_resolution.markdown
$nameResolver = new PhpParser\NodeVisitor\NameResolver; $nodeTraverser = new PhpParser\NodeTraverser; $nodeTraverser->addVisitor($nameResolver); // Resolve names $stmts = $nodeTraverser->traverse($stmts);ノードの除去
トラバース中に、特定のタイプを返却することで、ノードを除去することが出来ます。
public function leaveNode(Node $node) { if ($node instanceof Node\Stmt\Return_) { // すべてのreturnを削除します。 return NodeTraverser::REMOVE_NODE; } }トラバースの中断、パフォーマンス改善
複数のVisitorを設定している場合、Node数の増加によって速度がおそくなるケースがあります。
特定のNodeを探している時などは、下記のようにすることでトラバースを終了できます。
1. 子ノードのトラバース回避
private $classes = []; public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Class_) { $this->classes[] = $node; return NodeTraverser::DONT_TRAVERSE_CHILDREN; } }2. トラバースの中止
private $class = null; public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Class_) { $this->class = $node; return NodeTraverser::STOP_TRAVERSAL; } }まとめ
PHP-Parserで、名前空間を付与したり、変数を書き変えたり、PHPDocを追加したり動的にPHPコードを拡張することが出来ます。
ただ限界はあり、コードの整形などは出来ないので、他のツールも組み合わせて柔軟に対応していくのがよいと思います。
参考
下記、参考にさせて頂きました。ありがとうございます。
- 投稿日:2020-06-28T13:35:25+09:00
【heroku,FuelPHP】デプロイ時のyahooメール設定方法
思うようにメール設定がうまくいかず時間がかかったので、同じ環境の方がさくっと対処できるように手順を残しておきます。
対象の方
herokuにFuelPHPを使ったwebサービスをデプロイ済みでyahooメール設定がうまくいかない方。
※MacOS環境での方法を記載しています。手順
5ステップで解説していきます。
1.config.phpにemailを追加
2.email.phpを..fuel/app/configに複製
3.email.phpを編集
4.yahooメールの設定変更
5.email.phpのnewlineを編集(それでもエラーが出る場合)1.config.phpにemailを追加
fuel/app/config/config.phpのpackages=>array()にemailを追加
※Emailパッケージ読み込んでEmailクラスを利用できるようにします。fuel/app/config/config.php'packages' => array( // 'email', //新規で追加 ),2.email.phpを..fuel/app/configに複製
/fuel/packages/email/config/email.phpをコピーし、/fuel/app/configディレクトリ内にemail.phpを複製します。
※mail設定の編集はこちらで行なっていきます。3.email.phpを編集
複製したemail.phpを下記のように編集していきましょう。
fuel/app/config/email.php... /** * Mail driver (mail, smtp, sendmail, noop) */ 'driver' => 'smtp', //smtpに変更 ... 'smtp' => array( 'host' => 'smtp.mail.yahoo.co.jp', //変更 'port' => 587, //変更 'username' => 'yahooメールのログインID', //変更 'password' => 'yahooメールのパスワード', //変更 'timeout' => 5, 'starttls' => false, 'options' => array( ), ), ...設定が気になる方はyahoo公式サイト(メールソフトやモバイル端末での設定)にも解説してあるので参考にしてみてください。
4.yahooメールの設定変更
ここまで設定して下記の認証エラーが出る場合はyahooメール側の設定を確認してみましょう。
先ほどと同じyahoo公式サイト(Yahoo!メールでの設定)に解説してあるので確認してみましょう。
※「Yahoo! JAPAN公式サービス以外からのアクセスも有効にする」にチェックを入れ、「IMAP」「POP」「SMTP」をそれぞれ「有効にする」を選択できていればOKです。私はここに行き着くのに結構時間が掛かってしましました笑
5.email.phpのnewlineを編集(それでもエラーが出る場合)
4まで設定してメール送信できた方は見なくて良いですが、私の場合は下記エラーが出ました。
色々と試してみましたが、fuel/app/config/email.phpのnewlineを変更することでメール送信できるようになりました。
fuel/app/config/email.php/** * Newline */ 'newline' => "\r\n", //"\n"から変更gmailの場合ですが、公式アカウントにも記載ありました。
それでは最後まで読んで頂きありがとうございました。
参考になった方はLGTMをポチッと押していただけると励みになります。
- 投稿日:2020-06-28T13:35:05+09:00
カラーミーAPIを利用して商品一覧のjsonを取得する
「カラーミーAPI」を利用して、
カラーミーで作ったショップの全商品一覧および各商品の詳細を、
別サーバー(ここではheteml)にjsonとして保存した時のソースコードです。
以下のようなコードをhetemlのweb以下に「products.php」として置き、
cronで10分ごとに動かし、
同ディレクトリに「cache.json」として保存します。#!/usr/local/php/7.3/bin/php <?php $request_options = array( 'http' => array( 'method' => 'GET', 'header'=> "Authorization: Bearer オーソリゼーションコードだよ〜\r\n" ) ); //アクセストークンなどAPI取得に必要な情報だよ $context = stream_context_create($request_options); //商品一覧を取得する //先に商品が全部でいくつあるか取得する $url = 'https://api.shop-pro.jp/v1/products.json'; //あとでoffsetで51件目以降の処理を作る $response_body = file_get_contents($url, false, $context);//API取得に必要な情報を一緒に送る $response_body = json_decode($response_body, true); $all_the_products_num = $response_body["meta"]["total"]; //次に、1-50件、51-100件、と回していく for($i = 0; $i < $all_the_products_num; $i++){ $key_num = $i%50; if($i%50 == 1){ $url = 'https://api.shop-pro.jp/v1/products.json?limit=50&offset='.($i-1); $response_body = file_get_contents($url, false, $context); $response_body = json_decode($response_body, true); } $product_ids[$i] = $response_body["products"][$i%50]["id"]; } //productIDの一覧ができたので、それをもとに商品詳細のapiを叩く $tmp .= "{\"product\":["; for($i = 0; $i < count($product_ids); $i++){ $url = 'https://api.shop-pro.jp/v1/products/'.$product_ids[$i].'.json'; $response_body = file_get_contents($url, false, $context);//API取得に必要な情報を一緒に送る $result = json_decode($response_body); $tmp .= json_encode($result->{"product"}); if($i != count($product_ids)-1){ $tmp .= ","; } } $tmp .= "]}"; $file = '書き出し先のhetemlの絶対パス/cache.json'; // ファイルをオープンして既存のコンテンツを取得します $current = file_get_contents($file); // 結果をファイルに書き出します file_put_contents($file, $tmp); ?>
- 投稿日:2020-06-28T09:44:40+09:00
(自分用)Flask_AWS_1(AWS仮想環境にPHP,MySQL,phpMyAdmin,Pythonのインストール)
項目
- PHPのインストール
- MySQLのインストール
- PHPMyAdminのインストール
- Pythonのインストール
1.PHPのインストール
ターミナル# まずLinux仮想マシンに接続、ここを覚えてないなら1つ前のやつを見て $ ssh -i ~/.ssh/FirstKey.pem ec2-user@(パブリックIP) # 管理者権限に変更 $$ sudo su # PHPをインストール $$ yum install -y php # PHPの設定を変える前にバックアップを取っておく $$ cp /etc/php.ini /etc/php.bak # viでPHPの設定変更 $$ vi /etc/php.ini
:set number
で行番号を表示:520
で520行目に移動i
にて記述モードにし、error_reporting = E_ALL & ~E_DEPRECATED
という行を、error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE
に変更するesc
でコマンドモードに戻る:537
で537行目へi
からのdisplay_errors = Off
をOn
に変更、esc
:wq
で保存終了ターミナル# Apacheを再起動し適用 $$ service httpd restart # [OK]*2出たら多分大丈夫2.MySQLのインストール
- 前回に引き続き管理者権限でログインしてる前提
ターミナル# MySQLのインストール $$ yum install -y mysql-server # MySQLをPHPがいい感じに動かせるものを入れる $$ yum install -y php-mysqlnd # Pythonでいい感じにね $$ yum install mysql-connector-python # MySQLを起動 $$ service mysqld start # MySQLの設定をあれこれ $$ mysql_secure_installation # 一発目のrootパスワード聞いてくるところはそのままEnter # rootパスワード新しくするか聞かれるので、yes # 好きなパスワードを入力 # anonymousユーザ消すか聞いてくるのでyes # あとは全部yでも良さげ # MySQLでの文字コードを設定 $$ vi /etc/my.cnf # `:set number`で行数を表示し空白の10行目に $$ character-set-server = utf8 # と入力する、繰り返すが`i`で入力モードにし、入力後`esc`でコマンドモード # `:wq`で保存終了 # MySQLを再起動 $$ service mysqld restart #[OK]と出れば`OK`3.PHPMyAdminのインストール
- 前回に続き仮想環境の管理者権限内を前提
ターミナル# phpMyAdminのインストール先を作成 $$ yum-config-manager --enable epel # phpMyAdminをインストール $$ yum install -y phpmyadmin # phpMyAdminの設定を開く $$ vi /etc/httpd/conf.d/phpMyAdmin.conf # ここで127.0.0.1って書いてあるところをグローバルIPに変更すると、自分のローカルなマシンだけが接続変更出来る様になる # やらなくていいと思う # もし変えたい場合は、`:%s/127.0.0.1/自分のグローバルIP/g`で変更 # Apacheの再起動 $$ service httpd restart
http://前に設定したAWSのパブリックIP/phpmyadmin/
に接続すると、phpMyAdmin
のページが出てくる- ユーザ名は
root
、パスワードはMySQLインストールで設定したやつを入れる4.Pythonのインストール
- 前に続き仮想環境内(管理者権限ではない)
ターミナル# 一回Linux環境アップデート $$ sudo yum -y update # Pythonインストール $$ sudo yum install python36-devel python36-libs python36-setuptools # もうMySQLをPythonでいい感じにするやつはインストール済み # Apacheを再起動 $$ service httpd restart5.終わりに
- 環境構築がいっっっちばんつまらない最悪
- 投稿日:2020-06-28T08:13:54+09:00
環境を汚さずに Composer を使いたい
要約
- Docker には Composer イメージがあって Docker Compose から使うと便利
- 使い方とサンプルコード
Composer どうやって実行してますか?
PHP 開発に Composer は必須です。
ではどうやって実行しているでしょうか。
- ローカルで composer コマンドを使う
- ローカルにインストールする
- composer.phar をコミットする
- Docker コンテナで composer コマンドを使う
- PHP コンテナにインストールする
- 自前インストール
- Composer イメージを使ってインストール
- コミットされた composer.phar を PHP コンテナ内で使う
- Composer コンテナを使う
- Docker で使う
- Docker Compose で使う ← 今回はこれ
- GitHub Actions で composer コマンドを使う
様々な実行方法があると思います。
ローカルで composer コマンドを使うには PHP もインストールしないといけません。
ローカルに環境を構築するのは大変です。チームメンバー全員の面倒なんて見たくありません。
そこで Docker コンテナの出番です。
よく見かけるのは PHP コンテナに Composer をインストールする方法です。
ですが Composer を使うのはライブラリのインストール(復元)時だけです。
稼働中の PHP コンテナに果たして必要なものでしょうか?
そこで今回は独立した Composer コンテナでコマンドを実行する方法を紹介します。Composer コンテナ
docker
コマンドで直接使用する方法は ComposerをDockerコンテナで動かす - Qiita を、
PHP コンテナにインストールする方法は Docker に Composer をインストールするベストプラクティス(と解説) - Qiita を参考にしてください。
この記事では独立した Composer コンテナをdocker-compose
コマンドで使用する方法を紹介します。
PHP コンテナは不要なので Composer だけ使いたい方にもおすすめです。Docker Compose で定義する
docker-compose.ymlversion: "3.8" services: composer: image: composer command: "composer install" volumes: - ./:/app
./:/app
は:
の前がローカルのディレクトリパス、後が composer コンテナ内のパスです。
ローカルは composer.json, composer.lock を作成したい場所、 composer コンテナは/app
を指定します。ライブラリのインストール (composer require)
docker-compose run composer composer require --dev phpunit/phpunit
composer
が 2 つあるのは 1 つ目がコンテナ名(変更可)で 2 つ目がコマンド名です。ライブラリの復元 (composer install)
docker-compose.yml に
command: "composer install"
を定義しておけばdocker-compose up -d
する際に自動的にcomposer install
が実行されます。定義しなくても良いです。もちろんコマンドでも実行できます。
docker-compose run composer composer install
ライブラリのバージョンアップ
composer.lock
の更新。docker-compose run composer composer updateその他のコマンド
他のコマンドも同様に
docker-compose run composer
から実行できます。
どんなコマンドがあるかは PHP開発でComposerを使わないなんてありえない!基礎編 - Qiita を参照してみてください。サンプルコード
SnowCait/docker-compose-composer-example にサンプルコードを置いています。
Docker がインストールされていれば git clone して気軽に試せると思うので良かったら試してみてください。参考にした記事
Docker環境でcomposerのimageつかってupdateしてみる - Qiita
補足:リンク先ではcommand: "composer update"
となっていますが起動する度にバージョンが変わるのはよろしくないのでcommand: "composer install"
にしておいた方がいいのではないかと思います。余談
(Docker) Compose と Composer が似ていて間違えそう。
- 投稿日:2020-06-28T01:06:26+09:00
PHPUnit 配列の順番は考慮・気にしないAssert
概要
配列(連想配列も含む)の順番は気にせず、要素は同じであることを検証するためのAssert
※ そもそも、順番も保証された上でのテストであったほうが良いというのも分かるのですが、今回はスコープ外とします
※ こんなAssertがあればいいなと作成したものなので「既にPHPUnitで用意されてるよ」などあればコメントお願いします?
本題
利用したいテストでこのTraitを使う想定でこんな感じで実装しました
<?php trait ArraySimilarTrait { /** * PHPUnit assert arrays ignore orders. * * @param array $expected * @param array $actual */ protected function assertArraySimilar(array $expected, array $actual) { $this->assertSame([], array_diff_key($actual, $expected)); array_multisort($expected); array_multisort($actual); foreach ($expected as $key => $value) { if (is_array($value)) { $this->assertArraySimilar($value, $actual[$key]); } else { $this->assertContains($value, $actual); } } } }gistでも公開しています
ポイントとしては以下の3つです。
- 比較する2つの配列でキーが同じであること
- 配列・連想配列どちらでも対応できること
- どの次元においても順番は異なっても問題ないこと
1. 比較する2つの配列でキーが同じであること
$this->assertSame([], array_diff_key($actual, $expected));言葉の通りですが、比較する2つの配列で同じキーで差分がないことを検証しています。
2. 配列・連想配列どちらでも対応できること
array_multisort($expected); array_multisort($actual);「順番を考慮しないはずなのに、なぜ並び替えをしているのか。」という疑問が生まれると思います。
今回、配列のどの次元においても順番は気にない仕様にしております。
そのために、要素が配列であれば、続くコードで以下のように再帰的に
assertArraySimilar
メソッドを呼び出すようにしています。foreach ($expected as $key => $value) { if (is_array($value)) { $this->assertArraySimilar($value, $actual[$key]); // ? 配列(Array)の場合、添字での参照になります } else { $this->assertContains($value, $actual); } }コメントにも書いたように
$actual[$key]
のように書くと、配列(Array)の場合、添字での参照になります。そのため、配列(Array)の場合でも、同じ要素へ参照するために並び替えをしています。
※ ここで
配列(Array)
という書き方をしたのは、連想配列(Hash)
と区別するためです。PHPには、配列(Array), 連想配列(Hash)は型として区別されていませんが、
array_multisort
メソッドを利用することで、
配列(Array)であれば、キーを振り直すsort
メソッドにあたる挙動を
連想配列(Hash)であれば、キーを振り直さないasort
メソッドにあたる挙動をしてくれます。3. どの次元においても順番は異なっても問題ないこと
2. 配列・連想配列どちらでも対応できること
でほぼ書いてしまいましたが、
再帰的にassertArraySimilar
メソッドを呼び出すことで、
どの次元においても順番は異なっても気にない = Pass します。
テスト用Traitのテスト
今回、作成したAssertが期待した挙動になっているのか確認のためテストも書きました。
どういうパターンだと成功・失敗するのかこちらのほうがわかりやすいかと思います。
<?php class ArraySimilarTraitTest extends \PHPUnit\Framework\TestCase { use ArraySimilarTrait; /** * @test * @dataProvider data_assertArraySimilar_expected_passed * @param array $expected * @param array $actual */ public function assertArraySimilar_expected_passed(array $expected, array $actual) { $this->assertArraySimilar($expected, $actual); } public function data_assertArraySimilar_expected_passed() { return [ '1D Array' => [ [1, 2, 3], [3, 2, 1], ], '1D Hash' => [ ['a' => 1, 'b' => 2, 'c' => 3], ['c' => 3, 'b' => 2, 'a' => 1], ], '1D Array, 2D Array' => [ [[1, 2], [3, 4]], [[2, 1], [4, 3]], ], '1D Array, 2D Hash' => [ [['a' => 1, 'b' => 2], ['c' => 3, 'd' => 4]], [['b' => 2, 'a' => 1], ['d' => 4, 'c' => 3]], ], '1D Hash, 2D Array' => [ ['a' => [1, 2], 'b' => [3, 4]], ['b' => [4, 3], 'a' => [2, 1]], ], '1D Hash, 2D Hash' => [ ['a' => ['a1' => 11, 'a2' => 12], 'b' => ['b1' => 21, 'b2' => 22]], ['b' => ['b2' => 22, 'b1' => 21], 'a' => ['a2' => 12, 'a1' => 11]], ], 'Array, Hash mixed' => [ ['a' => ['a1' => 11, 'a2' => 12], 'b' => [21, 22]], ['b' => [22, 21], 'a' => ['a2' => 12, 'a1' => 11]], ], ]; } /** * @test * @dataProvider data_assertArraySimilar_expected_failure * @param array $expected * @param array $actual */ public function assertArraySimilar_expected_failure(array $expected, array $actual) { try { $this->assertArraySimilar($expected, $actual); } catch (\PHPUnit\Framework\ExpectationFailedException $e) { return; } $this->fail('Expected exception was not thrown.'); } public function data_assertArraySimilar_expected_failure() { return [ '1D Array' => [ [1, 2, 3], [1, 2, 4], ], '1D Hash' => [ ['a' => 1, 'b' => 2, 'c' => 3], ['a' => 1, 'b' => 2, 'd' => 3], ], '1D Array, 2D Array' => [ [[1, 2], [3, 4]], [[1, 2], [3, 5]], ], '1D Array, 2D Hash' => [ [['a' => 1, 'b' => 2], ['c' => 3, 'd' => 4]], [['a' => 1, 'b' => 2], ['c' => 3, 'e' => 4]], ], '1D Hash, 2D Array' => [ ['a' => [1, 2], 'b' => [3, 4]], ['a' => [1, 2], 'b' => [3, 5]], ], '1D Hash, 2D Hash' => [ ['a' => ['a1' => 11, 'a2' => 12], 'b' => ['b1' => 21, 'b2' => 22]], ['a' => ['a1' => 11, 'a2' => 12], 'b' => ['b1' => 21, 'b3' => 22]], ], 'Array, Hash mixed' => [ ['a' => ['a1' => 11, 'a2' => 12], 'b' => [21, 22]], ['a' => ['a1' => 11, 'a3' => 12], 'b' => [21, 22]], ], ]; } }さいごに、冒頭で述べたように、本来、できることなら、順番も保証された上でのテストであったほうが良いとは思うので「なぜ期待した順番にならないのか。」というところも調査するのがいいのかもしれません。
- 投稿日:2020-06-28T00:16:04+09:00
学習日記8日目(2020/6/27)
学習内容
- JavaScript基礎学習:Progateの学習コース全終了
JSの学習を終了してPHPやLaravelの学習に移っていきたいと思います。
その他
- タイピング練習
- paizaのスキルチェックに挑戦
参加しているサロンの方々がやっているのを目にし、気になったのでpaizaのスキルチェックやってみました。しかし、、標準入力ってなんやねん!という感じでそもそも入力値を変数等に代入できなくて積みました?
そんなこんなでfgets関数を使ったことがなかったので調べ、どうもfgets関数を繰り返し実行すれ入力を一行目から順に取り出せるらしいことがわかったので明日以降Cランクの問題にチャレンジしようと思います。今日はDランクの問題1問解いて終了です。
明日の予定
- UdemyでPHP&Laravelの学習
- 投稿日:2020-06-28T00:08:35+09:00
Laravelアプリ同士をWeb APIで連携させる
オンプレで動く Laravel と、クラウドで動く Laravel を、Web API でシステム間連携させた話。
やりたいこと
社内オンプレの給与明細APサーバにアクセスすると、クラウドの勤怠打刻APサーバのデータも取得し、給与明細と合わせて勤怠実績明細も表示させる。
Web API の設計
設計時に決めておくべき項目。
HTTPメソッド
GETエンドポイント
/api/v1/kintai/{yyyyMM}/{勤怠個人番号}
機能概要
従業員の勤怠実績データを返すリクエスト
yyyyMM
・・・ 年月("last" と指定された場合は最終年月とする)勤怠個人番号
・・・ 従業員の勤怠個人番号(Active Directory に照会すること)レスポンス
- ステータスコード
200
・・・ 成功404
・・・ リクエストされた勤怠実績が存在しなかった- 応答形式 ・・・ JSON
REST APIはステートレスなので、認証にCookieやセッションは使えない。APIではトークン認証やBASIC認証が一般的だが、今回は社内システムということもあり、簡素化のため認証は省略する。
【参考】人事労務パッケージが提供するAPI
- 人事労務freee API - https://developer.freee.co.jp/docs/hr
- SmartHR API - https://developer.smarthr.jp/api/index.html
Laravelでの実装方法
勤怠打刻APサーバ
APIが呼ばれる側のサーバ。
api.php
にエンドポイントを定義する。web.php
とは違い、api.php
にはCSRF保護が無い。routes\api.phpRoute::get('v1/kintai/{yyyymm}/{id}', 'Api\KintaiController@index');呼ばれるコントローラを作成する。
app\Http\Controllers\Api\KintaiController.php<?php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use App\Models\Result; use Carbon\Carbon; use App\Http\Controllers\Controller; /* 給与明細サーバから呼ばれるクラス */ class KintaiController extends Controller { public function index(Request $request, $yyyymm, $syaincd) { if ($yyyymm == 'last') { // last指定時は最終年月を仮定 $yyyymm = Carbon::yesterday()->format('Ym'); } $baseDate = Carbon::create(substr($yyyymm, 0, 4), substr($yyyymm, 4, 2), 1); // 基準日の設定 // 勤怠実績データベースの読み込み $query = Result::where('syaincd', $syaincd) ->whereBetween('kintaijissekiymd', array($baseDate->copy()->startOfMonth()->format('Ymd'), $baseDate->copy()->endOfMonth()->format('Ymd'))) ->orderBy('kintaijissekiymd'); // レコード件数が0より大きければデータを返し、そうでなければエラーレスポンスとする if ($query->count() > 0) { return response()->json([ 'code' => 200, 'contents' => $query->get() ], 200); } else { return response()->json([ 'code' => 404, 'message' => 'Not Found' ], 404); } } }Eloquentモデルでは、文字列にキャストされるときJSONに変換されるため、Eloquentオブジェクトをそのまま返すことができる。
コマンドからAPIをテストする
curl
コマンドで得られるJSONデータをjq
コマンドで整形して確認。シェルからコマンドを実行curl -s https://example.com/api/v1/kintai/202006/12765 | jq
出力結果{ "code": 200, "contents": [ { "syaincd": 12765, "syainmei": "山田 太郎", "kanrisyouninkengencode": "一般社員", "kintaijissekiymd": "20200625", "syugyozikantaikaishihhmi": "0850", "syugyozikantaisyuryohhmi": "1730", "meisyo": "出勤", "barcodesyukkinhhmi": "0842", "barcodetaikinhhmi": "1738", :給与明細APサーバ
APIを呼ぶ側のサーバ。
昨今のWebアプリでは、APIで取得したデータはVueやReactで表示することが多いが、ここではLaravelで受けてビュー(Blade)に渡す。app\Http\Controllers\KintaiController.php(一部)$context = stream_context_create(array( 'http' => array('ignore_errors' => true) )); $kintaicd = $request->session()->get('user')->kintaicd; // APIを呼び出す $json = file_get_contents("https://example.com/api/v1/kintai/$yyyymm/$kintaicd", false, $context); $ary = json_decode($json); // JSONを配列に // 正常なら "contents" をViewに渡す if ($ary->code == 200) { $reports = $ary->contents; } else { $reports = null; } return view('kintai', compact('reports')) ->with('spec_type', 'kintai') ->with('ym', substr($yyyymm, 0, 4) . '年' . ltrim(substr($yyyymm, 4, 2), '0') . '月') ->with('prev_month', $dt->copy()->subMonth(1)->format('Ym')) ->with('next_month', $dt->copy()->addMonth(1)->format('Ym'));ビュー(Blade)で $reports を展開する。
resources\views\kintai.blade.php<div class="table-responsive"> <table class="table text-nowrap table-bordered table-striped table-hover table-condensed"> <thead> <tr> <td rowspan="2">日付</td> <td rowspan="2">勤怠種別</td> <td colspan="2">打刻</td> : 省略 </tr> <tr> <td>出勤</td> <td>退勤</td> <td>就業開始</td> : 省略 </tr> </thead> <tbody> @foreach ($reports as $report) <tr> <td>{{ $report->kintaijissekiymd }}</td> <td>{{ $report->syukkinhhmi }}</td> <td>{{ $report->taikinhhmi }}</td> : 省略 </tr> @endforeach </tbody> </table> </div>
- 投稿日:2020-06-28T00:08:35+09:00
Laravelアプリ同士をWeb APIで連携させるには
オンプレで動く Laravel と、クラウドで動く Laravel を、Web API でシステム間連携させてみた話。
やりたいこと
社内オンプレの給与明細APサーバにアクセスすると、クラウドの勤怠打刻APサーバのデータも取得し、給与明細と合わせて勤怠実績明細も表示できるようにする。
シーケンス図で書くと次のようになる。
明細表示例
Web API の設計
設計時に決めておくべき項目。
HTTPメソッド
GETエンドポイント
/api/v1/kintai/{yyyyMM}/{勤怠個人番号}
機能概要
従業員の勤怠実績データを返すリクエスト
yyyyMM
・・・ 年月("last" と指定された場合は最終年月とする)勤怠個人番号
・・・ 従業員の勤怠個人番号(Active Directory に照会すること)レスポンス
- ステータスコード
200
・・・ 成功404
・・・ リクエストされた勤怠実績が存在しなかった- 応答形式 ・・・ JSON
REST APIはステートレスなので、認証にCookieやセッションは使えない。APIではトークン認証やBASIC認証が一般的だが、今回は社内限定利用ということもあり、簡素化のため認証は省略する。
【参考】人事労務パッケージが提供するAPI
- 人事労務freee API - https://developer.freee.co.jp/docs/hr
- SmartHR API - https://developer.smarthr.jp/api/index.html
Laravelでの実装方法
勤怠打刻APサーバ
APIが呼ばれる側のサーバ。
api.php
にエンドポイントを定義する。web.php
とは違い、api.php
にはCSRF保護が無い。routes\api.phpRoute::get('v1/kintai/{yyyymm}/{id}', 'Api\KintaiController@index');呼ばれるコントローラを作成する。
app\Http\Controllers\Api\KintaiController.php<?php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use App\Models\Result; use Carbon\Carbon; use App\Http\Controllers\Controller; /* 給与明細サーバから呼ばれるクラス */ class KintaiController extends Controller { public function index(Request $request, $yyyymm, $syaincd) { if ($yyyymm == 'last') { // last指定時は最終年月を仮定 $yyyymm = Carbon::yesterday()->format('Ym'); } $baseDate = Carbon::create(substr($yyyymm, 0, 4), substr($yyyymm, 4, 2), 1); // 基準日の設定 // 勤怠実績データベースの読み込み $query = Result::where('syaincd', $syaincd) ->whereBetween('kintaijissekiymd', array($baseDate->copy()->startOfMonth()->format('Ymd'), $baseDate->copy()->endOfMonth()->format('Ymd'))) ->orderBy('kintaijissekiymd'); // レコード件数が0より大きければデータを返し、そうでなければエラーレスポンスとする if ($query->count() > 0) { return response()->json([ 'code' => 200, 'contents' => $query->get() ], 200); } else { return response()->json([ 'code' => 404, 'message' => 'Not Found' ], 404); } } }Eloquentモデルでは、文字列にキャストされるときJSONに変換されるため、Eloquentオブジェクトをそのまま返すことができる。
コマンドからAPIをテストする
curl
コマンドで得られるJSONデータをjq
コマンドで整形して確認。シェルからコマンドを実行curl -s https://example.com/api/v1/kintai/202006/12765 | jq
出力結果{ "code": 200, "contents": [ { "syaincd": 12765, "syainmei": "山田 太郎", "kanrisyouninkengencode": "一般社員", "kintaijissekiymd": "20200625", "syugyozikantaikaishihhmi": "0850", "syugyozikantaisyuryohhmi": "1730", "meisyo": "出勤", "barcodesyukkinhhmi": "0842", "barcodetaikinhhmi": "1738", :給与明細APサーバ
APIを呼ぶ側のサーバ。
昨今のWebアプリでは、APIで取得したデータはVueやReactで表示することが多いが、ここではLaravelで受けてビュー(Blade)に渡す。app\Http\Controllers\KintaiController.php(一部)$context = stream_context_create(array( 'http' => array('ignore_errors' => true) )); $kintaicd = $request->session()->get('user')->kintaicd; // APIを呼び出す $json = file_get_contents("https://example.com/api/v1/kintai/$yyyymm/$kintaicd", false, $context); $ary = json_decode($json); // JSONを配列に // 正常なら "contents" をViewに渡す if ($ary->code == 200) { $reports = $ary->contents; } else { $reports = null; } return view('kintai', compact('reports')) ->with('spec_type', 'kintai') ->with('ym', substr($yyyymm, 0, 4) . '年' . ltrim(substr($yyyymm, 4, 2), '0') . '月') ->with('prev_month', $dt->copy()->subMonth(1)->format('Ym')) ->with('next_month', $dt->copy()->addMonth(1)->format('Ym'));ビュー(Blade)で $reports を展開する。
resources\views\kintai.blade.php<div class="table-responsive"> <table class="table text-nowrap table-bordered table-striped table-hover table-condensed"> <thead> <tr> <td rowspan="2">日付</td> <td rowspan="2">勤怠種別</td> <td colspan="2">打刻</td> : 省略 </tr> <tr> <td>出勤</td> <td>退勤</td> <td>就業開始</td> : 省略 </tr> </thead> <tbody> @foreach ($reports as $report) <tr> <td>{{ $report->kintaijissekiymd }}</td> <td>{{ $report->syukkinhhmi }}</td> <td>{{ $report->taikinhhmi }}</td> : 省略 </tr> @endforeach </tbody> </table> </div>
- 投稿日:2020-06-28T00:00:59+09:00
Laravel MySQL DBのカラムタイプを途中で変更する
目的
- Laravelにて新規作成後のDBのカラムタイプの変更方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提情報
- MySQLを使用しているが確認箇所以外はそのほかのRDSでも作業内容は変わらない。
- カラム名を変更するとLaravelアプリで自分で実装したデータ取得処理を変更しないといけない場合があるので注視する。
- すでに存在するカラムのタイプ(データ型や有効文字数など)を変更する際の方法を記載する。
- 現在のカラムタイプが下記に記載されているものではないと変更することはできない。
- bigInteger
- binary
- boolean
- date
- dateTime
- dateTimeTz
- decimal
- integer
- json
- longText
- mediumText
- smallInteger
- string
- text
- time
- unsignedBigInteger
- unsignedInteger
- unsignedSmallInteger
- uuid
- 公式ドキュメントに記載されている内容を元に自分なりにまとめてみる。
概要
- ライブラリのインストール
- マイグレーションファイルの作成と記載
- マイグレート
- 確認
詳細
ライブラリのインストール
アプリ名ディレクトリで下記コマンドを実行してライブラリをインストールする。
$ composer require doctrine/dbalマイグレーションファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行してマイグレーションファイルを作成する。(下記のファイル名出なくても良い。自分がわかりやすいと思うものにする必要がある。)
$ php artisan make:migration change_カラムタイプ変更するカラム名_既存カラムタイプ_to_変更後のカラムタイプ_on_所属テーブル名_table --table=所属テーブル名アプリ名ディレクトリで下記コマンドを実行してマイグレーションファイルを開く。
$ vi database/migrations/YYYY_MM_DD_XXXXXX_change_カラムタイプ変更するカラム名_既存カラムタイプ_to_変更後のカラムタイプ_on_所属テーブル名_tableマイグレーションファイルを下記の様に修正する。(
public function up()
にはマイグレーションにより実行したい処理(今回だとカラムタイプの変更)処理を記載する。public function down()
にはロールバックで実行したい(カラムタイプを元に直す)処理)database/migrations/YYYY_MM_DD_XXXXXX_change_変更する前のカラム名_to_変更した後のカラム名_on_所属テーブル名_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class Rename対象カラム名変更前カラムタイプTo変更後カラムタイプOnテーブル名Table extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('テーブル名', function (Blueprint $table) { //下記を追記する $table->変更したいデータ型('カラム名')->change(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('テーブル名', function (Blueprint $table) { //下記を追記する $table->今までのデータ型('カラム名')->change(); }); } }マイグレート
アプリ名ディレクトリで下記コマンドを実行してマイグレートを実行する。
$ php artisan migrate確認
下記コマンドを実行してMySQLにターミナルからログインする。(MySQLのパスワードを忘れてしまった方はこちら→Mac ローカル環境の MySQL 8.0 のrootパスワードを忘れた時のリセット方法)
$ mysql -u root -p下記を実行してカラムタイプが変更されたことを確認する。
mysql> show columns from DB名.テーブル名;メモ
- stringからintだとできなかったけどstringからintegerの指定だといけるかも知れん。試してみて
参考文献