- 投稿日:2020-11-26T23:37:45+09:00
【ajax】本番環境で謎の403エラー
環境
- Laravel 8系
- PHP 7.4
- appache
実装したいこと
ajaxを使用して非同期でPATCH処理を行いたい。
本題
$.ajax({ type: 'PATCH', url: '/hoge_update', headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, data: { id: id, item: item, }, dataType: 'json', }).done(function (data) {こんな感じで非同期でPATCH処理を実装しようと思ったら、本番環境で
403 Forbidden
エラーが。調べてみるとどうやらapache側で許可されているのが
GET
POST
のみみたい(詳細はこちら)ただssh接続の許可がなかったり本番環境に入れない場合、confファイルを直接編集できないので、
$.ajax({ type: 'POST', //POSTに変更 url: '/hoge_update', headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, data: { _method: 'PATCH', //PATCHを追加 id: id, item: item, }, dataType: 'json', }).done(function (data) {だいぶ無理やりですがこれでしっかり200ステータスを返してくれました。
参考
- 投稿日:2020-11-26T19:09:31+09:00
WordPressでサイドバーを自作する
ワードプレスのテーマtwentytwentyを基盤に、ページに表示するサイドバーを自作する実装を行ったので、備忘録的にこの記事を残す。
環境情報
PHP:version 7.3.12
WordPress:version 5.5.3
WPテーマ:twentytwenty作業
ワードプレスのいろいろな機能は、全て「テーマのファイル」の中でプログラム的に記述されていて(定義されていて)、これはサイドバーも同じです。
ということから、サイドバーを増やす、ホームならホーム専用のものを別に作る、といった場合には、以下3つの事を行えば良さそうです。
1)サイドバーの定義(register_sidebar)を探す
テーマのファイルの中で、サイドバーが定義されているところ(記述されているところ)を探す
2)サイドバーの定義を複製する
その「定義されているサイドバーの記述」をコピペして、もう1つサイドバーを定義する
3)複製したサイドバーを場合分けで呼び出す
サイドバーを表示するファイルの中で、ホームならホームの場合はこれ、と場合分けして、サイドバーを表示するサイドバーの定義を探す
テーマファイルの中で、サイドバーは、register_sidebar (または register_sidebars )で定義されてるので、その箇所を探します。
function.phpfunction twentyseventeen_widgets_init() { register_sidebar( array( 'name' => __( 'Blog Sidebar', 'twentyseventeen' ), 'id' => 'sidebar-1', 'description' => __( 'Add widgets here to appear in your sidebar on blog posts and archive pages.', 'twentyseventeen' ), 'before_widget' => '<section id="%1$s" class="widget %2$s">', 'after_widget' => '</section>', 'before_title' => '<h2 class="widget-title">', 'after_title' => '</h2>', ) ); register_sidebar( array( 'name' => __( 'Footer 1', 'twentyseventeen' ), 'id' => 'sidebar-2', 'description' => __( 'Add widgets here to appear in your footer.', 'twentyseventeen' ), 'before_widget' => '<section id="%1$s" class="widget %2$s">', 'after_widget' => '</section>', 'before_title' => '<h2 class="widget-title">', 'after_title' => '</h2>', ) ); register_sidebar( array( 'name' => __( 'Footer 2', 'twentyseventeen' ), 'id' => 'sidebar-3', 'description' => __( 'Add widgets here to appear in your footer.', 'twentyseventeen' ), 'before_widget' => '<section id="%1$s" class="widget %2$s">', 'after_widget' => '</section>', 'before_title' => '<h2 class="widget-title">', 'after_title' => '</h2>', ) ); } add_action( 'widgets_init', 'twentyseventeen_widgets_init' );twentyseventeenではfunction.phpの中で定義されています。
サイドバーの定義を複製する
では、サイドバーの定義が分かったので、それを使って新たにサイドバーを複製(追加)してみます。
複製する場合、サイドバーの定義に対して3点の変更が必要ですが、今回はまず、ホーム(トップページ)専用のサイドバーを作ってみます。
1)3つの変更点
専用のサイドバーをつくるにあたり、先ほどコピーして抜き出したサイドバーの定義の中で、name, id, description の3点を変更しておきます。
ただ description は、サイドバーの定義の中にある場合だけ変更する、でOK。
name について
「ホーム専用サイドバー」とかにすると分かりやすいですね。以下のような感じです。
'name' => __( 'ホーム専用サイドバー' ),id について
今回はまずホーム専用サイドバー、ということで、たとえば、my-top-sidebar としていきます。
※元々あるサイドバーと同じ名前にしない!'id' => 'my-top-sidebar',description について
テーマによって、サイドバーの定義に含まれないものもありますが、含まれている場合には、以下の様にしておけば分かりやすくて良さそうです。
'description' => 'ホーム専用サイドバーに表示されるコンテンツ',作ったサイドバーを貼り付け
つづいて、上で変更したサイドバーの定義を子テーマのfunction.phpファイルの中に貼り付けます。
この時親テーマの関数とコンフリクトする場合は、別途関数定義を外す処理が必要。複製したサイドバーを呼び出す
ここまででサイドバーを新たに追加した、ということになりますが、最後は、その追加したサイドバーを場合分けをして呼び出して表示するだけ。
サイドバーを呼び出している箇所を探す
まずサイドバーを呼び出して表示する箇所(dynamic_sidebar)を探します。
基本は「サイドバー」(sidebar.php)の中で、以下の様にな感じになっていると思います。
<?php dynamic_sidebar( 'sidebar-1' ); ?>※丸カッコ内の sidebar-1 がテーマによって異なる(数字だったり違う名称となっている)
場合分けをして呼び出し表示
サイドバーを呼び出して表示する箇所(dynamic_sidebar)が分かったら、以下のような場合分けをしてサイドバーを呼び出すように変更します。
1)ホームでは
今回追加したサイドバーを呼び出し表示する2)その他では
今まで使用していたサイドバーを表示する今回追加したサイドバーは、上の方で id に「my-top-sidebar」を設定したので、呼び出すには以下のような記述になります。
dynamic_sidebar( 'my-top-sidebar' );ということで、場合分け含めた具体的なサイドバーの呼び出しは、以下の様な感じになりますね。
<?php if( is_front_page() ){ // ホームの場合 dynamic_sidebar( 'my-top-sidebar' ); // 新たに追加したサイドバー } else { // その他の場合 dynamic_sidebar( 'sidebar' ); // 今まであるサイドバー } ?>追加したサイドバーを呼び出して表示
では実際に、新たに追加したサイドバーを呼び出せるように「サイドバー」(sidebar.php)を編集してみます。
sidebar.phpif( is_front_page() ){ // ホームの場合 dynamic_sidebar( 'my-top-sidebar' ); // 新たに追加したサイドバー } else { // その他の場合 dynamic_sidebar( 'sidebar-1' ); // 今まであるサイドバー }
- 投稿日:2020-11-26T18:23:21+09:00
Requestは、ただのバリデーションファイルじゃない【2020/11/26】
今まで僕は、LaravelのRequestファイル(ex userRequest.phpとか)は、バリデーションをするためにあるファイルだと思っていました。しかし、バリデーション機能はどちらかとういうとおまけ。
- △ バリデーション機能
- ◯ データを保持するオブジェクト + おまけでバリデーション
だった。
strutsでいう、ActionFormに一番近い(懐かしいな)。
ここからフォームやクエリパラメータに突っ込んだデータを取り出してくる。だから、Controllerファイルの中で、
xxxController.php$this->all()でデータとれるのはもちろん、
xxxRequest.php$this->all()と、Requestファイルの中でも普通に
$this
でデータが取れる。バリデーションしているだけなのになんで
Request
ファイルなのか疑問だったけど、get/postのリクエスト
を受け取っている紛れもないファイルだった。フレームワーク作る人って、すごいな。
- 投稿日:2020-11-26T17:21:57+09:00
PHP: Google Cloud Translation API の使い方 (Basic)
basic01.php#! /usr/bin/php <?php // ------------------------------------------------------------------ // // basic01.php // // Nov/26/2020 // // ------------------------------------------------------------------ require 'vendor/autoload.php'; use Google\Cloud\Translate\TranslateClient; $text = 'Es war einmal ein kleines Mädchen.'; $targetLanguage = 'ja'; $translate = new TranslateClient(); $result = $translate->translate($text, [ 'target' => $targetLanguage, ]); print("Source language: $result[source]\n"); print("Translation: $result[text]\n"); // ------------------------------------------------------------------ ?>実行方法
export GOOGLE_APPLICATION_CREDENTIALS=./***.json # ./basic01.php実行結果
Source language: de Translation: 昔々、小さな女の子がいました。
- 投稿日:2020-11-26T16:14:31+09:00
[PHPUNIT]setupメソッド使用時のよくあるエラー
laravelのphpunitでsetUp()メソッドを定義したところ、エラーが出て詰まったので共有します。
現状
.phpclass ...Test extends \TestCase { public function setUp() { ... } }エラー文
PHP Fatal error: Declaration of W3\Controllers\...Test::setUp() must be compatible with Illuminate\Foundation\Testing\TestCase::setUp(): void in作成したテストのsetUp()の返り値と自動で作成されるIlluminate\Foundation\Testing\TestCase
のsetUp()の返り値が違うことでした。解決策
.phpclass ...Test extends \TestCase { public function setUp():void { ... } }上記のようにsetup()に:voidを追記することで、実行できるようになります。
会社の紹介
私は現在、株式会社ダイアログという物流×ITの会社に勤務しております。
2020年11月現在、エンジニアの募集はしていませんが、他にも様々な職種を募集しているので、Wantedlyのページをご覧ください。
- 投稿日:2020-11-26T16:02:14+09:00
LaravelのKernel.phpのscheduleメソッド内でcommandを使ってハマった件
PHPとLaravelのバージョン
- Laravel Framework 5.7.28
- PHP 7.3.11
出ていたエラー
ERROR: Aborted. {"exception":"[object] (Symfony\\Component\\Console\\Exception\\RuntimeException(code: 0): Aborted. at /home/hoge/hoge/hoge_api/vendor/symfony/console/Helper/QuestionHelper.php:135){"exception":"[object] (Symfony\\Component\\Console\\Exception\\CommandNotFoundException(code: 0): Command \"hoge\" is not defined.書いていたコード
app/Console/Kernal.php
protected function schedule(Schedule $schedule) { $schedule->command('hoge')->cron('* * * * *'); }app/Console/Commands/Hoge.php
/** * The name and signature of the console command. * * @var string */ protected $signature = 'command:hoge';解決策
下記が叩かれていてそんなものないよ、と怒られていたので
'/usr/bin/php' 'artisan' hogecommand:を付ける
protected function schedule(Schedule $schedule) { $schedule->command('command:hoge')->cron('* * * * *'); }そうすると下記が叩かれるようになります。
'/usr/bin/php' 'artisan' command:hoge
- 投稿日:2020-11-26T15:58:13+09:00
PHPを深くまでしろう part1~言語構造編~
はじめに
最近、同年代の最強エンジニア方からいろいろなアドバイスを頂いているのですがその中で言語の言語仕様そのものについて深く知ることが重要と言われてから自分がよくPHPを使用しているので今一度PHPについて深く勉強しようとした記録です。
言語構造
PHPでよく使うechoやprintなどは関数ではなく言語構造と言われているのですが、そのことについて全然違いを理解していなかったので調べてみました。
if (式) { }上記のif文は()の中に式をいれることにより条件に一致する場合は{}の中が実行されます。
if (1 === 1) { echo 'ok'; } // とか if ($result = 'hello') { echo 'ok'; }など、他にも沢山の方法があります。
()の中には式を入れるのですが、式とはどういったことを指すの言葉なのでしょうか?
簡潔にいうと、PHPにおける式とは、値を持つものすべてのこと
このことを踏まえて、最初の話に戻るのですが、echoは言語構造なので()の中に使うとエラーが起きてしまいます。
なら、printはとなるのですがprintは関数の振る舞いをする言語構造になるのでエラーが起きません。if (print('hello')) { echo 'world'; } // hellowolrdと出力されるprint()を実行した後、返り値で1が返ってくるのでhellowolrdが出力されます。
このように他にも言語構造では返り値が返ってくるものがあります。
また、言語構造はif文やwhile文など制御構造に近しい部分があるみたいです。関数のように決まった形である
hello();とかではなく
echo 'hello'; echo('hello');のように両方の方法で呼び出せる
さいごに
自分が普段何気なく使用しているものを深く知ることが、一つ抜き出たエンジニアになれるのではないかと思っているので今後もこのような記事をアップしていきます。
まだまだ、全然理解できていな部分もありますが言語仕様を知ると今までとは違う視点で見ることが出来るので、とにかく楽しいです(笑)
- 投稿日:2020-11-26T15:53:19+09:00
PHPのセッション時間を伸ばす
- 投稿日:2020-11-26T15:53:19+09:00
レンタルサーバーのPHPのセッション時間を伸ばす
目的
レンタルサーバーmixhost上でのセッション時間を伸ばす
php.iniのsession.gc_maxlifetimeを伸ばしたが変化なし参考
仮説
セッションファイルはレンタルサーバーの共有フォルダにありそうなので他のユーザのアクセスで消される可能性がある
※下記の記事より仮説は正しそう
https://webmaster.chielog.com/php/161.htmlやったこと
セッション時間設定の延長(php.iniのsession.gc_maxlifetime)とともに、セッションファイルの保存場所を変更した
MultiPHP INI Editorに設定(session.save_path)があるので変更結果
セッション時間が延長された
- 投稿日:2020-11-26T15:53:19+09:00
レンタルサーバーのPHPのセッション時間を伸ばす【結果確認中】
目的
レンタルサーバーmixhost上でのセッション時間を伸ばす
php.iniのsession.gc_maxlifetimeを伸ばしたが変化なし参考
仮説
セッションファイルはレンタルサーバーの共有フォルダにありそうなので他のユーザのアクセスで消される可能性がある
※下記の記事より仮説は正しそう
https://webmaster.chielog.com/php/161.htmlやったこと
セッション時間設定の延長(php.iniのsession.gc_maxlifetime)とともに、セッションファイルの保存場所を変更した
MultiPHP INI Editorに設定(session.save_path)があるので変更結果
セッション時間が延長された
- 投稿日:2020-11-26T15:44:46+09:00
PHPで作る二段階認証
はじめに
PHPに限らず、Webアプリケーションを作る際に、何らかの”本人確認”的なことをしたい場面があると思います。事前に登録されたユーザ情報と照らし合わせるとかでいいと思うんですが、もうちょい手軽にできる一例を紹介したいと思います。それが、メールと組み合わせた2段階認証です。
この手法は、PHPで敷居の高い掲示板を作ってみたに使ったもので、Tokenというランダムな文字列を合言葉にして、メール宛てにその合言葉を送ることで、2段階認証をするものです。つまり、「メールアドレス=身分証明」的な扱いをしているという事です。※この手法の欠点としては、そもそも”メール=本人”ではないこと。メールで認証したから必ず本人とは限らない。あと、GETはURLの一部として扱われるので、気を付けてください。
概要
この仕組みの概要としては、
- サーバーでtokenを作り、token.txtに保存する
- メールにurl(token付き)を送信
- 確認画面でtoken.txtの内容と、urlについたtokenを確認する
って感じで、3のステップにて、urlについたtokenが正しければ、認証成功ってことです。
ちなみに、2のurl(token付き)とは、確認用ページへのURLに、GETでtokenをくっつけるって事です。例えば、確認用ページが「http://hogehoge/confirm.php」だったとして、tokenが「abcdefg123456」だったとすると、http://hogehoge/confirm.php?token=abcdefg123456がtoken付きURLという事になります。
(PHPでの例ですが、GETで指定できれば何の言語でも構いません。)ソースコード(PHPでの実装)
まず、ステップ1と2を実装したPHPファイルです。
index.php<?php function generate_token(){ $length = 10; return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyz', $length)), 0, $length); } function send_mail($message, $to){ $subject = "Tokenを送ります"; $headers = "From: hogehoge@gmail.com" . "\r\n"; $headers .= 'Content-type: text/html; charset=UTF-8'; mail($to, $subject, $message, $headers); } function inputForm(){ echo ' <form action="" method="post"> メールアドレス: <input type="email" name="mail"> <input type="submit"> </form> '; } function main(){ inputForm(); if(isset($_POST["mail"])){ $token = generate_token(); file_put_contents("token.txt", $token); echo $token.'<br><br>というTokenを発行しました。<br>'; $url = '<a href="http://localhost/confirm.php?token='.$token.'">token付きのURL</a>'; send_mail($url, $_POST["mail"]); echo $_POST["mail"].'へToken付きURLを送信しました。<br>'; }else{ echo 'メールアドレスを入力してください。'; } } main(); ?>確認用ページのPHPファイルです。
confirm.php<?php $token_form_mail = ""; if(isset($_GET["token"])){ $token_form_mail = $_GET["token"]; echo $token_form_mail.'というTokenを受け取りました。<br>'; } $token_from_file = file_get_contents("token.txt"); echo $token_from_file.'というTokenが保存されていました。<br>'; if($token_form_mail === $token_from_file){ echo "一致しました!"; }else{ echo "不一致です、有効でないTokenを受け取りました。"; } ?>動かしてみる
そんじゃ動かしてみます。index.phpに行くと、入力フォームがあるので、メールアドレスを入力してあげます。
すると、こんな感じでメッセージが出ます。token.txtにtokenを保存して、メールへtoken付きURLを送信しました。
サーバーのディレクトリのtoken.txtを確認すると、ちゃんと保存されてますね。
ちなみに、あて先のメールを確認してみると、ちゃんとURLが届いています。
メールのURLをクリックすると、承認成功!って感じですね。
ちなみに、tokenを適当に変更してみる(getで指定しているので、urlの最後に変な文字列を入れればいい)と、こんな感じで、認証に失敗します。
さいごに
これが、メールとtokenの組み合わせによる認証の仕組みです。ほかにもいろいろあるかと思いますが、簡単にまとめてみました。
それでは!
- 投稿日:2020-11-26T15:44:46+09:00
Token+メールによる認証
はじめに
PHPに限らず、Webアプリケーションを作る際に、何らかの”本人確認”的なことをしたい場面があると思います。事前に登録されたユーザ情報と照らし合わせるとかでいいと思うんですが、もうちょい手軽にできる一例を紹介したいと思います。
この手法は、PHPで敷居の高い掲示板を作ってみたに使ったもので、Tokenというランダムな文字列を合言葉にして、メール宛てにその合言葉を送ることで、本人確認的なことをするものです。つまり、「メールアドレス=身分証明」的な扱いをしているという事です。概要
この仕組みの概要としては、
- サーバーでtokenを作り、token.txtに保存する
- メールにurl(token付き)を送信
- 確認画面でtoken.txtの内容と、urlについたtokenを確認する
って感じで、3のステップにて、urlについたtokenが正しければ、認証成功ってことです。
ちなみに、2のurl(token付き)とは、確認用ページへのURLに、GETでtokenをくっつけるって事です。例えば、確認用ページが「http://hogehoge/confirm.php」だったとして、tokenが「abcdefg123456」だったとすると、http://hogehoge/confirm.php?token=abcdefg123456がtoken付きURLという事になります。
(PHPでの例ですが、GETで指定できれば何の言語でも構いません。)ソースコード(PHPでの実装)
まず、ステップ1と2を実装したPHPファイルです。
index.php<?php function generate_token(){ $length = 10; return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyz', $length)), 0, $length); } function send_mail($message, $to){ $subject = "Tokenを送ります"; $headers = "From: mimurosyunya@gmail.com" . "\r\n"; $headers .= 'Content-type: text/html; charset=UTF-8'; mail($to, $subject, $message, $headers); } function inputForm(){ echo ' <form action="" method="post"> メールアドレス: <input type="email" name="mail"> <input type="submit"> </form> '; } function main(){ inputForm(); if(isset($_POST["mail"])){ $token = generate_token(); file_put_contents("token.txt", $token); echo $token.'<br><br>というTokenを発行しました。<br>'; $url = '<a href="http://localhost/confirm.php?token='.$token.'">token付きのURL</a>'; send_mail($url, $_POST["mail"]); echo $_POST["mail"].'へToken付きURLを送信しました。<br>'; }else{ echo 'メールアドレスを入力してください。'; } } main(); ?>確認用ページのPHPファイルです。
confirm.php<?php $token_form_mail = ""; if(isset($_GET["token"])){ $token_form_mail = $_GET["token"]; echo $token_form_mail.'というTokenを受け取りました。<br>'; } $token_from_file = file_get_contents("token.txt"); echo $token_from_file.'というTokenが保存されていました。<br>'; if($token_form_mail === $token_from_file){ echo "一致しました!"; }else{ echo "不一致です、有効でないTokenを受け取りました。"; } ?>動かしてみる
そんじゃ動かしてみます。index.phpに行くと、入力フォームがあるので、メールアドレスを入力してあげます。
すると、こんな感じでメッセージが出ます。token.txtにtokenを保存して、メールへtoken付きURLを送信しました。
サーバーのディレクトリのtoken.txtを確認すると、ちゃんと保存されてますね。
ちなみに、あて先のメールを確認してみると、ちゃんとURLが届いています。
メールのURLをクリックすると、承認成功!って感じですね。
ちなみに、tokenを適当に変更してみる(getで指定しているので、urlの最後に変な文字列を入れればいい)と、こんな感じで、認証に失敗します。
さいごに
これが、メールとtokenの組み合わせによる認証の仕組みです。ほかにもいろいろあるかと思いますが、簡単にまとめてみました。
それでは!
- 投稿日:2020-11-26T13:20:53+09:00
【CakePHP】フォーム内のhiddenに配列を設定し、コントローラーで受け取る方法
実現したかったこと
hiddenに配列をもたせて、コントローラー側で取得する。
改善前
cakeのビュー側<?php echo $this->Form->hidden('hoge',array('value' => 1)); ?> <?php echo $this->Form->hidden('hoge',array('value' => 2)); ?> <?php echo $this->Form->hidden('hoge',array('value' => 3)); ?>クロムの検証ツールで覗いてみる
<input type="hidden" name="hoge" value="1"> <input type="hidden" name="hoge" value="2"> <input type="hidden" name="hoge" value="3">よし、いい感じ。
コントローラー側で受け取れているか見てみる。
コントローラー側のアクションの中で実行print_r($this->request->getData("hoge")); exit;画面出力結果3なんでやねん(笑)
自分が期待してた(欲しかった)結果Array ( [0] => 1 [1] => 2 [2] => 3 )修正した箇所
cakeのビュー側<?php echo $this->Form->hidden('hoge[]',array('value' => 1)); ?> <?php echo $this->Form->hidden('hoge[]',array('value' => 2)); ?> <?php echo $this->Form->hidden('hoge[]',array('value' => 3)); ?>のようにhogeの後ろに[]をつけてあげれば期待通りの配列が取得できました。
終わりに
解決出来てよかったですが、なんで改善前の記述だと「3」が返ってくるのか、いまいちわからないのが現状です。。。
- 投稿日:2020-11-26T09:33:07+09:00
Laravelでシングルアクションコントローラーを採用した際にInvalid route actionが出た件
はじめに
最近、アプリケーションアーキテクチャに興味を持ち、実際にLaravelでADR(Action Domain Responder)を試しています。Laravelは自由度が高いのでアーキテクチャ次第で全く違ったものになるという印象を受けています。
ADRには、1つのアクションとルートを対応させる事で複雑化を防ぐという思想があり、シングルアクションコントローラー(実際は、Action)を採用しました。(他にもメリットがあるかは勉強中です...)ルートが認識されない
公式を見つつシングルアクションクラスとルートを作成しました。
BookRegisterAction.php<?php namespace App\Http\Actions\Book; use App\Http\Controllers\Controller; final class BookRegisterAction extends Controller { public function __invoke() { // アクションの処理 } }以下のようにルーティングを設定。
web.phpRoute::prefix('book')->group(function () { Route::post('/register', App\Http\Actions\Book\BookRegisterAction::class)->name('book.register'); });すると下図のようなエラーが発生しました。
どうやらルーティングがだめとのこと。。。原因と解決策
Laravelはデフォルトで、コントローラーがApp\Http\Controllers\にあることを前提としています。
実際に発生したエラーには以下のような記載がありました。Illuminate\Routing\RouteAction::makeInvokable("App\Http\Controllers\App\Http\Actions\Book\BookRegisterAction")完全にデフォルト+追記したアクションになってます...
こちらはapp/Providers/RouteServiceProvider.phpに定義されているので修正を加えます。RouteServiceProvider.php<?php namespace App\Providers; class RouteServiceProvider extends ServiceProvider { protected $namespace = 'App\Http\Controllers'; ← ココがデフォルトのネームスペース /** * Define the "api" routes for the application. * * These routes are typically stateless. * * @return void */ protected function mapApiRoutes() { Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ← ここを削除 ->group(base_path('routes/api.php')); }上記のように変更を加えるとルートを認識させることができました。(デフォルトのnamespaceを変更でも可能です)
よりよい解決策などあれば、ご教授お願いいたします。
- 投稿日:2020-11-26T09:33:07+09:00
シングルアクションコントローラーをLaravelで採用した際にInvalid route actionが出た件
はじめに
最近、アプリケーションアーキテクチャに興味を持ち、実際にLaravelでADR(Action Domain Responder)を試しています。Laravelは自由度が高いのでアーキテクチャ次第で全く違ったものになるという印象を受けています。
ADRには、1つのアクションとルートを対応させる事で複雑化を防ぐという思想があり、シングルアクションコントローラー(実際は、Action)を採用しました。(他にもメリットがあるかは勉強中です...)ルートが認識されない
公式を見つつシングルアクションクラスとルートを作成しました。
BookRegisterAction.php<?php namespace App\Http\Actions\Book; use App\Http\Controllers\Controller; final class BookRegisterAction extends Controller { public function __invoke() { // アクションの処理 } }以下のようにルーティングを設定。
web.phpRoute::prefix('book')->group(function () { Route::post('/register', App\Http\Actions\Book\BookRegisterAction::class)->name('book.register'); });すると下図のようなエラーが発生しました。
どうやらルーティングがだめとのこと。。。原因と解決策
Laravelはデフォルトで、コントローラーがApp\Http\Controllers\にあることを前提としています。
実際に発生したエラーには以下のような記載がありました。Illuminate\Routing\RouteAction::makeInvokable("App\Http\Controllers\App\Http\Actions\Book\BookRegisterAction")完全にデフォルト+追記したアクションになってます...
こちらはapp/Providers/RouteServiceProvider.phpに定義されているので修正を加えます。RouteServiceProvider.php<?php namespace App\Providers; class RouteServiceProvider extends ServiceProvider { protected $namespace = 'App\Http\Controllers'; ← ココがデフォルトのネームスペース /** * Define the "api" routes for the application. * * These routes are typically stateless. * * @return void */ protected function mapApiRoutes() { Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ← ここを削除 ->group(base_path('routes/api.php')); }上記のように変更を加えるとルートを認識させることができました。(デフォルトのnamespaceを変更でも可能です)
よりよい解決策などあれば、ご教授お願いいたします。
- 投稿日:2020-11-26T08:41:29+09:00
Composerがインストールできない環境でComposerを使用したい時
PHPでシステムを組むときにComposerを使うかと思いますが、
レンタルサーバーだったり、権限がなかったりと、Composerをインストールできないときがあると思います。その場合、以下手順でComposerのコマンドを叩けます。
※既にローカル環境などで開発していて、composer.jsonがある前提です。
まずはcomposer.jsonがあるフォルダに移動します。
次にcomposer.pharというComposerを叩くためのファイルを落としてきます。
curl -sS https://getcomposer.org/installer | phpあとは以下で叩けます。
php composer.phar installupdate
php composer.phar updatecomposer
が
php composer.phar
に置き換わった感じですね。
- 投稿日:2020-11-26T04:49:03+09:00
Laravel で Target class [Controller] does not exist.が出た時の対応
事象
最近のLaravel (多分v8以降とかそのあたり)では、初期状態で以下のようなコードをroutes/web.phpに記述すると、あるはずのController Classを見つけられずエラーが発生する。
Route::get('/hoge', 'Controller@index');Illuminate\Contracts\Container\BindingResolutionException Target class [Controller] does not exist. http://laravel.internal/hoge Illuminate\Container\Container::build htdocs\laravel\vendor\laravel\framework\src\Illuminate\Container\Container.php:811どうやらContollerの初期位置を見失うようになったらしい。
対応
App/Providers/RouteServiceProvider.php の
$namespace
のコメントアウトを外す/** * The controller namespace for the application. * * When present, controller route declarations will automatically be prefixed with this namespace. * * @var string|null */ protected $namespace = 'App\\Http\\Controllers'; // ここがデフォルトでコメントアウトされるようになった参考等
https://kawax.biz/laravel8-routing/
(ここまで書いて理由とか気になってググったらこのブログで全部解決していた)
- 投稿日:2020-11-26T03:42:43+09:00
MAMPが起動できない時のエラー解除方法
Apache couldn't be started because port 8888 is in use by some other software.
というエラーが出て、MAMPがスタート出来なかったので調べていたところ、
https://qiita.com/xiaoxiao/items/9e5686a4b02b34929c8b
こちらの方の記事を見たら解決出来ました。
実際に実行したことは、
1、ターミナルのホームディレクトリで、以下コマンドを入力。
$ sudo lsof -i -P | grep "LISTEN"
2、ポートの状態が一覧で表示
※8888が使われているものを見つける。3、以下のコマンドで8888を削除
$ kill -9 XXXX
(XXXXは該当する番号を入力)4、もう一度 1、と同じコマンドで状態を確認すると、
8888を使っていたものが削除されていました。5、その後、MAMPを起動してみると無事に起動出来ました。
- 投稿日:2020-11-26T01:01:56+09:00
Centos7にてcomposer installを行うと「Your requirements could not be resolved to an installable set of packages.」と怒られた
なにが起こったか
Centos7に運用中のLaravelプロジェクトをクローンしてcomposer installしたらコケた。
$ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Your requirements could not be resolved to an installable set of packages. Problem 1 - Installation request for php-di/invoker 2.1.0 -> satisfiable by php-di/invoker[2.1.0]. - php-di/invoker 2.1.0 requires php >=7.3 -> your PHP version (7.2.28) does not satisfy that requirement. Problem 2 - Installation request for doctrine/dbal 2.11.1 -> satisfiable by doctrine/dbal[2.11.1]. - doctrine/dbal 2.11.1 requires php ^7.3 -> your PHP version (7.2.28) does not satisfy that requirement. Problem 3 - php-di/invoker 2.1.0 requires php >=7.3 -> your PHP version (7.2.28) does not satisfy that requirement. - mnapoli/silly 1.7.2 requires php-di/invoker ~2.0 -> satisfiable by php-di/invoker[2.1.0]. - Installation request for mnapoli/silly 1.7.2 -> satisfiable by mnapoli/silly[1.7.2].解決方法
エラーメッセージを見る限りPHPのバージョン問題だったので、PHPを7.2から7.4へアップデートした。
※正確にはPHP7.3以上を求められていた。PHP7.4インストール
$ sudo yum install -y --enablerepo=remi-php74 php which み込んだプラグイン:fastestmirror, langpacks Loading mirror speeds from cached hostfile * base: ty1.mirror.newmediaexpress.com * epel: nrt.edge.kernel.org * extras: ty1.mirror.newmediaexpress.com * remi-php74: ftp.riken.jp * remi-safe: ftp.riken.jp * updates: ty1.mirror.newmediaexpress.com ...PHPインストール確認
$ php -v PHP 7.4.13 (cli) (built: Nov 24 2020 10:03:34) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.13, Copyright (c), by Zend Technologies with Xdebug v2.9.8, Copyright (c) 2002-2020, by Derick Rethans
- 投稿日:2020-11-26T00:22:11+09:00
Symfonyの認可について
初めに
User
に紐づいたPost
エンティティがあったとき「自分の所有するPost
以外は見れない」という用件を達成するにはどのような実装方法があるか、という話です。最も単純な方法
最も単純な方法は、コントローラーでログインしている
User
のPost
かどうか判断する方法です。特に難しいこともありません。
が、しかし、 アクセス権を確認する際に同じようなコードが至る所に出てきてしまうことが容易に想像できます。/** * @Route("/{id}/detail", name="post_detail") */ public function edit(Post $post) { $user = $this->getUser(); if ($post->getUser() !== $user) { throw $this->createAccessDeniedException(); } ... }Voterを用いる方法
Symfonyには
Voter
という認可のための仕組みがあります1。
こちらを用いることで、認可のロジックを分離することができ、あらゆる箇所で使い回すことが出来るようになります。makeコマンドで作れます。
bin/console make:voter The name of the security voter class (e.g. BlogPostVoter): > PostVoter created: src/Security/Voter/PostVoter.phpmakeコマンドで作ると雛形が作成されます。今回は閲覧だけなのでこのように書き換えます。
src/Security/Voter/PostVoter.php<?php namespace App\Security\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\User\UserInterface; class PostVoter extends Voter { protected function supports($attribute, $subject) { return in_array($attribute, ['VIEW']) && $subject instanceof \App\Entity\Post; } protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { $user = $token->getUser(); if (!$user instanceof UserInterface) { return false; } switch ($attribute) { case 'VIEW': return $subject->getUser() === $user; // $subjectに認可対象のエンティティが入る } return false; } }これを、コントローラーからこのようにして呼び出すことができます。内部では
Voter
のvoteOnAttribute
メソッドが呼び出され、アクセス権がないと判断されると、自動でAccessDeniedException
が投げられます。/** * @Route("/{id}/detail", name="post_detail") */ public function edit(Post $post) { $this->denyAccessUnlessGranted('VIEW', $post); ... }更に美しく
コントローラーでの認可に限った話で言えば、アノテーションを使うことで更にメソッド内部をすっきりさせることができます。
認可のためのコードがメソッド内に1行も現れず、本来のロジックのみに集中出来るのでとても見やすくて良いと思います。/** * @Route("/{id}/detail", name="post_detail") * @IsGranted("VIEW", subject="post") */ public function edit(Post $post) { ... }参考文献