- 投稿日:2020-07-09T23:17:50+09:00
(Laravel)変更はマッピングに追加するだけのSingleton登録サービスプロバイダの実装
希望:複数クラスで依存されるクラスのSingleton化を楽に管理したい
複数のクラスで依存されるSingletonにしても問題がいないクラスだけを、ServiceProviderで同じインスタンスが取得されるよう実装しようと思いました。
数が多かったのでマッピングを編集すればSingletonに登録するクラスが追加できるように実装してみました。app/Providers/SingletonServiceClassRegisterProvider.php<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class SingletonServiceClassRegisterProvider extends ServiceProvider { protected function singletonTargetServiceClassNamesMap():array { return [ \App\Service\PlanService::class, ]; } /** * Register any application services. * * @return void */ public function register() { $this->registerAllClassInstanceInClassNamesMap($this->singletonTargetServiceClassNamesMap()); } protected function registerAllClassInstanceInClassNamesMap(array $classNamesMap):void { foreach($classNamesMap as $key=>$className){ $instance = $this->app->make($className); $this->app->instance( is_numeric($key) ? $className : $key, $instance ); } } }これで、マッピングにSignletonで登録したいクラスを追加、外したいクラスを削除することで管理できるサービスプロバイダになりました。
以上です。
- 投稿日:2020-07-09T19:33:55+09:00
【Laravel】Bladeの構文
書籍のアウトプットとして
Bladeの構文
レイアウト内でヒョ持したり、レイアウトを継承して複数を組み合わせたりするための構文について。
値の表示
{{値・変数・式・関数等}}{{}}はHTMLスケープ処理されるためHTMLタグとしては機能しない。
エスケープ処理してほしくない場合は以下のようにする。{{!! 値・変数・式・関数等 !!}}@ifディレクティブ
Bladeにはディレクティブという言語で言う構文が用意されている。
まずは条件分岐(if)に相当するディレクティブから。条件がtrueの時に表示する。
@if (条件) ...出力内容... @endif条件によって異なる表示をする
@if (条件) ...出力内容... @else ...出力内容... @endif複数の条件を設定
@if (条件) ...出力内容... @endif(条件) ...出力内容... @else ...出力内容... @endif(条件)ディレクティブは何かを実行するのではなく表示する。
条件がこれなら表示する、表示しないという動きをする。@ifを使ってみる
以下のように修正する
index.blade.php<body> <h1>Blade/Index</h1> @if ($msg!='') <p>こんにちは、{{$msg}}さん。</p> @else <p>なにか書いてください</p> @endif <form action="/hello" method="post"> @csrf <input type="text" name="msg"> <input type="submit"> </form> </body>次はコントローラを修正
public function index() { return view('hello.index', ['msg'=>'']); } public function post(Request $request) { return view('hello.index', ['msg'=>$request->msg]); }/helloにアクセスして正常に表示されているか確認する。
特殊なディレクティブ
条件が非成立のときに表示
@unless(条件) ...表示内容... @endunless条件がfalseの時に表示をする。
変数が空の場合に表示
@empty(変数) ...表示内容... @endempty変数が定義済みの場合に表示
@isset(変数) ...表示内容... @endissetemptyと似ているが、これは変数そのものが定義されているかどうかを判定する。
@issetで変数定義をチェックする
@issetを使って例を見てみる。
index.blade.php<body> <h1>Blade/Index</h1> @isset ($msg) <p>こんにちは、{{$msg}}さん。</p> @else <p>なにか書いてください</p> @endisset <form action="/hello" method="post"> @csrf <input type="text" name="msg"> <input type="submit"> </form> </body>コントローラはこのように
public function index() { return view('hello.index'); } public function post(Request $request) { return view('hello.index', ['msg'=>$request->msg]); }indexアクションメソッドでは値をテンプレに渡していない。
そのため、テンプレ側では$msgは未定義になる。繰り返しのディレクティブ
forに相当
@for(初期化;条件;後処理) ...繰り返す表示... #endforforeachに相当
@foreach($配列 as $変数) ...繰り返す表示... @endforeachforeach-elseに相当
@forelse($配列 as $変数) ...繰り返す表示... @empty ...$変数が空のときの表示... @endforelseforeachにelseを追加したもの相当する。
whileに相当
@while(条件) ...繰り返す処理... @endwhile繰り返しディレクティブを利用する
index.blade.php<body> <h1>Blade/Index</h1> <p>@foreachディレクティブの例</p> <ol> @foreach($data as $item) <li>{{$item}}</li> @endforeach </ol> </body>コントローラ
public function index() { $data=['one','two','three','four','five']; return view('hello.index', ['data'=>$data]); }/helloにアクセスするとリストが表示される。
viewメソッドの[data->\$data]でdataに\$dataを設定してテンプレートに渡している。$loopによるループ変数
$loopでループに関する情報が得られる。
プロパティ 説明 $loop->index 現在のインデックス(0から開始) $loop->iteration 現在の繰り返しかず(1から開始) $loop->remaining 後何回繰り返すか(残り回数) $loop->count 繰り返しで使っているあ配列の要素数 $loop->first 最初の繰り返しかどうか(最初ならtrue) $loop->last 最後の繰り返しかどうか(最後ならtrue) $loop->depth 繰り返しのネスト数 $loop->parent ネストしている場合、親送り返しのループ変数を示す。 例を見てみる。
index.blade.php<body> <h1>Blade/Index</h1> <p>@forディレクティブの例</p> @foreach($data as $item) @if($loop->first) <p>データ一覧</p><ul> @endif <li>No,{{$loop->iteration}}.{{$item}}</li> @if($loop->last) </ul><p>ーーーここまで</p> @endif @endforeach </body>これで確認できる。
phpディテクティブについて
phpディレクティブでPHPスクリプトを直接実行できる。
@php ...PHPスクリプト... @endphpindex.blade.php<body> <h1>Blade/Index</h1> <p>@phpディレクティブの例</p> <ol> @php $counter=0; @endphp @while($counter<count($data)) <li>{{$data[$counter]}}</li> @php $counter++; @endphp @endwhile </ol> </body>テンプレに用意する@phpは最小限に、が基本
- 投稿日:2020-07-09T16:50:55+09:00
laravel7 マルチ認証でログアウト後にログインできなくなる
現象
管理者画面のログアウト後に違うアカウントでログインするとログインできないで「/login」にリダイレクトされる。
ちなみに認証は「laravel/ui 」を利用しています。
原因
ログアウト時にセッションがクリアされていないみたい。
対応
logout時にセッションをクリアするように修正。
--- a/app/Http/Controllers/Admin/Auth/LoginController.php +++ b/app/Http/Controllers/Admin/Auth/LoginController.php @@ -7,6 +7,7 @@ use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Session; class LoginController extends Controller { @@ -53,6 +54,7 @@ class LoginController extends Controller public function logout(Request $request) { Auth::guard('admin')->logout(); + Session::flush(); return $this->loggedOut($request); }
- 投稿日:2020-07-09T11:47:33+09:00
tempusdominusのDatatimepickerのスタイルが崩れる
bootstrap4スタイル用のDatetimepickerで利用させてもらっている
tempusdominus版のdatetimepickerモジュールですが、
いつの間にかデザインがずれているのに気づく。原因はtempusdominus用のスタイル(css)を使用していなかったため
npm install --save tempusdominus-bootstrap-4laravelなら
resources/sass/app.scss
に以下を追加@import '~tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4-build';
npm run dev
してブラウザの再読み込みで直った。
- 投稿日:2020-07-09T05:23:03+09:00
Cannot end a section without first starting one. [Laravel]
Cannot end a section without first starting one.
bladeテンプレートの構文エラーと発生する原因について
発生する原因
シンタックスエラーの可能性:セクションが正しく囲われていないケース
master.blade.php@section 〜記述 @endsectionタイポ:共通レイアウトの呼び出し名が間違っているケース
master.blade.php// 呼び出した共通レイアウト名が正しいか確認する @extends('layouts.base')タイポ:構文が間違っているケース
master.blade.php// sectionがsecitonになっている @seciton('main') <p>{{ $msg }}</p> @endsection
- 投稿日:2020-07-09T02:31:17+09:00
LaravelのMailで、機能ごとに送信元アドレスを切り替えたい(Gmailなど)時にめっちゃ詰まった話
顧客「Aの機能の時は、hoge@gmail.comで確認メールを送信して欲しいんだよね」
ぼく「わかりました」
顧客「Bの機能の時は、hogehoge@gmail.comで確認メールを送信して欲しいんだよね」
ぼく「承知の助」
1時間後
ぼく「ん?むずくね?」
というわけで、詰まったお話
なんで詰まったのか
LaravelのMailってfromとかのfunctionがあって一見簡単に切り替えられるように見えるんですけど
こんな感じに変えられそうMail::from('hogehoge@gmail.com') ->to($request->user_mail) ->send(new Mail($request->user_name));まぁ当たり前なんですけど、ダメなわけで
そもそもGmailを使用するには、当然usernameとpasswordを使用しているわけで、それを.envに書いて読み込んでるわけですよね
.envMAIL_DRIVER=smtp MAIL_HOST=smtp.gmail.com MAIL_PORT=465 MAIL_USERNAME= hoge@gmail.com MAIL_PASSWORD=passpasspasspasspass MAIL_ENCRYPTION=ssl要はこの
MAIL_USERNAMEと
MAIL_PASSWORDはいつ読み込まれて、どうやって変更するんだ!!
って悩んだわけですよ。参考にさせてもらったのが↓
https://qiita.com/dublog/items/3314ca25a90e76f63b17
かなりわかりやすくプロセスのライフサイクルとサービスコンテナについて書いてくれていて助かりました。
かいつまんで説明すると
・ユーザーからリクエストがくる
・app.phpがMailServiceProviderをコンテナに格納する
・内部でTransportManagerがnewされる(singleton)
・createSmtpDriver()でusernameとpasswordがconfigから読み込まれる
・メールをsendする際に、読み込まれたusernameとpasswordなどからメールが送信されるという流れなわけですよ。
これsingletonとか諸々の影響で、後から書き換えられない(できるかもしれないけど)せいでまぁえらい時間がかかりました。
結局どうやったの?
こうやりました↓
controllerの内部extract(\Config::get('temp_mail')); $transport = (new \Swift_SmtpTransport($host, $port)) ->setUsername($username) ->setPassword($password) ->setEncryption($encryption); \Mail::setSwiftMailer(new \Swift_Mailer($transport)); \Mail::to($request->user_mail) ->send(new TempMail($request->user_name));config/temp_mail<?php return [ 'host' => 'smtp.gmail.com', 'port' => '465', 'username' => 'hogehoge@gmail.com', 'password' => 'passpasspasspass', 'encryption' => 'ssl', ];何をやっているの?
既存のSwiftMailerだと
transport(usernameとかpasswordとか持ってるあれ)
がcontrollerに処理が届く前に、configから読み込まれて上書きできないので
もう1から$transportを作って、 SwiftMailerもろともnewしちゃいましょうって解決法でした。これは
https://laravel.io/forum/07-22-2014-swiftmailer-with-dynamic-mail-configuration
にて参考にさせてもらいました。というかね
本当にlaravelのMailでさくっと出来ないんですかね・・・?
めちゃくちゃ頻発する案件だと思うんですが。。
何か他にさくっとできる方法があったら共有していただきたいです。
- 投稿日:2020-07-09T00:11:17+09:00
Dockerを使ったphp-fpm(+Laravel)とNginx環境の構築
背景
「LaravelってどうやってHTTPサーバと連携するんだ?」と思い、調査しましたが結構はまったので、自分用のメモも兼ねて記事を書きます。
やりたいこと
・Laravelを使ったWebアプリサーバを立てたい
・Dockerを使って、コマンド一発で楽にWebサーバとWebアプリサーバを立てたい特にDockerを使ってWebサーバとWebアプリサーバを立てるのは、Kubernetesを使うのに必須だったりするので、鍛錬もかねて、実施しました。
システムの全体図
システムというほどのものではないですが、下記のようにDockerの環境を構築しました。
・Nginxのコンテナを立てて、80番と443番でHTTP/HTTPSのリクエストを受け付ける
・NginxのコンテナとWebアプリのコンテナは9000番で通信
・Webアプリのコンテナには、外部から直接アクセスはできない負荷分散は今回は未実施です。
(勉強のためKubernetesやりたいので、取り組んだらまた投稿します。)今回もdocker-compose(コンテナをまとめて管理できるツール)を使いました。
Composeファイル
ディレクトリ構造は下記のようにしました。
project/ ├ nginx/ │ └ dockerfile │ └ server.conf ├ php-fpm/ │ └ dockerfile │ └ www.conf ├ web/ │ └ Laravelアプリ/ ├ docker-compose.ymldocker-compose.ymlは下記のようにしました。
docker-compose.ymlversion: '3' services: web: build: context: ./nginx ports: - "80:80" - "443:443" volumes: - ./web/public:/etc/nginx/public - ../ssl/certs/:/etc/pki/tls/certs/ - ../ssl/private/:/etc/pki/tls/private/ depends_on: - app container_name: blog_web app: build: ./php-fpm volumes: - ./web:/var/www/ container_name: blog_appwebがNginxコンテナで、appがphp-fpm+laravelのコンテナです。
Nginxコンテナは
・外部から、80番と443番を受け付けるようにし、appと通信できる
・publicディレクトリにおいてある、cssやjsを直接さわれるようにする(staticファイルはnginxから直接返す)
・sslの証明書と鍵は、アクセスの権限を調整して、マウントして見れるphp-fpm+Laravelコンテナは
・ソースをまるまるマウントするという感じです。
Nginxのコンテナ
下記のように書くだけです。
DockerfileFROM nginx:latest # 設定ファイルを指定の場所に置く COPY ./server.conf /etc/nginx/nginx.conf CMD ["nginx", "-g", "daemon off;"]server.confuser nginx; worker_processes auto; pid /var/run/nginx.pid; events{ worker_connections 2048; multi_accept on; use epoll; } http { charset UTF-8; # versionを表示しない server_tokens off; include /etc/nginx/mime.types; default_type text/plain; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ssl_protocols TLSv1.1 TLSv1.2; server { listen 80; server_name localhost; # 80番でアクセスしてきた人は、443番のリダイレクトを返す return 301 https://$host$request_uri; } server { listen 443; ssl on; server_name localhost; # 証明書 ssl_certificate /etc/pki/tls/certs/example.crt; # 秘密鍵 ssl_certificate_key /etc/pki/tls/private/example.key; # rootディレクトリ root /var/www/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; # indexファイルの指定 index index.php index.html; charset utf-8; # アクセスしてきたパスに対応するファイルを返す location / { try_files $uri $uri/ /index.php?$query_string; } # staticファイルはnginxが直接返す # cssファイル location /css/ { alias public/css/; } # jsファイル location /js/ { alias public/js/; } # imageファイル location /images/ { alias public/images/; } # fontsファイル location /fonts/ { alias public/fonts/; } # faviconリクエストはログを残さない location = /favicon.ico { access_log off; log_not_found off; } # php-fpmとの連携 location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } # 指定のパス以外へのアクセスを禁止する location ~ /\.(?!well-known).* { deny all; } # 証明書更新のパスへアクセスできるようにする location /.well-known/ { alias public/.well-known/; } # クローラがアクセスできるようにする location /robots.txt { alias public/robots.txt; } # サイトマップにアクセスできるようにする location /sitemap.xml { alias public/sitemap.xml; } } }まだわかっていない箇所もありますが、
Nginxのコンテナは、fastcgi_pass app:9000;とすることで、Webアプリのコンテナのpublicディレクトリのindex.phpをたたきにいけるようにしています。
私はここの記載の仕方でちょっとはまりました。。。cssファイルやjsファイルなどのstaticファイルは、Nginxコンテナがアプリのpublicディレクトリをマウントしていて、Nginxから直接返せるようにしています。
クローラが見に来れるようにrobots.txtとsitemap.xmlにアクセスできるようにしています。php-fpm+Laravelコンテナ
dockerfileに入る前に、そもそもphp-fpmとは何ぞ???というのが自分の中であったので、調べました。
公式ドキュメントとか見ましたが、一番わかりやすかったのは他者様の記事php-fpmとは
php向けのFastCGIで、メモリでキャッシュを保持することで高速にWebサーバ上でPHPを動作させるアプリケーション
ということです。
この他者様の記事ではコンテナ間の通信のことも書いてありましたが、今回Nginxとphp-fpmはTCPで通信しています。
次やる時はUNIXドメインソケットで通信しようかと思います。さて、Dockerfileですが、下記のようにしました。
DockerfileFROM php:7.4.7-fpm RUN apt update -y RUN apt install -y libfcgi0ldbl curl git unzip wget vim # nginxというユーザを作る RUN useradd -m -s /bin/sh -u 1000 nginx # 設定ファイルを指定の場所に置く COPY ./www.conf /usr/local/etc/php-fpm.d/zzz-www.conf # Composeインストール RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # nodejs 12インストール RUN apt install -y npm RUN npm install n -g RUN n 12 # ユーザ変更 USER nginx # 作業ディレクトリ WORKDIR /var/www VOLUME ["/var/run/php-fpm"]www.conf[www] user = nginx group = nginx listen = 9000 listen.owner = nginx listen.group = nginx listen.mode = 0660 request_terminate_timeout=30sphp-fpmの設定はwww.confの設定ファイルと公開されているDockerImageで設定は簡単にできました。
あとは、コンテナにComposerとnodejs12を入れたかったので、Dockerfile内でインストールするようにしました。
実際のnpmパッケージのインストールと、composerのビルドは、コンテナを立ち上げた後、コマンドで実行しています。docker exec blog_app composer install --optimize-autoloader --no-dev docker exec blog_app npm install docker exec blog_app npm run productionこのようにしたのは、マウント後にnpmパッケージのインストールやcomposerのビルドをしないと、ソースがない時にdockerビルドが走り、失敗してしまうからです。
dockerfile内で、ソースをとってくる処理を書いている場合は、dockerfile内でnpmパッケージのインストールやcomposerのビルドができます。Composeのビルド
あとは、コンテナのビルド+コンテナ起動して、npmパッケージのインストールやcomposerのビルドして終了です。
docker-compose build docker-compose up -dドメイン設定・SSL対応
ドメインはこちらで買いました。
DNSはAWSのものを使いたかったので、Route53を使用しました。
設定は下記です。
DNSの設定はこちらを参考しました。
最低限の設定は下記で、
・Aレコード:グローバルIPとホスト名の紐付け
・NSレコード:ドメインのDNSサーバ(ドメインを購入したサイトのネームサーバにこの値を登録します。)
・SOAレコード:上位のDNSサーバ
オプションで、
・CNAMEレコード:別ホスト名
・TXTレコード:今回はクローラの認証情報記載
を記載しています。証明書の取得は、お金をかけずにやりたかったので、
他者様の記事を参考にbacmeでオレオレ証書を取得しました。ホスト名とマシンの紐付けができていれば、やることは単純で、
bacmeをgit cloneしてきて、取得したホスト名で./bacme -w /var/www/example/ example.com www.example.comを実行します。wは、.well-knownディレクトリのパスです。
(80番でwell-knownのパスにアクセスできないと↑のコマンドは失敗します。アクセスできるようにNginxの設定を見直してみてください。)
成功すると、bacmeディレクトリ直下に証明書と秘密鍵ができますので、
Nginxのマウント先(/etc/pki/tls/certs/)に指定してください。参考文献
nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話
次は、最低限のセキュリティ設定とKubernetesのことを調べます。