- 投稿日:2019-12-13T23:58:14+09:00
Laravel の認可(Policy)にモデル以外のパラメータを与える
この記事について
Laravel #2 Advent Calendar 2019 - Qiita 14日目の記事です。
表題の通りではあるんですが、意外と使われてないなぁと思ったので記事にすることにしました。
はじめに
環境
- Laravel 6.6.0
5.x でも同じです。
Policy について
特定のモデルに対し、ユーザーが操作権限があるかどうかを判定する仕組みで、公式ドキュメントには以下のような使用例が載っています。
class PostPolicy { public function update(User $user, Post $post) { return $user->id === $post->user_id; } }呼び出し側は、
class PostController extends Controller { public function update(Request $request, Post $post) { $this->authorize('update', $post); } }みたいになります。
この仕組みを利用するメリットは、
- 渡されたモデルのインスタンスに応じて Policy クラスをよしなに選択してくれる
- User インスタンスを自動でバインドしてくれる(デフォルトでは現在ログインしているユーザー)
- 権限がない場合は勝手に 403 エラーにしてくれる
あたりでしょうか。
権限管理が複雑なアプリケーションだと、アプリケーション独自で認可機構を実装したほうが柔軟にできると思うので、無理に使う必要はないと思いますが、そこはチームのポリシー次第、ということで。
ユースケース
- 他のモデルの状態が必要
- 静的な権限テーブルみたいなやつがある
- 動的にポリシーを差し替えたい
ざっと思いついたものを挙げましたが、基本的にはぜんぶ一緒です。
結論
authorize メソッドに配列で引数を渡すと、Policy の各メソッドには、スプレッド演算子によって展開された形で渡ってくるので、それらを使って複雑な認可ルールに対処できます。
呼び出し側でこのように呼ぶと、
$this->authorize('update', [$mainModel, $subModel]);Policy のメソッドにはこのように渡ってきます。
public function update(User $user, MainModel $main, SubModel $sub) {使用例
1. 他のモデルの状態が必要
ユーザーにも対象のモデルにもひもづかない、別のモデルの状態が必要になったとき、Controller でいったんそのモデルのインスタンスを取得して、authorize メソッドに渡してやります(ちょっといい例が浮かばなかったので適当なモデル名になっていますが、ご容赦ください)。
public function update(Request $request, Post $post) { $someModel = SomeModel::findOrFail($request->some_model_id); $this->authorize('update', [$post, $someModel]); }Policy 側では以下のように状態を参照できます(メソッドを呼んでもいいでしょう)。
public function update(User $user, Post $post, SomeModel $someModel) { return $user->id === $post->user_id && $someModel->acceptable; }2. 静的な権限テーブルみたいなやつがある
たとえばユースケースごとに操作可能な権限のリストを持っていて、それに合致しない場合は弾く、みたいなケースです。
ユースケースに決め打ちで(もしくはなんらかのルールに基づいてデータベースから取ってくるとかでも)、
class UpdateTask extends UseCase { public function validRoles(): array { return [Role::Admin, Role::Wheel]; } public function invoke() {...} }みたいに権限のリストがあり、それを以下のように authorize に渡してやります。
public function update(UpdateTask $useCase, Post $post) { $this->authorize('update', [$post, $useCase->validRoles()]); }Policy 側はこうなります。
public function update(User $user, Post $post, array $roles) { return $user->hasRole($roles) || $user->id === $post->user_id; }3. 動的にポリシーを差し替えたい
ユースケースごとに独自で認可ルール(関数)を持っていて、そいつをなんらかのルールで動的に生成する、といったようなケースです。
class UpdateTask extends UseCase { public function authorizeRule(): \Closure { return function (User $user, Task $task): bool { // ... }; } public function invoke() {...} }呼び出し側は、
public function update(UpdateTask $useCase, Post $post) { $this->authorize('update', [$post, $useCase->authorizeRule()]); }で、Policy 側はこうなります。
public function update(User $user, Post $post, \Closure $rule) { return $rule($user, $task); }ここまでくると、Policy 使う必要ない気もしてきますが、認可の仕組み自体は Laravel に任せて、ドメインロジックはドメインモデルに任せる、という分担もやりやすいと思うので、検討する価値はあるんじゃないかと思います。
おわりに
上記に挙げた以外にもユースケースがありましたら、コメント欄にて教えていただけると助かります
![]()
- 投稿日:2019-12-13T23:40:55+09:00
フレームワークを理解するために自作PHPフレームワーク作ってみた
この記事は2019新卒 エンジニア Advent Calendar 2019の1日目の記事です。
(参加しようと思ったのが13日でしたが、空いている枠が1日しかありませんでしたので1日目の記事として投稿しました。すみません。)動機
仕事でフレームワークを使ってWebアプリを開発しています。
自作でフレームワークを作ることで、
- プログラマとしてのスキルアップ
- フレームワークが何をしているのか理解できるようになり、バグの原因発見が容易になる
- 機能追加の際にフレームワークの動きも加味していいコードが書けるようになる
以上のメリットがあると考えたためです。
どのように作ったか
パーフェクトPHPの第7~8章を参考にしました。
得られた知見(大きなもの一部抜粋)
スクラッチでフレームワークを作ることで以下のような知見を得られました。
PHPがどのようにレスポンスを返すか
header('HTTP/1.1 200 OK') #HTTP header echo $content; #HTTP body恥ずかしいことにechoでresponsebodyを返していることすら理解していませんでした...
HTTPRequest/Responseを追えるようになったのは大きな成長だと思います。リクエストURLからコントローラへの橋渡し
$tokens = explode('/', ltrim($url, '/')); foreach ($tokens as $i => $token) { if (0 === strpos($token, ':')) { $name = substr($token, 1); $token = '(?P<' . $name . '>[^/]+)'; } $tokens[$i] = $token; } $pattern = '/' . implode('/', $tokens); $routes[$pattern] = $params;urlを分割し、controller名とaction名に分ける処理です
この後ルーティングを参照し、controller名を使って目的のphpをrequireします
普段投げているリクエストURLがどのようにcontrollerと結びついているのかを知ることができましたまとめ
普段意識せずにviewとcontrollerを配置してwebアプリを実装していました。
今回のようにスクラッチでフレームワークを実装してみると大小様々に得られるものが多く、特に
- PHPの言語的な理解
- フレームワーク内の処理に関する理解
が深まって良いのではないかと思います。
- 投稿日:2019-12-13T22:43:42+09:00
【Laravel】保守性をアップさせるかもしれない 3 つのテクニック
はじめに
初めまして、MasaKu です。
今年も残すところわずかですね。
今年もたくさんのコードを書いてきたことだと思います。
中には埃をかぶりまくった個人開発のコードもあるかと思いますが、年末のこの時期にちょっとだけリファクタリングとして手を入れていみませんか?
今回は、Laravel で作成されたアプリケーションを、ちょこっとした工夫だけで可読性をアップできるかもしれないテクニックをご紹介します。
その1:ルートグループ
Laravel では
web.php
にルーティングする処理を記載します。超ざっくりしたブログページのサンプルを作成しました。
// ホーム画面 Route::get('/home', 'HomeController@home'); // ブログページ Route::get('/blog', 'BlogController@blog'); Route::get('/create', 'BlogController@create'); Route::get('/update', 'BlogController@update'); Route::get('/delete', 'BlogController@delete');この時、ブログページにアクセスする際は、twitter の OAuth でログイン認証している状態にさせたいという場合は各ルーティングにミドルウェアを設定をするのが便利かと思います。
// ブログページ Route::get('/blog', 'BlogController@blog')->middleware('oAuth'); Route::get('/create', 'BlogController@create')->middleware('oAuth'); Route::get('/update', 'BlogController@update')->middleware('oAuth'); Route::get('/delete', 'BlogController@delete')->middleware('oAuth');もちろん、このように1件ずつ Middleware を設定することもできるのですが、これだと Middleware を差し替えようとした際に、全ての Middleware の定義を修正しなければならなくなります。
それに、こういったコードがたくさん並ぶとパッと見たときに、どの Middleware を設定しているのかがわかりづらいし、Middleware を付け忘れてしまうことにもなりかねません。
こういう時は、ルーティングをグループ化して、そのグループに Middleware を設定するとコードがスッキリしてわかりやすくなります。
// ブログページ Route::middleware(['oAuth'])->group(function(){ Route::get('/blog', 'BlogController@blog'); Route::get('/create', 'BlogController@create'); Route::get('/update', 'BlogController@update'); Route::get('/delete', 'BlogController@delete'); });このようにすれば、パッと見ただけで Middleware をかける処理が一覧として理解しやすく付け忘れるということもなくなると思います。
ちなみに、グループ化しておくことのメリットとして、グループ内のコントローラ対して名前空間を指定することもできるということもあります。
今後コントローラの名前空間を変更した場合なども一括で設定することができるので非常に便利です。
// ブログページ Route::namespace('Blog')->middleware(['oAuth'])->group(function(){ Route::get('/blog', 'BlogController@blog'); Route::get('/create', 'BlogController@create'); Route::get('/update', 'BlogController@update'); Route::get('/delete', 'BlogController@delete'); });その2:サービスコンテナからサービスクラスを利用する
Laravel ではコントローラ内に処理を記載して、処理結果を View に渡すようにしていきます。
そのため、コントローラ内が複雑になってしまうことをできるだけ避けたいのではないでしょうか。
特に、似たような処理がほかのコントローラ内にあるような状態はできるだけ避けたいです。
そんなときは、共通処理をサービスクラスに書き出してサービスコンテナに登録し、コントローラ内でそのクラスのインスタンスを利用するようにしましょう。
Laravel プロジェクトの
app
フォルダ内にOriginClasses
というフォルダを作成します。その後、
OriginService.php
というファイルを作成し、以下のように入力してください。<?php namespace App\OriginClasses; class OriginService { private $msg; private $data; public function __construct() { $this->msg = 'サービスクラスを取得'; $this->data = ['ホーム', 'ブログ']; } public function getMessage() { return $this->msg; } public function getData() { return $this->data; } }メッセージとデータを取得する共通処理です。
この処理を各コントローラ内で呼び出して利用できるようにしていきます。
Laravel では
app()
というメソッドを通してサービスコンテナからインスタンスを取得することができます。以下のようにコントローラ内に
app('App\OriginClasses\OriginService')
を記載することで、インスタンスを取得することができます。public function blog() { // OriginServiceクラスのインスタンスを取得 $originService = app('App\OriginClasses\OriginService'); echo $originService->getMessage(); return view('blog'); }このようにしておくことで、コントローラ内に共通の処理がいたるところにベタ書きされることを防ぐことができます。
今後、同じ処理を別のコントローラに埋め込みたくなったときや、これまで定義していた共通処理を修正する場合も、クラスファイルのメソッドを修正するだけですべてのコントローラ内の処理を修正することができるので非常に便利です。
その3:ファサードを使う
先程はサービスコンテナからインスタンスを取得する方法をご紹介しましたが、サービスを利用する入口だけを使ってサービスを利用する方法もがります。
ファサードではコントローラ側でインスタンスを取得するのではなく、サービスの入口だけを利用して処理することができます。
まずは、Laravel プロジェクト内の
app
フォルダにFacades
というフォルダを作成します。その後、
Facades
フォルダ内にOriginService.php
というファイルを作成して以下のように入力してください。<?php namespace App\Facades; use Illumination\Support\Facades\Facade; class OriginService extends Facade { protected static function getFacadeAccessor() { return 'originservice'; } }それでは、作成したファサードを
/config/app.php
のaliases
配列に追加していきます。'aliases' => [ 'App' => Illuminate\Support\Facades\App::class, 'Arr' => Illuminate\Support\Arr::class, // 一部省略 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, 'originservice' => App\Facades\MyService::class, //この行を追加 ],最後に
aliases
に登録したファサードを Laravel のサービスプロバイダに設定して利用できるようにしましょう。サービスプロバイダは以下のコマンドで作成できます。
php artisan make:provide OriginServiceProvider作成した
OriginServiceProvider.php
を以下の通り修正してください。<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class OriginServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { app()->singleton('originservice', 'App\OriginClasses\OriginSevice'); } /** * Bootstrap services. * * @return void */ public function boot() { // } }作成したサービスプロバイダを
/config/app.php
に設定します。'providers' => [ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, // 一部省略 App\Providers\RouteServiceProvider::class, App\Providers\OriginServiceProvider::class, //この行を追加 ]これでファサードを通してサービスを利用できるようになりました。
あとは以下のようにすることでコントローラ側からサービスを利用することができます。
use App\Facades\OriginService; class BlogController extends Controller { public function blog() { OriginService::getMessage(); return view('blog'); }ファサードの処理の流れを整理しますと以下のとおりになります
- ファサードを作成し
getFacadeAccessor()
メソッドに利用するサービスのファサード(入口)の名前を返すようにするconfig/app.php
にファサードを登録する(エイリアスとファサードの実態を登録)- サービスプロバイダを作成し、利用するサービス(コントローラ側で呼び出したい処理)をファサードの名前で設定する
- コントローラ側でファサードを経由してサービスの処理を実行する
おわりに
いかがでしたでしょうか。
いずれも基本的な内容にはなりますが、もし取り入れていなくてもちょっとした工夫だけで直ぐに取り入れることができる内容なので、リファクタリングとしてちょうど良い内容なのではないかと思いました。
ぜひお試しあれ!
参考資料
- 投稿日:2019-12-13T18:32:51+09:00
pukiwiki を markdown に変換する pukiwiki2markdown というツールを公開しました
とはいえ、1年前くらいに書いたツールなんですが、
「そういえば Qiita にどうやって実装したのか、とか書いてないなー」
と思ったので供養がてら記事しました。(∩´∀`)∩
デモサイト
https://pukiwiki2markdown.saino.me/
画面向かって左側の input に pukiwiki の文章を入れると右側に変換結果が表示されます。
変換結果をクリップボードにコピーすることもできます。
ソースコード (Github)
https://github.com/kaishuu0123/pukiwiki2markdown
API っぽく動くエンドポイント
下記コマンドを実行することで、pukiwiki 文法から markdown 形式に変換できます。
body の中に文章を入れる感じですね。
curl -XPOST https://pukiwiki2markdown.saino.me/api/v1/convert \ -H 'Content-Type: application/json' \ -d '{"body": "*Header1\n**Header2\n"}'実装するのに使った技術
バックエンド
- PHP + Slim Framework 3
- pukiwiki のコード本体
Slim Framework というのは本家のサイトでも謳っている通り、PHP のマイクロフレームワークです。
選定理由としては 「pukiwiki のコードをそのまま再利用したかったから」 ということに尽きます。pukiwiki 文法のパーサーを書こうかな〜とも思ったのですが、pukiwiki の元コードを見るとゴリゴリにロジックが書かれていたので、「これは元のコードを再利用した方が圧倒的に早い」と思い、変換するロジックも PHP 側に寄せました。
(変換ツールなので割と使い捨てっちゃ使い捨てですし)
フロントエンド
- React
- webpack
こちらはいわゆる「普通のフロントエンド」という感じで、何ら変わったことはしてないです。
ただ単にテキストを入力して変換結果をもらうだけなので、そんなに React の恩恵は受けていません。
最後に
自前で建てたい方はリポジトリの中に docker-compose.yml を同梱しているのでお試しください。
Docker image 提供は今のところ考えてはいません。本当はやった方がいいのかもしれないけど、変換するだけのツールですので。今後何らかの改善点があるかな〜と考えましたが、現在のところ割と悪くない精度だったので、このまましばらく運用するかもしれません。
何かご意見あれば Issue や Pull Request は受け付けていますので、よろしくおねがいします٩(๑>◡<๑)۶
- 投稿日:2019-12-13T17:39:41+09:00
Laravelでリサイズした画像をdropboxにアップロードする
はじめに
iPhoneで撮った写真をDropboxにアップロードする機能を導入する際に、無料で使える容量が2Gしかなかったのでサイズを小さくしてアップロードしようとしたけど詰まったので備忘録としてやり方を載せておきます。
環境・事前準備
- Laravel 6.0
- Dropboxアカウント
Dropbox接続手順
composerでDropboxを操作するためのライブラリをインストール。
composer require benjamincrozat/laravel-dropbox-driverconfig/app.php'providers' => [ BC\Laravel\DropboxDriver\ServiceProvider::class, ],filesystems.php'disk' => [ 'dropbox' => [ 'driver' => 'dropbox', 'token' => env('DROPBOX_TOKEN'), ],dropboxのaccessTokenを環境変数にセット。
.envDROPBOX_TOKEN=accessTokenIntervention Image導入手順
画像をリサイズするためにパッケージをcomposerでインストール
php aritsan require intervention/imageconfig/app.php'providers' => [ Intervention\Image\ImageServiceProvider::class, ], 'alianses' => [ 'Image' => Intervention\Image\Facades\Image::class, ],画像アップロード処理
Controllers/HelloController.php//画像のリサイズとアップロードするために下記2つを追加 use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image; public function store(Request $request) { //アップロードされたリクエストの中に画像があるかチェック if($request->hasFile('image')){ $file = $request->file('image'); $imgName = $file->getClientOriginalName(); //画像リサイズ処理(横幅を1200pxに指定) $image = Image::make($file) ->resize(1200,null,function($constraint{ $constraint->aspectRatio();//aspectRatio、縦幅アスペクト比維持で自動調整 }); //putメソッドの第一引数にパスを指定(下記のコードではdropboxデベロッパーで設定したフォルダ直下に保存される) Storage::disk('dropbox')->put($imgName,(string)$image->encode('jpg',100),'public'); } return view('Hello'); }おしまい
AWS s3がアカウント認証でこけたので今回Dropboxを使うことにしました!
エンジニアとしてまだ未熟なので間違っているところがあればコメントいただけると助かります!
- 投稿日:2019-12-13T17:16:52+09:00
牛に言葉を喋らせる
cowsay というプロジェクトをご存知だろうか。
その名の通り、牛に言葉を喋らせるという高尚なプロジェクトである。
私も兼ねてから、このプロジェクトに参加したいと思っていました。そして今日、牛に言葉を話させることに成功した。
$ php say.php < Ohmg I'm a cow! > \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||さらに、タックスにも言葉を喋らせられる。
$ php say.php -ctux -m僕はタックス! < 僕はタックス! > \ \ .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/さらには、ドラゴンも召喚できる。
$ php say.php -cdragon -mお腹がすいたよ < お腹がすいたよ > \ / \ //\ \ |\___/| / \// \\ /0 0 \__ / // | \ \ / / \/_/ // | \ \ @_^_@'/ \/_ // | \ \ //_^_/ \/_ // | \ \ ( //) | \/// | \ \ ( / /) _|_ / ) // | \ _\ ( // /) '/,_ _ _/ ( ; -. | _ _\.-~ .-~~~^-. (( / / )) ,-{ _ `-.|.-~-. .~ `. (( // / )) '/\ / ~-. _ .-~ .-~^-. \ (( /// )) `. { } / \ \ (( / )) .----~-.\ \-' .~ \ `. \^-. ///.----..> \ _ -~ `. ^-` ^-_ ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~ /.-~素晴らしい。
我が物のように書いてきたが、これは公開されているリポジトリを使っているだけです。
https://github.com/alrik11es/cowsayphpコマンドラインで牛に言葉を喋らせる
リポジトリをクローンする。
ただ喋らすだけなら、クローンせず
composer require alrik11es/cowsayphp
で良いのだが、後ほどキャラクターを追加したいので、クローンする。$ git clone git@github.com:zumikiti/cowsayphp.git $ cd cowsayphp/say.php を作る
say.php<?php require 'vendor/autoload.php'; use Cowsayphp\Farm; use Cowsayphp\Farm\Cow; use Cowsayphp\Farm\Dragon; use Cowsayphp\Farm\Tux; use Cowsayphp\Farm\Whale; // コマンドラインから -c と -m を受け取る $args = getopt('c:m:'); // それぞれ変数に代入 $char = $args['c'] ?? 'cow'; $msg = $args['m'] ?? 'Ohmg I\'m a cow!'; // cowsayphp にデフォルトで用意されているキャラクターを代入 switch ($char) { case 'tux': $cow = Farm::create(Tux::class); break; case 'dragon': $cow = Farm::create(Dragon::class); break; case 'whale': $cow = Farm::create(Whale::class); break; default: $cow = Farm::create(Cow::class); } // 喋らす echo $cow->say($msg);コマンドラインで実行する
$ php say.php < Ohmg I'm a cow! > \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||引数に
-c
にキャラクター名、-m
で喋らせたい言葉を指定できます。$ php say.php -ctux -m僕はタックス < 僕はタックス > \ \ .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/キャラクターを追加する
自分の好きなキャラクターを追加したいですよね。
例えば、みんな大好きカービィを追加したい。
src/Farm/Kirby.php
を追加するなお、カービィのAAは https://smashwiki.info/%E3%82%AB%E3%83%BC%E3%83%93%E3%82%A3%E3%81%AEAA からお借りしました。
src/Farm/Kirby.php<?php namespace Cowsayphp\Farm; use Cowsayphp\AbstractAnimal; class Kirby extends AbstractAnimal { protected $character = <<<DOC {{bubble}} \ ,-‐――、 \/ ┃ ┃ ヽ-、 し " ∇ " |‐' ヽ___ _ノ、 'ー-' ̄ `ー-' DOC; }say.php の switch に kirby を追加
say.phpcase 'kirby': $cow = Farm::create(Kirby::class); break;コマンドラインで実行する
php say.php -ckirby < Ohmg I'm a cow! > \ ,-‐――、 \/ ┃ ┃ ヽ -、 し " ∇ " |‐' ヽ___ _ノ、 'ー-' ̄ `ー-'カービィのデフォルトのメッセージは
ぽよぽよ〜
にしたい。say.php- $msg = $args['m'] ?? "Ohmg I'm a cow!"; switch ($char) { ... case 'kirby': $cow = Farm::create(Kirby::class); + $msg = 'ぽよぽよ〜'; break; ... } - echo $cow->say($msg); + echo $cow->say($msg ?? ($args['m'] ?? 'Ohmg I\'m a cow!'));コマンドラインで実行する
php say.php -ckirby < ぽよぽよ〜 > \ ,-‐――、 \/ ┃ ┃ ヽ-、 し " ∇ " |‐' ヽ___ _ノ、 'ー-' ̄ `ー-'終わりに
私が普段 PHP を使っているので、一番いじりやすいと感じた
cowsayphp
を使ってみましたが、cowsay は多くの言語で実装されており、さらには cow 以外のキャラクターを使ったものも多く存在します。ざっと、GitHub で検索しただけでも以下リポジトリが見つかりました。
- Neo-cowsay https://github.com/Code-Hex/Neo-cowsay
- cowsay https://github.com/piuccio/cowsay
- ponysay https://github.com/erkin/ponysay
- pokemonsay https://github.com/possatti/pokemonsay
- parrotsay https://github.com/matheuss/parrotsay
今回は、公開リポジトリを触ってみるだけになってしまいましたが、来年は私もコマンドラインで遊べる何かを作って公開してみたいと思いました。
- 投稿日:2019-12-13T17:10:09+09:00
Windows Server + IIS + PHP + OracleDB 設定したこと
はじめに
タイトルの環境を用意するために行ったこと、
参考にしたサイトを、メモしておきます。Oracle Instant Clientが出てくるところまでは、スムーズに進みました。
Windows Server 初期設定編
日本語化
https://qiita.com/komacchi_u/items/66b908aa32ffdb9ef389
タイムゾーンの設定
https://support.microsoft.com/ja-jp/help/4026213/windows-how-to-set-your-time-and-time-zone
メニューバーが文字化けする問題の対策
IISとPHP編
IISとPHPのインストール(CGIも一緒に)
LaravelやNode.jsについては、今回は使用しないため、対応していない。
https://ottan.xyz/windows-server-iis-php-laravel-6739/registのインストール
https://support.microsoft.com/ja-jp/help/2977003/the-latest-supported-visual-c-downloads
OracleDBを扱うための準備
php.iniの編集
以下の箇所のコメントアウトを外す;extension=pdo_oci ;extension=mbstringデバッグのために、エラーの表示設定変更
https://serverfault.com/questions/19561/how-can-i-display-and-log-php-errors-on-iis7
https://www.remember-the-time.xyz/2016/02/iis-on-php-500-error.htmlOracle Instant Clientを導入
ここが、一番ハマったポイントでした
Appディレクトリの中に格納
http://everything-you-do-is-practice.blogspot.com/2017/11/oracle-instant-client-windows.htmlいくつかのファイルをPHPディレクトリに格納
https://qiita.com/nanasess/items/efa7c027838509a44586サーバー再起動
IISマネージャーを選択し、対象のサーバーを選択。
再起動ボタンを選択し、再起動することで、php.iniなどの設定が反映される。おわりに
普段はLinuxばかり触っていたので、
Windows Serverの設定は慣れてなく、探し方が悪いのか文献もあまり出てこなかったので、結構時間がかかってしまいました。GUIでポチポチするだけで、構築できるのはよいですね
- 投稿日:2019-12-13T16:58:27+09:00
PHPの生みの親、ラスマス氏のインタビューの紹介
空いてたので・・・。
私の好きなインタビューです。
PHPの生みの親,ラスマス・ラードフ氏インタビュー
https://gihyo.jp/news/report/2015/12/1401PHPをやって、なぜ、implodeとexplodeの引数の順番が違うのか、
array_mapとarray_filterの引数の順番が違うのか、
<?phpはなんで閉じちゃいけないんだとか、
この統一性のない言語に疑問を持った人も、このインタビューを読めば、
そんなことはどうでもいいと思えます。私はもっとよいハンマーを使いたいですが、
でもすごく共感できる考え方です。もう一つ、好きなインタビューです。
Linuxの背後にある精神
http://www.aoky.net/articles/linus_torvalds/the_mind_behind_linux.htm何が面白いかといえば、
日頃お世話になっているLinuxの世界本部が見られることです。
でもセンスのいいプログラムはかけません・・・ただのインタビューの紹介でした。
- 投稿日:2019-12-13T15:52:06+09:00
Azure Key Vault を利用した .env 内の機密情報の管理
.env と機密情報
Laravel を利用したプロジェクトは通常
.env
を使って環境変数を管理しています。.env
には各種認証情報などを含めることもあると思いますが、それらは Git などのバージョン管理システムに平文で保存するべきではありません。
かと言って、どこにも管理されておらず稼動している環境に置いてあるだけの状態というのも心許無さがあります。そこで Azure Key Vault (和名:キー コンテナー)のシークレットと、Go で書いた簡素な vaultenv というツールで、
.env
に直接機密情報を記述することなく管理できるようにしました。以下では Azure VM を利用していることを前提に、例を紹介します。Azure Key Vault の準備
- まずは Key Vault を作成します。(ここでは Azure CLI を使った例で説明します)
az keyvault create --location japaneast --name <YourKeyVaultName> --resource-group <YourResourceGroupName>
- 開発者のグループやユーザーに
セット
(set)と一覧取得
(list)の権限を付与します。格納されているデータは取得できないようにします。az keyvault set-policy --resource-group <YourResourceGroupName> --name <YourKeyVaultName> --secret-permissions set list --object-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- シークレットを復元したいデプロイ用途などの VM に対して、
取得
の権限を付与します。az vm identity assign --name <NameOfYourVirtualMachine> --resource-group <YourResourceGroupName> { "systemAssignedIdentity": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "userAssignedIdentities": {} }az keyvault set-policy --name <YourKeyVaultName> --object-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --secret-permissions getKey Vault へ機密情報の格納
- 機密情報を Key Vault のシークレットに保存します。
$ az keyvault secret set --vault-name <YourKeyVaultName> -n example-password --value "naisho" { "attributes": { "created": "2019-12-13T03:41:15+00:00", "enabled": true, "expires": null, "notBefore": null, "recoveryLevel": "Purgeable", "updated": "2019-12-13T03:41:15+00:00" }, "contentType": null, "id": "https://<YourKeyVaultName>.vault.azure.net/secrets/example-password/97a8cfac350c4b67b1f3510b1598cdce", "kid": null, "managed": null, "tags": { "file-encoding": "utf-8" }, "value": "naisho" }
- 登録時に出力された id を
{{ kv < id > }}
の形式で任意のテキストファイルに埋め込みます。example-password 以下(/97a8c...
) も含めるとバージョンを固定することができます。含めない場合は最新の値を取得します。.env.templateUSER=user1 PASSWORD={{ kv "https://<YourKeyVaultName>.vault.azure.net/secrets/example-password" }}.env への展開
- シークレットの取得を許可した VM 上で行います。上記のファイルを vaultenv を通すことにより
{{ kv < id > }}
で記述された部分が Key Vault に保存したデータに置換されます。実際にはデプロイ時に自動実行されるスクリプト内で行っています。$ go get github.com/sensyn-robotics/vaultenv $ vaultenv < .env.template > .env $ cat .env USER=user1 PASSWORD=naishoまとめ
Key Vault のアクセスポリシーを設定するすることにより、開発者自身のアカウントでは保存されたデータを参照せずに、登録のみが行えるようにすることができます。さらに、データの取得権限を特定の VM に限定することで、(VM に対するアクセスコントロールを適切に行えていれば)機密情報が漏洩してしまうリスクを低減できます。
参考リンク
- 投稿日:2019-12-13T14:57:50+09:00
OpenStreatMapの環境を自前で整える
どうものらぬこです。
ちょっとやりたいことがあったので、個人所有のRapberryPi4に、OpenStreatMapの地図データ + PostgreSQL(with postGIS) + mapnik な環境と、データをいい感じに処理するためのフロントプログラミング環境として PHP + php-mapnik を導入しました。
OpenStreatMapサーバーを自前で用意するための記事は探せば結構出てくるのですが、情報が古かったり、環境の違いなどによりうまく動かなかったりと、オープンソースをいろいろ組み合わせて環境準備する系のあるあるな罠盛りだくさんで、意図通りに動作させるまでにはかなりの苦労を強いられました。
環境構築に利用した環境は以下の通りです。
Raspberry pi 4(4Gb) + Raspbian Buster with desktop(2019-09-26)
(最初にRaspberry pi 3B+でチャレンジしたのですがpostgreSQLにデータをインポートする処理がメモリー不足が原因で完了せず、失敗に終わりました)
ストレージ microSD 128Gb(環境構築には45Gbほどの容量を使用するため、最低でも64Gbは必要です)RaspbianはDebian系Linuxのため、DebianやUbuntu等であれば、x86, x64系PC、AWS上のEC2インスタンス環境などでも、パッケージのバージョンなどで多少の差異は出てくるかもしれませんが、大体同等の手順で環境が建てられるかと思います。
この記事では、OpenStreatMap地図データ(日本)をPostgresql+postGISな環境にインポートし、日本国内の指定された範囲の地図をmapnikを使ってpng出力できるところまでをやります。
mod_tileを使ってタイルサーバに仕立て上げ、leafletjsを利用してgoogle mapのような地図アプリを構築するというようなお話は扱いませんのでご了承ください。
これ以降は、RaspberryPiにRasbianが導入されている前提で話を進めます。
まずは作業用ユーザを作ります。導入中、頻繁にroot権限を利用するため sudoグループに追加しておきます。
# adduser osm ユーザ `osm' を追加しています... 新しいグループ `osm' (1001) を追加しています... 新しいユーザ `osm' (1001) をグループ `osm' として追加しています... ホームディレクトリ `/home/osm' を作成しています... `/etc/skel' からファイルをコピーしています... 新しいパスワード: 新しいパスワードを再入力してください: passwd: パスワードは正しく更新されました osm のユーザ情報を変更中 新しい値を入力してください。標準設定値を使うならリターンを押してください フルネーム []: 部屋番号 []: 職場電話番号 []: 自宅電話番号 []: その他 []: 以上で正しいですか? [Y/n] Y # usermod -G sudo osmpostgreSQL + postGIS を導入します。
apt でインストールした場合、postgreSQLは11が、postGISは2.5系がインストールされます# apt install postgresql postgisOpenStreatMapのデータを格納するためのDATABASEを作成し、postGIS拡張を有効化します。
postgreSQLのユーザは、作業用ユーザアカウントと合わせてください。# su postgres $ createuser osm $ createdb -E UTF8 -O osm gis $ psql -c "CREATE EXTENSION hstore;" -d gis $ psql -c "CREATE EXTENSION postgis;" -d gis $ exitOpenStreatMapの地図データをダウンロードし、それをpostgreSQLのデータベースに格納します。
japan-latest.osm.pbf は日本の地図データで、ファイル形式はxmlです。この記事を書いている時点では13Gbあります。
かなり昔の記事ですが9Gbと書かれていた記事もあったため、情報が充実するに従い、ファイルサイズも増加しているのだと思われます。openstreetmap-cartoは、地図をレンダリングするために使用するスタイルシート的な情報です。
道路の色や線路の見た目、地図記号のアイコン情報などが格納されています。
また、地図データの取得方法もこの中で定義されていて、postgreSQLの接続設定情報、データを取得するためのSQL文なども含まれています。osm2pgsqlというのが、OpenStreatMapのxmlデータをpostgreSQLにインポートするためのプログラムです。
# su - osm $ wget -c http://download.geofabrik.de/asia/japan-latest.osm.pbf $ wget https://github.com/gravitystorm/openstreetmap-carto/archive/v4.24.1.tar.gz $ sudo apt install osm2pgsql $ osm2pgsql --slim -d gis -C 1600 --hstore -S openstreetmap-carto-4.24.1/openstreetmap-carto.style japan-latest.osm.pbfなお、openstreetmap-cartoのプロジェクトは、また後程使用します。削除せずそのまま残しておいてください。
次に、mapnikのインストールします。
mapnikはaptでもインストールできるのですが、日本語fontの設定がうまくいかなかったため、こちらもソースからビルドしています。
また、mapnikが依存するfreetypeもソースからビルドしています。aptでインストールされるバージョンにはビルドに必要な ft-config というプログラム(?)が含まれていないためです。$ sudo apt install libpq-dev libboost-dev libboost-regex-dev libboost-filesystem-dev libboost-system-dev libboost-program-options-dev libharfbuzz-dev libbz2-dev $ wget https://sourceforge.net/projects/freetype/files/freetype2/2.10.1/freetype-2.10.1.tar.gz $ tar xvzf freetype-2.10.1.tar.gz $ cd freetype-2.10.1/ $ ./configure $ make $ sudo make install $ git clone https://github.com/mapnik/mapnik.git $ cd mapnik $ git checkout v3.0.22 $ git submodule update --init $ ./configure FREETYPE_LIBS=/usr/local/lib FREETYPE_INCLUDES=/usr/local/include/freetype2 $ make $ sudo make installさて、mapnikはライブラリモジュールのため、mapnikの機能を利用して地図のレンダリングを行うには、何らかの言語でプログラムを書く必要があります。
ネイティブのC/C++のほか、Ruby/Java/Ptython/PHP等の各言語に対応したbindingも存在します。
僕は、使い慣れているPHPのbindingを選択しましたが、ご自分の使いたい言語向けに提供されているbindingを選択なさるのが良いかと思います。以下は、phpのmapnikバインディングとして提供されているphp-mapnikの導入方法となります。
何はともあれ、まずはPHPをインストールします。
拡張モジュールのビルドも行うため、php-devもインストールします。# apt install php php-dev次に、php-mapnikをインストールします。
$ git clone https://github.com/garrettrayj/php7-mapnik.git $ cd php7-mapnik $ git checkout 2.0.0 $ phpize $ ./configure $ make $ make install実際に地図をレンダリングするには、地図データの格納場所の設定(postgresqlサーバの設定)、地図の見た目(道路の色、線路の色、地図記号、地名などの表示用フォントなどなど)の設定が必要になります。
この情報を保持しているのが、osm2pgsql を実行する際にダウンロードした、openstreatmap-cartoというプロジェクトです。地図内の地名などの表記もmapnikライブラリの機能で、画像としてレンダリングされます。
- 日本語フォントの設定
- 設定情報のビルド(openstreatmap-carto で定義されたデータから mapnik の設定データ(.xml)を生成する)。
- 大陸・島々の海岸線データの生成
順にやっていきます。
以下、openstreatmap-carto ディレクトリ内で作業を行います。
$ cd openstreetmap-carto-4.24.1レンダリングに使用するフォントの種類は、
fonts.mss
というファイルで定義されています。
日本語フォントの設定も記載されており、日本語のレンダリングにはNotoフォントというgoogleが作ったフォントを使用する設定となっています。ただ、日本語フォント本体はパッケージには含まれておらず、別途ダウンロードする必要があります。Notoフォントのアーカイブは
https://www.google.com/get/noto/
からダウンロードできます。
対応しているすべての言語のフォントが含まれているため、アーカイブのファイルサイズも大きめ(1.1Gb)です。ファイルをダウンロードし、所定の場所(
/usr/local/lib/mapnik/fonts/
)に解凍すれば、地図レンダリング時に日本語が正しく表示されるようになる、、、はず、なのですが、提供されているフォント形式が.otf形式のためか、自分の環境ではうまくいきませんでした。無償で提供されているTTF形式の日本語フォントのひとつ、みかちゃんフォントを使用することにしました(
http://www001.upp.so-net.ne.jp/mikachan/
)。サイトでは、ttf形式、ttc形式、otf形式の3種類を配布していますが、ttf形式をダウンロードしてください(だうんろーど→Windows用→ひとつづつ欲しい方はこちら から入手)。
なお、ダウンロードした.ttfファイルは、 /usr/local/lib/mapnik/fonts/ 以下に置いてください。そして、
fonts.mss
を以下の内容で置き換えます。@book-fonts: "mikachan-P Regular"; @bold-fonts: "mikachan-PB Regular", @oblique-fonts: @book-fonts;次に、地図のレンダリング設定を、mapnikの設定ファイル形式(.xml)に変換します。
$ sudo apt install npm # sudo npm install -g carto $ carto project.mml > mapnik.xmlcarto 実行時にWarningがたくさん出力されますがスルーしておきます。
最後に、大陸・島々の海岸線データを準備しておきます。
openstreatmap-cartoプロジェクトディレクトリ内にスクリプトが用意されているので、これを呼び出すだけです。
$ python scripts/get_shapefile.py以上で、すべて完了です。たとえば以下のようなスクリプトを動かせば、引数で指定された緯度、経度を中心とした 10km四方の地図がレンダリングされ、
map.png
という名前で保存されます。rendermap.php<?php ini_set('display_errors', 0); $distance = 10; $sizeImage = 500; $lat = $argv[1]; $lng = $argv[2]; $latInterval = 360 / (6357 * 2 * 3.1416) * $distance; $lngInterval = 360 / (6378 * cos(deg2rad($lat)) * 2 * 3.1416) * $distance; $topLat = $lat - $latInterval / 2; $bottomLat = $lat + $latInterval / 2; $leftLng = $lng - $lngInterval / 2; $rightLng = $lng + $lngInterval / 2; $source = new \Mapnik\Projection('+init=epsg:4326'); $destination = new \Mapnik\Projection('+init=epsg:3857'); $transform = new \Mapnik\ProjTransform($source, $destination); $boundingBox = new \Mapnik\Box2D( $leftLng, $topLat, $rightLng, $bottomLat); $tileBoundingBox = $transform->forward($boundingBox); $pluginConfigOutput = []; exec('mapnik-config --input-plugins', $pluginConfigOutput); \Mapnik\DatasourceCache::registerDatasources($pluginConfigOutput[0]); $map = new \Mapnik\Map($sizeImage, $sizeImage, '+init=epsg:3857'); $fontConfigOutput = []; exec('mapnik-config --fonts', $fontConfigOutput); $map->registerFonts($fontConfigOutput[0]); $exampleXmlPath = realpath(dirname(__FILE__)) . '/mapnik.xml'; $basePath = realpath(dirname(__FILE__)); $map->loadXmlFile($exampleXmlPath, false, $basePath); $map->zoomToBox($tileBoundingBox); $image = new \Mapnik\Image(234, 234); $renderer = new \Mapnik\AggRenderer($map, $image); $renderer->apply(); $renderedImage = $image->saveToString('png'); file_put_contents('map.png', $renderedImage);$ php rendermap.php 36.0 134.0環境構築にかかった時間
環境構築に要した時間は、諸々の調査、試行錯誤の時間を含めて3日ほど(土日含む)です。
待ち時間もそれなりで、postgreSQLへのデータインポートに一晩、mapnikライブラリのビルドに1時間ほどかかっています。最初、raspberry pi 3B で同じことをやろうとしていたのですが、メモリー1Gbでは足りなかったようで、postgreSQLへのインポートが正常終了せず、失敗に終わっています。
何に使ったの?
一人で泊まれる宿検索|ソロ旅ねっと というサービスを個人で運営しているのですが、登録されている約22000件の宿それぞれの周辺広域地図を静的ファイルとして用意したかった、という目的で環境を作りました。
検索結果|ソロ旅ねっとOpenStreatMapの公式サイトからも、表示範囲を指定して地図画像をレンダリング&ダウンロードすることはできるのですが、レンダリングにはそれなりのCPUリソースが必要で、利用規約にも、一度にたくさんの画像を取得した場合ブロックするかも、機械的な収集なんてもってのほか!という記載があり、自分の好きに使えるように、自前で環境構築をしてみることにしました。
実際、縦横234ピクセルの300km四方の広域地図22000枚をレンダリングするのに、raspberry pi 4で4日以上はかかっています。
- 投稿日:2019-12-13T12:42:59+09:00
SlimのDI-ContainerでRay.Diを使ってみる
この記事はPHP Advent Calendar 2019の13日目の記事です。
昨日はpolidogさんのhelicon/object-mapperを作ったでした。
型情報と連想配列のマッピングまでできるというのはアツいですね。僕もライブラリ公開していきたい。さて本日はWeb Application FrameworkのSlimとDI ContainerのRay.Diを連携させる方法をご紹介します。
背景
僕はレガシーなフレームワークを使っている環境に関わっているのですが、
いい加減このフレームワークへの依存を切り離し、コードを減らしていきたいと常々思っています。ある日、色々検討する過程で、「別のWAFでリクエストをハンドル・プロキシして古いコード動かせばよくね?」ということを思いつきました。
色々ある世のフレームワークの中で、PSRに準拠しており、学習コストが低い(コードすぐ読める)Slimを実験で使うことにしました。また、該当のプロジェクトではコードの複雑な依存性を解消するために、DIコンテナとしてRay.Diを用いています。
極論を言ってしまえば、SlimでRay.Diを使えれば大体もうなんでもできるんじゃないか、ということですね。Slimとは?
SlimはPHPのマイクロフレームワークです。
PSRに準拠した、ルーティングとDIを提供する小さなライブラリなので、
フルスタックフレームワークのネットワーク系の処理の差し替えに向いていると思い検討に入れました。また、コード量が少なく、PSRに準拠した機構なので内容を把握しやすいです。
これらはフレームワークのレールから外れた対応を行うので、コードを読んで中身を把握しやすいという観点も重要です。Ray.Diとは?
Ray.DiはPHPのDIコンテナです。
RESTFulアプリケーションのフレームワークであるBEAR.Sundayでも利用されています。(絶賛勉強中)JavaのGuiceを参考に作られており、細かな挙動調整ができて非常に便利です。
アノテーションによるInjectionの制御やBindingの設定などの柔軟性が高く、既存の処理との兼ね合いを考慮した設定も可能なので、レガシーコードの改善で役立つシーンは多いと感じています。なにより、フレームワークライブラリが提供するDIではないため、今回のようなフレームワーク移管に関する取り回しがとても効きます。
SlimとDI-Container
Slimは独自でDI-Conatinerの仕組みを持っていますが、これもPSRに準拠しているため差し替えが用意です。
参考: Dependency ContainerSlimでDIを行う場合は以下のようにします。
<?php use DI\Container; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Factory\AppFactory; require __DIR__ . '/../vendor/autoload.php'; // Create Container using PHP-DI $container = new Container(); // Set container to create App with on AppFactory AppFactory::setContainer($container); $app = AppFactory::create(); // Add a service $container->set('myService', function () { return new \MyService(); }); // Get a myService's instance $app->get('/foo', function (Request $request, Response $response, $args) { $myService = $this->get('myService'); // ... return $response; });このとき、DIはPSR-11で定義されているContainerInterfaceを期待しています。
なので、Ray.DiをContainerInterfaceでラップしたクラスを定義してやれば違和感なく利用できます。Ray.Diのお手軽使い方
Redisを使った簡単データストア例です。昨今ならFirebaseとか使えよ感。
まずはInterfaceを定義。
src/Modules/Store/StoreInterface.php<?php namespace Example\Modules\Store; interface StoreInterface { public function get(string $id); public function set(string $id, $value); }本体を実装していきます。
src/Modules/Store/Store.php<?php namespace Example\Modules\Store; use Predis\Client; use Ray\Di\Di\Named; final class Store implements StoreInterface { private $client; /** * @Named("scheme=redis_scheme,host=redis_host,port=redis_port") */ public function __construct(string $scheme, string $host, int $port) { $this->client = new Client([ 'scheme' => $scheme, 'host' => $host, 'port' => $port, ]); } public function get(string $id) { return json_decode($this->client->get($id), true); } public function set(string $id, $value) { $this->client->set($id, json_encode($value)); } }モジュール定義します。
src/Modules/Store/StoreModule.php<?php namespace Example\Modules\Store; use Ray\Di\AbstractModule; use Ray\Di\Scope; final class StoreModule extends AbstractModule { protected function configure() { $this->bind(StoreInterface::class)->to(Store::class)->in(Scope::SINGLETON); $this->bind()->annotatedWith('redis_scheme')->toInstance($_ENV['REDIS_SCHEME']); $this->bind()->annotatedWith('redis_host')->toInstance($_ENV['REDIS_HOST']); $this->bind()->annotatedWith('redis_port')->toInstance($_ENV['REDIS_PORT']); } }呼び出し方。
<?php use Ray\Di\Injector; use Example\Modules\Store\StoreInterface; use Example\Modules\Store\StoreModule; $injector = new Injector(new StoreModule); $store = $injector->getInstance(StoreInterface::class);Slim with Ray.Di
実際にRay.DiをSlimと連携させていきます。
Containerのラッパーを作成
PSR-11のContainerInterfaceを実装します。
コンストラクタでAbstractModuleを受け取り、Injectorを作成します。
has
についてはRay.DiにそれっぽいI/Fが無く、UnboundのExceptionが発行されるようなのでとりあえずTry-Catchします。src/Container.php<?php namespace Example; use Psr\Container\ContainerInterface; use Ray\Di\AbstractModule; use Ray\Di\Injector; final class Container implements ContainerInterface { private $injector; public function __construct(AbstractModule $module) { $this->injector = new Injector($module); } /** * @inheritDoc */ public function get($id) { return $this->injector->getInstance($id); } /** * @inheritDoc */ public function has($id) { try { $this->get($id); return true; } catch (\Ray\Di\Exception\Unbound $e) { return false; } } }Ray.Diのモジュールを作成
モジュールをインストールするルートのModuleを定義します。
ここはBEAR.Sundayを参考にしました。ここでモジュールを一覧できるようになります。src/AppModule.php<?php namespace Example; use Example\Modules\Store\StoreModule; use Ray\Di\AbstractModule; final class AppModule extends AbstractModule { protected function configure() { $this->install(new StoreModule()); } }Slimアプリの作成
ContainerにAppModuleのインスタンスを渡して初期化したものを、AppFactoryに与えます。
これにより、Slimの処理からDI-Containerを呼び出せるようになります。index.php<?php use Example\AppModule; use Example\Container; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Factory\AppFactory; require __DIR__ . '/vendor/autoload.php'; $container = new Container(new AppModule()); AppFactory::setContainer($container); $app = AppFactory::create(); $app->get('/', function (Request $req, Response $res, array $args) { $res->getBody()->write("Hello, slim"); return $res; });DI-Containerと連携する
StoreInterfaceを元にモジュールをコールバックに注入する例です。
Controllerとか作ってコンストラクタでインジェクションすることもできます。index.phpuse Example\Modules\Store\StoreInterface; $app->get('/posts/{id}', function (Request $req, Response $res, array $args) { $store = $this->get(StoreInterface::class); $id = $args['id']; $row = $store->get($id); if (!$row) { throw new \Slim\Exception\HttpNotFoundException($req, "id($id) post notfound"); } $payload = json_encode($row); $res->getBody()->write($payload); return $res->withHeader('Content-Type', 'application/json'); }); $app->post('/posts', function (Request $req, Response $res) { $body = $req->getParsedBody(); $id = com_create_guid(); $store = $this->get(StoreInterface::class); $store->set($id, [ 'subject' => $body['subject'], 'content' => $body['content'], 'created_at' => (new \DateTime())->format(\DateTime::ATOM), ]); $payload = json_encode(['id' => $id]); $res->getBody()->write($payload); return $res->withHeader('Content-Type', 'application/json'); });まとめ
今回作成したコードはこちらにおいてあります。参考にどうぞ。
Containerを一段覆わないと行けない点はありますが、少ないコードで移植性の高い基盤が作れそうです。
Slimは簡単にかけるフレームワークだなと思ってたんですが、基礎モジュールがPSRのInterface依存なので、非常に拡張しやすいですね。(昨今のフレームワークはだいたいそうかな?)
Ray.Diも拡張性が高く、結合度の高いプロジェクトの中で部分的に疎結合な空間を作っていく際にとても役立ちます。
依存性が複雑 & 密結合なレガシーコードの改善の参考になれたら嬉しいです。
- 投稿日:2019-12-13T12:04:16+09:00
LaraDockを使って10分でLaravel+Nginx+MySql+Redisのローカル環境を構築してみる。
はじめに
こちらは TECOTEC Advent Calendar 2019 の13日目の記事です。
折り返しとなります。Qiitaの記事や他所のブログでも散々書かれていることかと思いますが、復習がてらLaraDockを使ってLaravelのローカル環境を構築してみようと思います。
目指せ10分でローカル環境構築!※当方macで作業しているため、macでの手順になります。Windowsの方は適時Windows版に読み換えてお試しください。
やること
- Dockerのインストール
- LaraDockのインストール
- Laravelプロジェクトの作成
- LaraDockの設定変更
- Laravelの設定変更
以上となります。
ではさっそくやってみましょう。
実演
Dockerのインストール
以下のサイトよりDocker for Macをダウンロードします。
https://docs.docker.com/docker-for-mac/install/
ダウンロードにはdocker hubへの登録が必要なので案内に従って登録してください。
Docker.dmg
をダウンロードできたら起動してインストールします。
インストール後はdocker.app
を起動しておいてください。LaraDockのインストール
GitHubからLaraDockのファイルを取得します。
ターミナルを起動して作業ディレクトリへ移動してください。ターミナルで作業$ cd ~/workcloneします。
ターミナルで作業$ git clone https://github.com/LaraDock/laradock.git取得できました。
laradock
ディレクトリが作成されています。
ワークスペースのコンテナを起動するために.env
ファイルを作ります。ターミナルで作業$ cd laradock $ cp env-example .envLaravelプロジェクトの作成
※開発が進んでいる場合はこの作業はスキップしてください。
※チーム等で管理しているGitからソースを取得してください。ワークスペースのコンテナを起動してログインします。
ターミナルで作業$ pwd ~/work/laradock $ docker-compose up -d workspace ~~~省略 Creating laradock_workspace_1 ... done $ docker-compose exec --user=laradock workspace bash laradock@0c7610d35e08:/var/www$ログイン完了です。
Laravelプロジェクトを作成します。workspace内で作業$ composer create-project laravel/laravel server
server
プロジェクトが作成されました。LaraDockの設定変更
ワークスペースからログアウトします。
workspace内で作業$ exit
vim
で.env
ファイルを開きます。(エディタならなんでもいいです)ターミナルで作業$ pwd ~/work/laradock $ vim .envプロジェクトのパスを変更します。
元の記述をコメントアウトして追記するか、直接編集してください。.env# APP_CODE_PATH_HOST=../ APP_CODE_PATH_HOST=../server/MySqlのデータを永続化させないために作業ディレクトリへ変更します。
※プロジェクトごとにデータを管理する想定です。共通で問題ない場合はこの作業はスキップしてください。。.env# DATA_PATH_HOST=~/.laradock/data DATA_PATH_HOST=.laradock/data/ポートを変更します。
※こちらは任意です。必要なければこの作業はスキップしてください。.env# NGINX_HOST_HTTP_PORT=80 NGINX_HOST_HTTP_PORT=8880MySqlのバージョンと接続情報を変更します。
最新の8.xは色々あれなので5.7を指定します。
※最新で問題ない場合はこの作業はスキップしてください。.env# MYSQL_VERSION=latest # MYSQL_DATABASE=default # MYSQL_USER=default MYSQL_VERSION=5.7 MYSQL_DATABASE=laradock MYSQL_USER=laradockMySqlとRedisのホスト名を設定しておきます。
ファイル内の一番下に追記してください。.envDB_HOST=mysql REDIS_HOST=redis設定を反映させるためコンテナを再起動します。
一緒にNginx・MySql・Redisを起動します。ターミナルで作業$ pwd ~/work/laradock $ docker-compose stop $ docker-compose up -d workspace nginx mysql redisLaravelの設定変更
ワークスペースのコンテナにログインして作業します。
ターミナルで作業$ pwd ~/work/laradock $ docker-compose exec --user=laradock workspace bash laradock@0c7610d35e08:/var/www$
.env
を編集します。workspace内で作業$ vim .envDBとRedis接続設定を書き換えます。
.envDB_HOST=mysql DB_DATABASE=laradock DB_USERNAME=laradock DB_PASSWORD=secret ~~ REDIS_HOST=redisパーミッションを変更します。
workspace内で作業$ chmod -R a+w storage $ chmod -R a+w bootstrap/cache依存ライブラリをインストールします。
workspace内で作業$ composer install接続確認!
http://localhost:8880/ にアクセスします。
Laravelのトップページが表示されれば完了です。
非常に簡単にLaravelの環境が構築できました。
ホストやDBの情報などは適時自分の使いやすいように変更してください。
NginxやMySqlの細かい設定はまた別の機会にかければ書きたいと思いいます。
というかそのままでローカルで使う分には全く問題ないのであまりいじったことがないです。おまけ
Laravelには便利な機能が沢山あります。
認証機能の実装
以下のコマンドを実行するだけで登録・ログイン・パスワードリセットなどが使用できるようになります。
workspace内で作業(ver5.xの場合)$ php artisan make:authworkspace内で作業(ver6.xの場合)$ composer require laravel/ui $ php artisan ui vue --authLaravelのバージョンで若干コマンドが異なるので注意が必要です。
表示を整えるためにnpm installとnpmを実行してcssとjsをコンパイルします。
workspace内で作業$ npm install && npm run devマイグレーションを実行します。
workspace内で作業$ php artisan migrate管理画面の実装
ついでに管理画面も作ってみます。
workspace内で作業$ composer require encore/laravel-admin以下のコマンドを実行します。
workspace内で作業$ php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider" $ php artisan admin:installhttp://localhost:8880/admin/ にアクセスします。
初期のIDパスワードは、ID:admin
パスワード:admin
となっています。そのままで使えることは少ないですが、上記で認証機能と管理画面が作れてしまいます。
起動シェルを作ってみる
PC起動後など起動コマンドが長かったりするのでシェルを作っておくと捗ります。
起動
up.sh#!/bin/bash cd ./laradock; docker-compose up -d workspace nginx mysql redisワークスペースへログイン
exec_workspace.sh#!/bin/bash cd ./laradock; docker-compose exec --user=laradock workspace bash停止
down.sh#!/bin/bash cd ./laradock; docker-compose downまとめ
LaraDockはローカル環境構築が非常に簡単です。
PHPでのメインフレームワークはしばらくLaravelが多くなりそうなので、LaraDockは扱えるようにしておくと色々便利そうです。
簡単なwebページ作るならあっという間です。今後も色々試して使いこなしていきたいと思います。
よいエンジニアライフを!
- 投稿日:2019-12-13T11:07:36+09:00
AmazonLinuxでPHPを5.3→5.6にバージョンアップする
概要
- PHP5.3を最新版ではなく5.6(もしくは何らかのバージョン)指定でアップデートしたい時の方法です
- PHP5.3はApache2.2系、PHP5.6はApache2.4系と依存関係があるので、一緒にアップデートする必要があります
- Apache再インストールの際にダウンタイムが発生するので、必要であればメンテナンス時間を設けましょう
流れ
- サーバーの状態を確認
- 現在のPHP,Apacheをアンインストール
- アップデート対象バージョンのPHP,Apacheをインストール
- (必要な場合)Apacheのhttpd.confを修正
- Apacheを起動
手順
OSの確認
対象サーバーにSSHでログインして以下コマンドを実行
# cat /etc/system-releasePHPバージョンの確認
# php -v PHP 5.3.29 (cli) (built: May 12 2015 22:42:19) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2014 Zend TechnologiesインストールされているPHPおよびライブラリのバージョン確認
# yum list installed | grep php php.x86_64 5.3.29-1.8.amzn1 @amzn-main php-cli.x86_64 5.3.29-1.8.amzn1 @amzn-main php-common.x86_64 5.3.29-1.8.amzn1 @amzn-main php-mbstring.x86_64 5.3.29-1.8.amzn1 @amzn-main php-mysql.x86_64 5.3.29-1.8.amzn1 @amzn-main php-pdo.x86_64 5.3.29-1.8.amzn1 @amzn-main php-xml.x86_64 5.3.29-1.8.amzn1 @amzn-mainインストールされているApacheのバージョン確認
# yum list installed | grep httpd httpd.x86_64 2.2.34-1.16.amzn1 @amzn-updates httpd-tools.x86_64 2.2.34-1.16.amzn1 @amzn-updatesインストール可能なPHPおよびライブラリのバージョン確認
# yum list available | grep php (〜中略〜) php56.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-cli.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-common.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-mbstring.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-pdo.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-xml.x86_64 5.6.40-1.143.amzn1 amzn-updates php56-mysqlnd.x86_64 5.6.40-1.143.amzn1 amzn-updates (〜以下略〜)php.iniのバックアップ
# cp /etc/php.ini /home/php.ini.yyyymmddhttpd.confのバックアップ
# cp /etc/httpd/conf/httpd.conf /home/httpd.conf.yyyymmddインストール済みPHPおよびライブラリの削除
# yum remove php # yum remove php-common # yum remove php-cli # yum remove httpd # yum remove httpd-toolsPHP5.6およびライブラリのインストール
※Apacheは依存関係で自動的にインストールされる
# yum install php56 php56-common php56-clihttpd.confの修正
バックアップしたhttpd.confを読み解き、必要な設定を加える
# vi /etc/httpd/conf/httpd.conf2.2系と2.4系でアクセス許可設定の記法が変わっているので注意
(参考:https://qiita.com/nwsoyogi/items/c8eb1fedef3c00c5fbac)2.2系<Directory "/home/xxx/htdocs"> Order allow,deny Allow from all </Directory>2.4系<Directory "/home/xxx/htdocs"> Require all granted </Directory>Apache起動
# service httpd start確認
PHPのサンプルコードやPHPアプリケーション(phpmyadminなど)で動作確認ができたらOKです
- 投稿日:2019-12-13T10:37:34+09:00
LaravelとSpringBootでDIコンテナを利用してみる
はじめに
これはユアマイスターAdventCalendar2019の13日目の記事です。
(社会人になってから学んだことをアウトプットする記事になります。)今回の経緯
社会人になってからDI(依存性の注入)という概念を知りました。
WEBサービスの開発を行う際に、フレームワークを利用する場面は多々ありましたが、主に利用していたCake PHP
ではDIという概念は出ていなかったと記憶しています。
(記憶違いだったらすみません。)DIは、インスタンスをnewで作成して利用するのではなく、DIコンテナやサービスコンテナ呼ばれるもの(SpringBootではDIコンテナ、Laravelではサービスコンテナと呼ばれます)を利用して、あらかじめ登録されたインスタンスを利用します。
今回は、業務で利用しているSpringBootでのDIコンテナの利用、最近独学で学んでいるLaravelでのサービスコンテナを利用してみるというテーマで記事を書いてみたいと思います。
SpringBootの場合
コンストラクターインジェクションを利用する
ユアマイスターアドベントカレンダー2019 の7日目の記事で書いたコードを用いて書いていきます。
SpringBootで動的にDBを切り替えてみる
https://github.com/Masaki-Ogawa/datasourceDemo
- PersonRepository.java
PersonRepository.javapackage com.example.dataSourceDemo.domain.repositories; import com.example.dataSourceDemo.domain.models.Person; import org.springframework.data.jpa.repository.JpaRepository; @Repository public interface PersonRepository extends JpaRepository<Person, Integer> { }JpaRepositoryを継承したRepositoryクラス
- PersonServiceImpl.java
PersonServiceImpl.javapackage com.example.dataSourceDemo.domain.services; import com.example.dataSourceDemo.annotations.DataSource; import com.example.dataSourceDemo.annotations.DataSource.DataSourceType; import com.example.dataSourceDemo.domain.models.Person; import com.example.dataSourceDemo.domain.repositories.PersonRepository; import java.util.List; import org.springframework.stereotype.Service; @Service public class PersonServiceImpl implements PersonService { private final PersonRepository personRepository; public PersonServiceImpl( PersonRepository personRepository) { this.personRepository = personRepository; } /** * stgのDBからPersonテーブルのレコードを取得するメソッド * @return Personテーブルのレコード */ @Override public List<Person> findAllPersonInStg() { return personRepository.findAll(); } /** * stgのDBからPersonテーブルのレコードを取得するメソッド * @return Personテーブルのレコード */ @DataSource(value = DataSourceType.PROD) @Override public List<Person> findAllPersonInProd() { return personRepository.findAll(); } }ここでDIを行っています。
具体的には、
private final PersonRepository personRepository; public PersonServiceImpl( PersonRepository personRepository) { this.personRepository = personRepository; }の部分でコンストラクターインジェクションによるDIを行っています。
@Service
や@Repository
というアノテーションを利用することにより、DIコンテナに登録されます。
利用するには上記のように、コンストラクターインジェクション等を利用して、インスタンスを作成します。参考
Spring Framework 要点まとめ ~ DIについてLaravelでのDI
サービスプロバイダーを利用する
作成したサービスをサービスコンテナ登録するためのサービスプロバイダーを作成します。
今回はDemoServiceというサービスクラスを作成しました。AppServiceProvider.phpclass AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { $this->app->bind('App\Services\DemoService'); } /** * Bootstrap any application services. * * @return void */ public function boot() { // } }上記のようにサービスを登録します。
DemoService.phpclass DemoService { public function show() { echo "何かを表示します"; } }サービスクラスは今回は適当ですが、何か文字を出力するメソッドのみ実装します。
DemoController.phpclass MessageController extends Controller { protected $demoService; public function __construct(DemoService $demoService) { $this->demoService = $demoService; } public function index(Request $request) { return $this->demoService->show(); } }このようにこちらもコンストラクターインジェクションを利用して、こちらもDIを行います。
終わりに
個人的には、
@Service
や@Controller
等のアノテーションで、DIコンテナに登録できるSpringBootの方が利用しやすいと感じています。
Java、SpringBootを利用してやはり、アノテーションの強力さに気づかされる場面が多々あります。また、7日目のアドベントカレンダーで書きましたが、それぞれのフレームワークに良さや悪さがあり、多数のフレームワークに触るという経験は、今後何かものを作るときに、「どんなものを採用すれば、そのプロダクトにとって一番良いのか?」という判断材料になると思います。
今後も、業務、業務外を含めて触れていきたいと思います。
- 投稿日:2019-12-13T09:28:56+09:00
phpMyAdminが、AWS EC2複数台構成で接続できなかった件
同じ構成のEC2インスタンスを2つ用意して、ALBで振り分ける構成の案件がありました。
それぞれにphpMyAdminが入っており、接続先は同一のRDSです。このphpMyAdminにログインしようとすると、エラーが表示されログインできない現象が起きました。
結論を先に書くと、対象のターゲットグループ設定の、維持設定を有効化にせいというアドバイスをもらい、解消しました。調べてみたら、振り分けられてセッションが上手く処理できないから、ユーザー(ブラウザ)毎に割り振りを固定するってことみたいですね。
スティッキーセッションはAWS専門用語では無く、ロードバランサー持つ一般的な機能の名称です。
セッション、インフラの知識を深めなくては
- 投稿日:2019-12-13T09:13:16+09:00
【Laravel】初心者から実務をこなしていくまでの6ヶ月にやったこと
CODEBASE2期生、新卒1年目の @avocadoneko です。
仕事では主に Laravel / Vue.js を使って、開発をしています。この記事について
どんな人に読んでほしいか
- プログラミングを学び始めたばかりの人
- これから PHP, Laravel を学びたいと思っている人
- Laravel で開発している会社に入社する予定の人
書いてあること
- 初心者が PHP と Laravel をどうやって勉強してきたか
- 仕事で学んだ Laravel のこと
書いてないこと
- PHP、Laravel 以外のこと(Git とか Web のこととか他の言語とか)
注意
- いくつか紹介している記事や動画では、Laravel のバージョンが古いことがあります。
やったこと
まずはPHPを触ってみる
■ Progate
ゲーム感覚でプログラミングを学べる教材。
有料版は月額980円。
超初心者が PHP はどんな言語なのか知るのの導入に役立つと思う。実際、プログラミングが何もわからない状態でもサクサクと進めることができた。
但し、Progate だけやっていても仕方がないので、基礎を学んだ後はやらなくてもいいと思う。■ ドットインストール
月額1,080円(無料でも多くの動画を視聴できる)で手軽に学べる動画教材。
ローカル環境構築の方法も動画になっているので、動画を見るだけではなく、自分のPCで再現して学ぶのがおすすめ。
手を動かして学ぶのが好きな人には向いていると思う。
Laravel の動画に入る前に、PHPで小さいアプリケーションを作る動画をやるのが良さそう。以下、良いなと思った動画↓
書籍
■ 詳細! PHP 7+MySQL 入門ノート
この本は最初から読んでいって、知らなかったことだけ自分のPCで実行してみると良い。
また、 一番読んでほしいところは Chapter7 の「オブジェクト指向プログラミング」の章。
クラスの定義方法、継承、コンストラクタなど知っておくべき基本的なことについてについて、実際のコードと共に説明されている。■ PHPフレームワーク Laravel入門
Laravel 入門書として一番おすすめの本がこちら。
レビューにもある通り、かなり回りくどく書かれているからこそ、入門書として人気が高い。
この本の使い方は、とりあえず写経。
書いてあることは全部必要な知識だから、隅から隅まで読むべき。
写経は GitHub でリポジトリを作って動く単位でコミットしていき、コミットメッセージに気づいたことを書くといいと思う。
写経のやり方については下記の記事にわかりやすく書いてある。
技術書の写経を始めたのでやり方を書いておく完全に理解しようとして行き詰まるより、とにかく読み切ることを目標にして、70~80%くらいの理解で最後のページまで写経するといいかもしれない。
また、丸々一冊読み切った本は、後に振り返るのにも便利。記事
■ 【PHP超入門】クラス~例外処理~PDOの基礎
$this
とか スコープ定義演算子とか、とにかく PHP の基礎が全くわかってなかった頃にお世話になった記事。
初心者がつまずきやすいところがピンポイントで書かれていて、何度も助けられた。チュートリアル
■ Laravel 5.5 入門として「基本のタスクリスト」を作成する
検索したら大量に出てくるチュートリアルの中でも、これは特に分かりやすかったなという印象。コントローラを使わずに
web.php
に処理を書いてタスクリストを実装しているので、使用するファイルが少なく、混乱せずにできた。
(実際開発するときはありえないけど、初心者には向いているチュートリアルだと思う)
さらなるステップとして、ここに機能を追加していくのもいい。私の場合は編集機能を追加した。■ Laravelで飲食店検索LINE Botを作ろう!
このチュートリアルは私はやったことがないが、もっと前からあったらやってみたかった。
有料だが、Docker 開発環境の準備から丁寧に説明しているチュートリアルは少ない上、LINEbot を作るまでできたらそれなりに達成感がありそう。Laravelでオリジナルのアプリを作る
DB 設計から機能、UI まで、一つのアプリを作ってみるのがおすすめ。
先に完成物の機能を具体的に決めてから実装したほうが良い。
自分が作りたいものを作るのが一番良い。
私の場合は下記のような機能を実装した。
- 新規登録 / ログイン機能
- イベントの作成 / 編集 / 削除
- イベントごとに写真を追加 / 削除
テーブル数は3-4つくらいが丁度よかった。
余裕があったら、API叩いて、画面遷移なしでイベントや写真を作成・削除できるようにしてもいいかも。
自分でアプリを作ることで、自分がわからない部分が明確になるし、完成させたら自信になる。業務に入ってから
仕事では独学と違って、0から何かを作ることは少ない。
初めて会社のプロダクトコードをみたときは、ファイルとコード量が多すぎてびっくりした。
業務ではチームで開発するし、人の入れ替わりがあるので保守性を意識する必要があるため、「動く」だけでなく「読める」コードを書く意識をしなければいけない。書籍
■ PHPフレームワーク Laravel実践開発
前に紹介した書籍「PHPフレームワーク Laravel入門」(以下、青本)の中級者向けバージョン。
青本を読み終わった人向け。
同じ著者なので、青本が読みやすくて気に入ったら、こちらも読んでみると良い。
レビューで内容が薄いとあるが、その分気軽に読めるので全体を流し読みするのには最適。足りない部分はググって補うといいかと思う。
また、これもレビューにある通り、誤字脱字は多いのでたまに自分のPCで動かしてみても動かないことがあるので注意。■ PHPフレームワーク Laravel Webアプリケーション開発
この本は、仕事でコードを書く上でのユースケースがたくさん書かれている。
どうやって実装したらいいか迷ってるときに、この本に手を伸ばすと、解決することがある。
全部読むというより、辞書代わりに手元に置いておくと良い。記事
■ Laravelで始める依存性の注入(DI)
DIってなんだ??となったときに読んで役に立った記事
■ Laravelで実践クリーンアーキテクチャ
Laravel をクリーンアーキテクチャに当てはめるなら、どういう感じになるのか?そもそもクリーンアーキテクチャって???となったときに読んで役立った記事。
わからなくなったら
■ Laravel 日本語公式ドキュメント
実装中にわからないことがあってググった時、まずは公式ドキュメントを読むのがいい。
私は最初、書いてある日本語が難しくて読めなかったから、ある程度 Laravel に慣れてきたらでもいいと思う。
バージョンの違いや正確性を考慮すると、公式ドキュメントを読むのがいちばん(当たり前のことだけど)。■ Laravel API
ドキュメントに書いてないメソッドなどもここで検索すれば出てくる。
最後に
いくつか宣伝をさせてください。
■ CODEBASE 沖縄 プログラミング教室
学生時代、エンジニアになるために通っていたスクールです。
3ヶ月間で、何もわからない状態からソフトウェアエンジニアとして新卒就職できるまで成長できました。■ 千株式会社
私が新卒で入社した会社です。
千株式会社 では幼稚園・保育園向けインターネット写真サービス「はいチーズ!」を提供しています。
新卒、中途共に絶賛採用中です!週1回のリモートワーク、フレックス制度(コアタイムが12:00-15:00)など、柔軟な働き方ができる会社です。(朝の6時に出社すれば、なんと15時に退勤できてしまいます...!)
モダン(Laravel + Docker + CircleCI + AWS)で自由な環境で働いてみたい方におすすめの会社です。
もっと詳しく知りたい方は こちら!アドベントカレンダー 千 Advent Calendar 2019もやっているのでぜひ覗いて行ってください〜
この記事は CODEBASE okinawa Advent Calendar 2019 13日目の記事です。
- 投稿日:2019-12-13T00:40:04+09:00
Laravel(5.8)のFormファザードサンプル
概要
見た目がスッキリするのでLaravelの拡張としてよく使うFormFacade、ただ書き方を忘れる事があるのでメモとして残しておく。
公式はこちら環境
- laravel:5.8.*
- laravelcollective/html: ^5.8
前提
- old()はLaravelのヘルパーであり、直前のフォームに入力した値を取得する働きをする。
- 第2引数は初期値
BootStrap4を使っているのでformで使うclassを指定している
セレクトボックス・ラジオボタン・チェックボックスで使う配列は下記のような形とする
$array = [ 1 => 'hoge', 2 => 'fuga', 3 => 'piyo', ];フォームの開始と終了
// 開始 {{ Form::open(['route' => ['user.update', 'user' => $user->id], 'method' => 'put']) }} // 終了 {{ Form::close() }}テキスト
一番使う基本的な形
emailとかpasswordとかはほぼ同じ形なので省略{{ Form::text('name', old('name', $user->name), ['class' => 'form-control']) }}セレクトボックス
第2引数に配列、第3引数に初期値を入力すればよい
{{ Form::select('sample_id', $array , old('sample_id', $user->sample_id) , ['class' => 'form-control']) }}ラジオボタン
第3引数についてはbooleanを設定する。
この場合は三項演算子の省略でtrueかfalseを返すようにしている。
注意点として配列のキーに0をもたせていると強制一致してしまう場合があるのでその場合は===を利用する。
ここで真偽値表を確認するとよい@foreach($array as $key => $val) {{ Form::radio('sample_radio', $key, ($key == old('sample_radio', $user->sample_radio)), ['id' => 'radio'.$key]) }} {{ Form::label('radio'.$key, $val) }} @endforeachチェックボックス
第3引数についてはbooleanを設定する。
この場合はin_arrayの戻り値を利用している。($keyが各配列に存在するかチェックしている)
in_arrayの比較が不安だという方はin_arrayの第3引数にtrueと書きましょう。@foreach($array as $key => $val) {{ Form::checkbox('sample_check[]', $key, in_array($key, old('sample_check', $user->sample_check)), ['id' => 'check'.$key]) }} {{ Form::label('check'.$key, $val) }} @endforeachテキストエリア
HTMLを出力したい場合があるのでサンプルでは「!!」でエスケープ処理を解除した例を書いておく
{!! Form::textarea('memo', old('memo', $user->memo), ['class' => 'form-control']) !!}最後に
自分用のメモですが、誰かのためになれば幸いです。
- 投稿日:2019-12-13T00:16:15+09:00
Javascriptでテーブルの特定列の表示・非表示を切り替える
実装したいもの
HTMLのテーブルで、チェックボックスを用いて、列ごとに値の表示・非表示を行いたい。
- 住所
- 年齢
氏名 住所 年齢 山田 東京 20 佐藤 神奈川 24 渡辺 大阪 18 たとえば、上記テーブルで「住所」の列だけを、非表示になるようにしたい。
- 住所
- 年齢
氏名 年齢 山田 20 佐藤 24 渡辺 18 こんな感じ。
コード
以下のようにコードを記述する。テーブルはPHPで出力していてもよい。
sample.html<body> <input type="checkbox" id="address_check" onclick="checkbox_cell(this,'address_display')" checked="checked">住所 <input type="checkbox" id="age_check" onclick="checkbox_cell(this,'age_display')" checked="checked">年齢 <table> <tr> <th>氏名</th> <th id="address_display">住所</th> <th id="age_display">年齢</th> </tr> <tr> <td>山田</td> <td>東京</td> <td>20</td> </tr> <tr> <td>佐藤</td> <td>神奈川</td> <td>24</td> </tr> <tr> <td>渡辺</td> <td>大阪</td> <td>18</td> </tr> </table> </body>sample.jswindow.onload = function(){ var array = ["address","age"]; for(var j=0;j<array.length;j++){ var id = array[j] + "_display"; var obj = array[j] + "_check"; var CELL = document.getElementById(id); var TABLE = CELL.parentNode.parentNode.parentNode; for(var i=0;TABLE.rows[i];i++) { TABLE.rows[i].cells[CELL.cellIndex].style.display = (document.getElementById(obj).checked) ? '' : 'none'; } } } function checkbox_cell( obj,id ){ var CELL = document.getElementById(id); var TABLE = CELL.parentNode.parentNode.parentNode; for(var i=0;TABLE.rows[i];i++) { TABLE.rows[i].cells[CELL.cellIndex].style.display = (obj.checked) ? '' : 'none'; } }注意点
- 先にJavascriptを読み込んでおく。
checked="checked"
は消しても動作する。必要に応じて使い分ける。参考サイト
- 投稿日:2019-12-13T00:09:18+09:00
PHPExcelでcellごとに加工する
はじめに
これは、いえらぶアドベントカレンダー用に投稿したものです。
- 文系エンジニア
- 入社1年目
とかいうプログラミング初心者が、特に下半期によくやったPHPExcelを使う案件で困ったことと見出した解決方法について書きます。
ググってもなかなか出てこなかった内容なので、同じ課題を抱えている人の参考になれば幸いですし、もしここ間違ってるよ、こうやったほうがいいよ、というものがあればコメントをいただけますと、めちゃくちゃ喜びます。案件の経緯
あるとき、とあるクラウドサービスがありました。
そのサービスには、契約書をExcelで出力できる機能がありました。
出力した契約書には、契約内容が自動で入力されていました。
あら、なんて便利なんでしょう。
しかし、クライアントは言いました。
「もっと使えるようにしてほしい」
※もちろん、盛ったうえでの意訳です。本題
要望が多岐にわたっていたので、いろいろやったんですが、
そのなかでもセルの加工に悩まされました。例えば、「このセルの色を変えたい!」となったとき、
「PHPExcel 色 変更」とかで調べます。そこででてきたコードがこちら
$sheet->getStyle( 'A1' )->getFill()->setFillType( PHPExcel_Style_Fill::FILL_SOLID )->getStartColor()->setARGB( 'FFFFCCCC');参考:https://elearn.jp/phpexcel/cell/setfilltype.html
ふむふむ、なるほど。
なんかシートを取得して、文字列でセルを指定して加工するんだね。
おっしゃ!!!やったろ!!!!
そして、確認した既存のソースコードがこちら。
// シートから行を取得してループ foreach ($sheet->getRowIterator() as $row) { // 行からセルを取得してループ foreach ($row->getCellIterator() as $cell) { // 取得したセルを加工する関数へジャンプ! $this->editCell($cell); } } /* ※関数名やコメントアウトは変えてます。 */・・・。
シートのまま加工するんじゃないんか!!!
さっきググったやつと違う!!!!
文字列でセルの指定なんかできなくないか!?!?
というわけで、他のサイトも探してみました。
しかし、探しても探しても
$sheet->
からそのまま加工するやり方ばかり、、、
$cell->
から始まる加工方法が全然でてきません。結論
いろいろ検索して、試してみて、トライアンドエラーを繰り返していたので、結論に至った経緯をはっきり覚えているわけではありませんが、、、
var_dump
を加工した社内の独自関数を使って、objectの中をみたり
トライアンドエラーのなかでも、「目的は果たせなかったけどここは使える気がする」みたいな学びをしつつ、少しづつPHPExcelの理解を深めました。そして出た結論。
PHPExcel_Cell
の中にあるメソッドで使えそうなものを探せばセルごとに加工できる。よく考えれば、
インスタンス化したクラス(オブジェクト)の中にあるメソッドを->で使えるよ
っていう基本のところを理解したうえで、
ライブラリの深部までじっくり読む
っていうことをすればどうってことなかったんですね。自分はまだ初心者だし、そんなところ見ても分からないっていう先入観から、避けてしまっていました。
ビビらずにやってみるというのができていればもっと早く解決できたかもしれません。
まあ、やりたかったことの中には、そこまでわかってもやり方が分からず、先輩にパスしてしまったものもあるんですが、、、
さいごに
最初は、きちんとcellごとに加工する方法まで書こうと思っていたのに、あんまり会社のソースコード公開するのもどうなんだろ~と思っていたら一般論みたいな結論になっていました。
釣りみたいなタイトルですみません。
釣ろうと思ったわけではないです。そんな感じで、私は社内でPHPExcelに詳しい人トップ8くらいには入れたのではないかと思います。(大きくでたのか小さくでたのかわからない)
これからも、このライブラリの使い方わかんね~~~~!!!!!ってなったときは、Google先生に頼りつつも自分でどうにかするようにしたいですね。