- 投稿日:2019-04-05T21:19:14+09:00
Laravel 5.8 にて["SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint"]エラー
Laravel 5.8 にて Cannot add foreign key constraint が出たときの対応
さきに答え書くと、
型が
bigIncrements
orbigInteger
で一致してるか確認してみて〜Laravel 5.8だと
php artisan make:migration 〜
で生成したテーブル定義では、
テーブルのメインidの型はbigIncrements
になってる。経緯
わーいLaravel 5.8 つかってみよ〜としたら、
php artisan migrate
で早速つまづいた。
以下のようなテーブル定義でmigrate実行したらエラー。//質問テーブル public function up() { Schema::create('questions', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->string('slug'); $table->string('body'); $table->integer('category_id')->unsigned(); $table->integer('user_id')->unsigned(); $table->timestamps(); }); } //返答テーブル public function up() { Schema::create('replies', function (Blueprint $table) { $table->bigIncrements('id'); $table->text('body'); $table->integer('question_id')->unsigned(); //外部キー制約 $table->foreign('question_id')->references('id') ->on('questions')->onDelete('cascade'); $table->integer('user_id'); $table->timestamps(); }); //エラー Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table `replies` add constraint `replies_question_id_foreign` foreign key (`question_id`) references `questions` (`id`) on delete cascade) at /var/www/bitfumes/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint") /var/www/bitfumes/vendor/laravel/framework/src/Illuminate/Database/Connection.php:458 2 PDOStatement::execute() /var/www/bitfumes/vendor/laravel/framework/src/Illuminate/Database/Connection.php:458 Please use the argument -v to see more details.対応
んじゃぐぐってみっか。
SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint でぐぐると、先人たちが
- テーブルの生成順序がおかしいのでは?外部キーを参照するテーブルが存在してないのでは?
- 制約キーのっけるカラムと、制約キーとして参照するカラムの型が一致してないのでは?
といった事例あり。たしかに確認してみると、自分の場合は
//questions $table->bigIncrements('id');//replies $table->integer('question_id')->unsigned(); //外部キー制約 $table->foreign('question_id')->references('id') ->on('questions')->onDelete('cascade');で型ずれてる。。。
ということで、以下なおしたらエラー解消されました◎//replies $table->bigInteger('question_id')->unsigned(); ^^^^^^^^^^^ //外部キー制約 $table->foreign('question_id')->references('id') ->on('questions')->onDelete('cascade');ちなみに、参照する側のテーブルのidを
increments
にしてもOKのようです。
以下参考URLのBe Careful: Laravel 5.8 Added bigIncrements As Defaults
には英語ですが、そのように記述が。
公式が変更点としてbigIncrements
orbigInteger
を取り上げてなかったことに、この記事の方おかんむりでした。参考URL
Be Careful: Laravel 5.8 Added bigIncrements As Defaults
【メモ】Laravelで外部キー制約付きのテーブルをマイグレーションする時に気をつけた方がよかった点
laravel5.7 外部キーエラー SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraintAdded bigIncrements
- 投稿日:2019-04-05T19:50:15+09:00
WindowsでCSVファイルアップロードうまくいかない。。。
導入
先日、とあるWebアプリケーションのCSVファイルアップロード機能を追加開発した時にMacではうまくいっていたファイルの中身のバリデートチェックがWindowsでは全然機能しなかった問題が発生したので備忘録として。
アプリケーション環境
- Ubuntu(バージョンは忘れた)
- PHP-7.2fpm
- CakePHP 2系
- nginx
動作確認環境
不具合が発生した環境
- WindowsのChrome、InternetExplorer11、FireFox
問題がなかった環境
- MacのChrome、FireFox
原因調査内容
MacとWindowでは同じChromeでも仕様がちがう?
→検索していろいろ調べてみたが原因解明できず、一旦保留
MacとWindowsではブラウザの初期のセキュリティ設定が違う??
→JavaScriptやCookieの設定等いろいろいじってみたが挙動変わらず。
nginxかPHPの設定がおかしい???
→nginxはそもそも関係なかった。。。
→PHPはphp.confにuploadファイルの設定があったので、一時保存先や最大ファイルサイズ等変更するが変わらず(そもそもサーバの設定ならWindowsとMacで挙動変わらないはず。)ctpファイル(View)の実装がおかしい????
→こちらもformタグに「multipart/form-data」が設定されているから問題ない気がする?
MacとWindowsでrequestの中身が違うんじゃ?????
→とりあえず、アップロードファイルが格納される変数をCotrollerで画面に出力するコードを記載
MacとWindowsのそれぞれで出力結果を確認してみる。// ファイルの中身を出力 echo "<pre>"; var_dump($_FILE); echo "</pre>"; exit();出力結果が以下、、、ん??
#Windows array(1) { ["data"]=> array(5) { ["name"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(12) "TEST.csv" } } ["type"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(24) "application/vnd.ms-excel" } } ["tmp_name"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(14) "/tmp/phpQYwrAs" } } ["error"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> int(0) } } ["size"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> int(56) } } } } #Mac array(1) { ["data"]=> array(5) { ["name"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(12) "TEST.csv" } } ["type"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(8) "text/csv" } } ["tmp_name"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> string(14) "/tmp/phpICM53a" } } ["error"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> int(0) } } ["size"]=> array(1) { ["XXXXXXXX"]=> array(1) { ["upfile"]=> int(18) } } } }MIMEtypeが違うな?
結論
ファイルの型のチェックの際、MIMEtypeのチェックをしていたが、
"text/csv"のみ許容する実装になっていたため、Windowsから送信されるMIMEtypeが"application/vnd.ms-excel"になっていたのでCSVファイルと判定されていませんでしたね。。。現在はファイル名の後ろが「.csv」かどうかで判定するように変更して落ち着きました。
- 投稿日:2019-04-05T15:56:33+09:00
conohaのオブジェクトストレージをopenstackでPHPから使う(2019年)
はい、S3使ったら苦労はないということはわかってます、ただ、転送量無料は正義です。
リファレンスも少しわかりにくかったので、メモ。$optionsの内容についてはconohaの管理画面にある。
使ってるopenstackは▽
https://github.com/php-opencloud/openstack/blob/master/doc/index.rstまずは、composerで入れてね。
$ composer require php-opencloud/openstack
use OpenStack\OpenStack; use Guzzle\Http\Exception\ClientErrorResponseException; use GuzzleHttp\Psr7\Stream; $options = [ 'authUrl' => env('CONOHA_AUTH_URL'), // APIの認証URL 'username' => env('CONOHA_USERNAME'), // API ユーザーのユーザー名 'password' => env('CONOHA_PASSWORD'), // API ユーザーのパスワード 'tenantName' => env('CONOHA_TENANT_NAME'), // テナント情報のテナント名 ]; try { $client = new OpenStack($options); $identityService = $client->identityV2(); $service = $client->objectStoreV1([ 'identityService' => $identityService, 'catalogName' => 'Object Storage Service', ]); //コンテナの存在確認 //ここはもしかすると作成だけでも良いかもしれない if ($service->containerExists('test')) { $container = $service->getContainer('test'); }else{ //コンテナが存在しない場合はコンテナを作成する $container_name = array( 'name' => 'test', 'readAccess' => '.r:*' //パブリックアクセスにする ); $container = $service->createContainer($container_name); } //gazelleでアップするファイルを読み込み $stream = new Stream(fopen('/var/www/html/app/webroot/img/unko.png', 'r')); $file_option = [ 'name' => 'unko.png', //オブジェクトストレージ保存するファイル名 'stream' => $stream, //これはファイルの本体データ ]; //ファイル(オブジェクトをアップする) $container->createObject($file_option); // コンテナ内のオブジェクト一覧の取得 $objects = $container->listObjects(); // コンテナの中のオブジェクトのパブリックURLを取得する foreach ($objects as $object) { $images_url[] = $object->getPublicUri(); }続いて、ACL関係も調査予定。
- 投稿日:2019-04-05T15:28:18+09:00
HomesteadでLaravel環境構築(Windows10)
前置き
もう各所で散々書かれている内容だから私が書かなくても…と思ったのですが、
「すでにネット上に同じ情報があるのだからQiitaに書くべきでない」という主張を目にすることがあるが、僕は反対の立場を取る。
— suin❄️PHPでオブジェクト指向 (@suin) 2019年4月1日
技術の再発明ならぬ「技術の再説明」が大事だと思うからだ。学んだことは気にせずどんどん書いていったほうがいいと思う。というsuinさんのツイートに背中を押されたので書くことにしました。
七転八倒したポイントを重点的に書けばいいよね。うんうん。環境
※ 構築したのは2019年1月頃
- Windows10
- Oracle VM VirtualBox 6.0
- vagrant 2.2.2
- PHP 7.2
- Laravel 5.7.21
準備
- VirtualBox インストール
- Vagrant インストール
- 上記2つをインストールしたら一旦PCを再起動してください
- Git Bashがあると便利
BOX追加
Git Bashまたはコマンドプロンプトを起動して以下を入力
GitBashcd c:\ vagrant box add laravel/homesteadここで「Enter your choice:」と出るので、
3(virtualbox) を入力してEnterします。しばらく待って、以下のような表示が出ればOK
GitBash==> box: Successfully added box 'laravel/homestead' (v6.4.0) for 'virtualbox'!Homesteadダウンロード(git clone)
公式からcloneします。
ディレクトリ名はお好きに。今回は特に何も考えずHomesteadにしました。GitBashgit clone https://github.com/laravel/homestead.git ~/Homestead設定の初期化
GitBashcd ~/Homestead ./init.sh # Homestead initialized! と表示されたらOKHomestead.yamlの修正
foldersとsitesを修正します
Gitbash# 場所は ~/Homestead のまま vi Homestead.yaml
Homestead.yaml# (中略) folders: - map: D:\homestead\Laravel # ローカルディレクトリ to: /home/vagrant/code # vagrant上のディレクトリ sites: - map: homestead.test # host名(起動後のドメイン名) to: /home/vagrant/code/public # web上に公開するディレクトリ(Apachで言うところのhtdocs)(補足) sites:mapに記載するドメイン名について
後で構築した環境をブラウザで確認するとき、
取り急ぎhttps://
ではなくhttp://
でアクセスする場合は
homestead.app ではなく homestead.test を指定する方が無難です。
http://homestead.app
はhttps://homestead.app
とリダイレクトされるのですが、
Chromeだと自己署名証明書がエラーとなるためアクセスできないためです。ただ、昨今の情勢としてはSSL通信で動かすことがベターだと思うので、
余裕があれば自己証明が通るよう設定した方がいいかもしれません。
(私はまだやってない……)SSH鍵ファイル作成
Homestead.yaml の authorize と keys にあたるファイルを作成します
Homestead.yamlauthorize: ~/.ssh/id_rsa.pub # 公開鍵 keys: - ~/.ssh/id_rsa # 秘密鍵GitBashcd ~ # 鍵が既にあるかどうか ls -la .ssh # 無ければ以下を実行 mkdir .ssh cd .ssh # ~/.ssh へ移動 # 鍵ファイル作成 ssh-keygen -t rsa # id_rsa.pub と id_rsa が作成されたことを確認する ls -lhostsファイル修正
C:\Windows\System32\drivers\etc\hosts
にホスト追加します。
これはGit Bashで編集できなかったのでサクラエディタでやっちゃいました。GitBash# Homestead.yamlの ip と sites:map を紐づける感じです 192.168.10.10 homestead.test
いざ、起動!
GitBash# Vagrantfileがある場所に移動 cd Homestead/ # 起動 ちょっと時間かかります vagrant up502 Bad Gateway になった
起動して
vagrant ssh
もできる状態になったのですが、
http://homestead.test
をブラウザで開くと502の表示……どうやら原因はnginxの方にあるらしい
502 Bad Gateway nginx (1.9.7) in Homestead [ Laravel 5 ]というわけで、nginxの設定を見ていきます
GitBash# homestead環境にログイン vagrant ssh # nginxの設定ファイルはこちら less /etc/nginx/nginx.conf「Virtual Host Configs」の項に何がincludeされているか書いてます
/etc/nginx/nginx.confhttp { # (中略) ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }includeしているディレクトリを見ると、
どうやら/etc/nginx/sites-available/homestead.test
がhomestead環境の設定ファイルのようです。/etc/nginx/sites-available/homestead.testserver { # (中略) access_log off; error_log /var/log/nginx/homestead.test-error.log error;エラーログの場所が分かったので、そちらを見ます。
(と言いつつ、調査当時のメモによるとなぜかhomestead.app-error.log
の方を見ている私…)/var/log/nginx/homestead.app-error.log2019/01/17 04:20:09 [crit] 1603#1603: *1 connect() to unix:/var/run/php/php7.2-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 192.168.10.1, server: homestead.app, request: "GET / HTTP/1.1", up stream: "fastcgi://unix:/var/run/php/php7.2-fpm.sock:", host: "homestead.test"php-fpmの方に原因がありそう。
類似のエラーを対処した方の記事を見ながら修正してみます。
nginxでphpのパッケージを追加したら’502 Bad Gateway’になってしまったときの対処法vagrant@homesteadsudo vi /etc/php/7.2/fpm/pool.d/www.conf # www.confのlistenを修正 ;listen = /run/php/php7.2-fpm.sock listen = /var/run/php/php7.2-fpm.sockそしてphp-fpmとnginxを再起動します
vagrant@homesteadsudo service php7.2-fpm restart sudo service nginx restartこれで502エラーが解消されました!
No input file specified が表示される
こちら によると
Homestead.yaml
のsites:to
のディレクトリが間違っているかもということで確認しましたが、私の場合その問題ではなさそうです。vagrant@homesteadls -l ~/code/public/index.php -rwxrwxrwx 1 vagrant vagrant 1823 Jan 17 04:56 /home/vagrant/code/public/index.php # Homestead.yamlの設定は以下のようになっているので問題なし sites: - map: homestead.test to: /home/vagrant/code/publicというわけで、エラーログをtailしながら
http://homestead.test
に再度アクセス。/var/log/nginx/homestead.app-error.log2019/01/17 05:16:18 [error] 7864#7864: *6 FastCGI sent in stderr: "Unable to open primary script: /home/vagrant/code/test/public/index.php (No such file or directory)" while reading response header from upstream, client: 192.168.10.1, server: homestead.app, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.2-fpm.sock:", host: "homestead.test"なぜか
/home/vagrant/code/test/public
を見に行っている模様。実は最初の頃Homestead.yamlを以下のように設定していた時期があり、その時の設定が適用されたままだったようです。
Homestead.yaml(古い設定)sites: - map: homestead.app to: /home/vagrant/code/test/publicエラーログが
homestead.app-error.log
の方に出ていたのもそれが原因です。
強制プロビジョニングで解消しました。GitBashvagrant up --provision
これで例の初期画面が表示されました。
その後、エラーログも/var/log/nginx/homestead.test-error.log
の方に出力されるようになりました。お疲れ様でした……!
その他参考
- Laravel 5.7 Laravel Homestead
- これだけを見て何とかしたかったですね(何とかならなかった)
- Vagrant + VirtualBoxでWindows上に開発環境をサクッと構築する
- Windows10でLaravel Homestead環境構築
- Laravel 開発環境の構築 ( VirtualBox + Vagrant + Homestead + Composer + Eclipse ) 第1回 -前編-
- 【Laravel超入門】開発環境の構築(VirtualBox + Vagrant + Homestead + Composer)
- Homestead で、Laravel 開発環境を構築する手順
- 【Vagrantドキュメント意訳】09.プロビジョニング
- 投稿日:2019-04-05T14:21:40+09:00
Wordpress でメールアドレスなしでユーザー登録したい(プラグインなし)
※注意:
Wordpressビギナーの記事(備忘録)です。
鵜呑みにしたら想定外のことが起こるかもしれないので気をつけてください。セキュリティ的なことも考慮していません。(気づきがあれば随時補足します)やりたいこと
Wordpress よ、メールアドレスなしでもユーザー追加させてくれ。
対象バージョン
Wordpress 5.1.1
結論から
以下のファイルを修正する。
[wp-admin/includes/user.php]
if ( empty( $user->user_email ) ) { // ↓をコメントアウト $errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please enter an email address.' ), array( 'form-field' => 'email' ) ); }[wp-admin/user-new.php]
メールアドレス部分のタグから form-required を削除。「必須」の表示が気になる人は required も削除。<!-- ↓からform-required を削除 --> <tr class="form-field form-required"> <!-- ↓をまるごと削除 --> <span class="description"><?php _e( '(required)' ); ?></span>経緯
開発用のユーザーとシステム管理のユーザーを同じメールアドレスで登録しようとしたらエラーになる。
Wordpress では同じメールアドレスで複数ユーザーを登録できない。ならば同じメールアドレスでも登録できるようにすればいいじゃない。
ということでソースを弄ることにする。ユーザー登録周りは user.php で処理している。
メールアドレスの存在チェックは email_exists() でしてる。function email_exists( $email ) { $user = get_user_by( 'email', $email ); if ( $user ) { return $user->ID; } return false; }最初は email_exists を弄るか、こいつの呼び出し元を変更しようと思ったけど、影響大きそうだからやめた。
ユーザー情報が複数とれたときの挙動の考慮とかしたくないし、email_exists の戻りのユーザーIDを一意にできない。
呼び出し元も各所に散らばってる。ググると「Allow Multiple Accounts」ってプラグインで解決できるとのことだったけど、プラグインが見つからない。
プラグインの新規追加で検索したら普通出てくるんじゃないの?とか思いながら、出ないものはしょーがないので、自力でどうにかする。そもそもメールアドレスを登録したいわけではないので、メールアドレスを非必須にする方針に変更。
意図的にメールアドレスを登録するなら、存在チェックもちゃんとやる。(つまり現状維持)サーバ側(user.php)は POST されたメールアドレスがブランクだったらそもそもテーブル参照すらせずにエラーにしてるから、そこをコメントアウトするだけ。
クライアント側(user-new.php)は form-required で必須チェックが効いてるから、それを外すだけ。これで無事、メールアドレスなしで複数ユーザー登録できました。
メールアドレスなしユーザーでのログインも問題なし。(ログインしただけだからその先は知らない)
おまけ
user.php は wp-includes/user.php と wp-admin/includes/user.php があるので注意。
最初 wp-includes/user.php しかないと思ってて、修正しても全然反映されないとか思ってハマった。
- 投稿日:2019-04-05T14:14:54+09:00
【Laravel】Request/Responseオブジェクトの利用
コントーラ編集
- useで
Request/Response
追加。- アクションを定義時に引数にRequest/Responseを入れる。
- この引数に入れた変数がオブジェクトになるので、アクションとして使用していく感じ。
サンプルコード
テストページの表示。
クライアント側からのリクエスト情報を表示。
こちらからのレスポンスを表示。<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\Response; class HelloControllerRR extends Controller { public function index(Request $request, Response $response) { $html= <<<EOF <html> <head> <meta charset="UTF-8"> <title>Request/Response</title> <style>body{font-size:16pt;} h1{font-size:100px; color:#eee;}</style> </head> <body> <h1>Request/Response</h1> <p>これはRequest/Responseオブジェクトを利用したアクションです。</p> <hr> <h3>Request</h3> <pre>{$request}</pre> <ul> <li>url()メソッド:{$request->url()}</li> <li>fullUrl()メソッド:{$request->fullUrl()}</li> <li>path()メソッド:{$request->path()}</li> </ul> <hr> <h3>Response</h3> <pre>{$response}</pre> </body> </html> EOF; $response->setContent($html); return $response; } } ?>使用メソッド
Request
$request->url()
:アクセスしたURLを返す。$request->fullUrl()
:アクセスしたアドレスを完全な形で返す。クエリー文字(?abc=xyzなど)を含む。$request->path()
:ドメイン下のパス部分だけ返す。Response
this->status()
:クライアント返答時のステータスコードを返す。正常なら200。this->content()
:コンテンツの取得。this->setContent(値)
:引数の値にコンテンツを変更する。
- 投稿日:2019-04-05T13:43:16+09:00
php-master-changes 2019-04-04
今日は JIT の修正、ドキュメントの typo 修正と不使用変数の削除があった!
2019-04-04
dstogov: Avoid useless code generation for RECV opcodes
- https://github.com/php/php-src/commit/fc28e9a0b3b8752898a4538721a58e366836630f
- ext/opcache/jit で、RECV オペコード(関数用のコード冒頭で引数をスタックから取り出して型チェックする奴)での不要なコード生成をしないよう修正
dstogov: Fixed crash
- https://github.com/php/php-src/commit/34d5c1b16fc0499548806b97e3d4c49a2756406a
- ext/opcache/jit で、最適化レベル 1 以下の場合でクラッシュする問題があったのを修正
dstogov: jump optimization
- https://github.com/php/php-src/commit/a146e6eea7aeb395cfac9e706d81107a97009ed1
- ext/opcache/jit で、new 用の命令 ZNED_NEW のコードを最適化
- 関数分離してたのをインラインに開いて zend_jit_cond_jmp() 使うようにして is_terminated 付けるようにしてる
dstogov: Avoid code generation for BB with single VM instruction
- https://github.com/php/php-src/commit/21f249cfa4c78466f54223092d4f2b316450e802
- ext/opcache/jit で、最適化レベル 1 以下の場合オペコード 1 つの basic block についてはコード生成をしないよう修正
cmb69: Fix typos
- https://github.com/php/php-src/commit/54bed8d2ea7c885118ea73e197fc3ff8eaf24cca
- [7.3~]
- NEWS の typo を修正
dstogov: unused variable
- 投稿日:2019-04-05T13:16:00+09:00
【Laravel】コントローラの作成と編集
前提知識
MVCアーキテクチャ
- Model:データ処理。DBに関係。
- View:テンプレートのレンダリング。
- Controller:全体制御。<-ここの話コントローラ作成
ターミナルにて下記コマンド。
php artisan make:controller HelloController
app/Http/Controllers
フォルダ内にHellorController.php
が作成される。
これはApp/Http/Controllers
という名前空間にクラスとして配置される。コントローラにアクションを追加・ルーティング
HellorController.php
を開いてみるとデフォルトでいろいろ書いてある。
namespace~
:名前空間use~
:Illuminate/Httpパッケージ内のRequestを使える状態にしている。class~
:HelloControllerはControllerというクラスを継承。- この下にアクションを追加していく。 なお、ルートパラメーターとして
$id
と$unknown
を渡す。<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class HelloController extends Controller { public function index($id='noname', $pass='unknown'){ return <<< EOF <html> <head> <title>Hello/Index</title> <meta chraset="UTF-8"> <style> body{font-size:16pt;} h1{font-size:100px; color:#eee;} </style> </head> <body> <h1>Index</h1> <p>これは、Helloコントーラのindexアクションです。</p> <ul> <li>ID: {$id}</li> <li>PASS: {$pass}</li> </ul> </body> </html> EOF; } } ?>次にルートを編集。
routes/web.php
にてRoute::get('hello/{id?}/{pass?}', 'HelloController@index');これでOK。
コントローラに複数のアクションを持たせる場合
(1)コントローラを記述
app/Http/Controllers/HelloController.php
を編集。
まず名前空間とRequestを設定(namespace~/use~)。
変数と関数の定義(global~/function~)。
そしてからクラス定義。
クラスの中にアクションを二つ以上持たせることで、コントーラに複数のアクションを持たせる。<?php // 名前空間とRequestの使用可能を設定。 namespace App\Http\Controllers; use Illuminate\Http\Request; // コントーラクラス作成時に必要な関数を自作 global $head, $head, $style, $body, $end; $head = '<html><head>'; $style = <<<EOF <style> body{font-size:16pt;} h1{font-size:100px; color:#eee;} </style> EOF; $body = '</head><body>'; $end = '</body></html>'; function tag($tag, $txt){ return "<{$tag}>".$txt."</{$tag}>"; } // コントーラクラスの作成 class HelloController extends Controller { // 第一アクション public function index(){ global $head, $head, $style, $body, $end; $html=$head.tag('title', 'Hello/Index') .$style .$body.tag('h1', 'Index').tag('p', 'this is Index page').'<a href="/hello/other">go to Other page</a>' .$end; return $html; } // 第二アクション public function other(){ global $head, $head, $style, $body, $end; $html=$head.tag('title', 'Hello/Other') .$style .$body.tag('h1', 'other').tag('p', 'this is Other page') .$end; return $html; } } ?>(2)ルート情報を編集
routes/web.php
を編集。
Route:get
の第二引数でコントーラとアクション名を指定。Route::get('hello', 'HelloController@index'); Route::get('hello/other', 'HelloController@other');これでコントローラに複数のアクションを持たせることができた。
シングルアクションコントーラ
namespace,useは省略。
クラスにつきアクションを一つのみ持つ。
アクション定義部に__invoke()
を足す。class HelloControllerSingle extends Controller { public function __invoke(){ return <<<EOF <html> <head> <meta charset="UTF-8"> <title>Hello/Single</title> <style>body{font-size:16pt;} h1{font-size:100px; color:#eee;}</style> </head> <body> <h1>Single Action</h1> <p>これはシングルアクションコントーラのアクションです。</p> </body> </html> EOF; } }
- 投稿日:2019-04-05T09:28:50+09:00
Heroku(PHP+MySQL)でWebサービス公開手順
はじめに
localhostでPHP+MySQLで開発したものを、Herokuで公開するためのもの
OS: mac
PHP: フレームワーク無し
DB: MySQL1.Herokuに登録
https://www.heroku.com/
クレジットカードを登録しておきます。2.Heroku CLIをインストール
https://devcenter.heroku.com/articles/heroku-cli
$ heroku login3.アプリ作成
ここではデスクトップにheroku-appというディレクトリを作ります。
$ cd Desktop $ mkdir heroku-app $ cd heroku-appアプリを作成します。アプリ名を指定しない場合は自動で割り振られます。
$ heroku create アプリ名4.composer
composer.jsonというファイルを作成します。
touch composer.jsonphpのバージョン7を指定します。これが正しいのか分かりませんがこれで動きました。
"ext-mbstring": "*"
とすることでmb_xxxの関数(mb_strlenとか)が使えるようになります。指定しないでmb_xxxの関数を使うとCall to undefined function
エラーになります。composer.json{ "require": { "php": "~7.0", "ext-mbstring": "*" } }デフォルトのPHPが入っていることが前提です。
$ curl -sS https://getcomposer.org/installer | php $ mv composer.phar /usr/local/bin/composerターミナルを再起動して確認します。大丈夫ならインストールします。
$ cd Desktop/heroku-app $ composer -v $ composer install5.Gitリポジトリ作成
$ git init $ git remote add heroku https://git.heroku.com/アプリ名.git $ git remote -v6.ClearDBアドオンをインストール
herokuでmysqlを使うための設定です。
$ heroku addons:add cleardb:ignite7.Workbenchをインストール、設定
Workbenchを使うことで、phpMyAdminのようにGUIでデータベースを管理できます。
インストールが完了したら赤枠で囲ったMySQL Connections
の横の+
をクリックして設定画面を開きます。
heroku config
コマンドでCLEARDB_DATABASE_URL
を確認します。$ heroku configこれを元に入力していきます。
CLEARDB_DATABASE_URL: mysql://ユーザ名:パスワード@サーバ名/データベース?reconnect=true
Connection Name: 適当な名前
Hostname: サーバ名
Username: ユーザ名
Password: パスワード
Default Schema: データベース入力を終えたら、
Test Connection
をクリック→上で入力したパスワードを入力→Connection Warning
画面が出る→Continue Anyway
→Successfully made the MySQL connection
→OK
→OK
8.phpMyAdminからDBエクスポート→WorkbenchにDBインポート
phpMyAdminからエクスポート
phpMyAdminからエクスポートしたいDBを選択→
詳細
にチェック→出力をファイルに保存する
→実行
Workbenchにインポート
7.で設定したものに接続して
Administraion
タブからData Import/Restore
→Import from Self-Contained File
にチェックを入れてphpMyAdminからエクスポートしたsqlファイルを選択します。
Default Target Schema
を指定します。newの方ではなくてセレクトメニューでデフォルトのものを指定します。
なぜかフリーズしたように?選択できないときがありますが、謎です。
最後にStart Import
をクリックします。9.PHPのDB接続設定を変更する
local用から本番用に書き換える必要があります。
function.phpfunction dbConnect(){ $db = parse_url($_SERVER['CLEARDB_DATABASE_URL']); $db['dbname'] = ltrim($db['path'], '/'); $dsn = "mysql:host={$db['host']};dbname={$db['dbname']};charset=utf8"; $user = $db['user']; $password = $db['pass']; $options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>true, ); $dbh = new PDO($dsn,$user,$password,$options); return $dbh; }10.herokuへデプロイ
$ git add -A $ git commit -m "commit" $ git push heroku masterheroku openでブラウザを開いて表示してくれます。localと同じようにDBのデータ等ちゃんと表示できていれば完了です。
$ heroku open11.WorkbenchでDBの中身を確認する
赤枠で囲った
Schemas
→Tables
各テーブルにマウスホバーすると、右にアイコンが出る(赤枠で囲った所)ので一番右のカレンダーみたいなアイコンをクリックするとデータが確認できます。または、SQL文を打ち込んで雷アイコンを押してもデータ確認ができます。
その他エラーなど
・herokuはphpログファイルとかエラーが見れないようなので、見るには
ini_set('display_errors',1)
を一時的に使います。・
Error Code: 2013. Lost connection to MySQL server〜
というエラーが出る場合
→https://stackoverflow.com/questions/10563619/error-code-2013-lost-connection-to-mysql-server-during-query・composer.jsonを編集したら作業ディレクトリで
composer update
を行う$ composer update参考:
herokuでPHPからMySQL(ClearDB)を使うhttp://blog.a-way-out.net/blog/2014/12/11/heroku-php-mysql/
herokuがcomposer.lock必須になったのでcomposerの入れ方をメモしておくhttp://kayakuguri.github.io/blog/2015/08/25/composer-lock-require/
Heroku データベースの管理(ClearDB アドオンによる MySQL の利用)http://www.ownway.info/Ruby/heroku/how/management/database/cleardb
MySQL WorkbenchをMacにインストールする方法https://programmingnavi.com/77/
heroku環境でmb_xxx関数を使うと、Fatal error: Call to undefined function …のエラーが表示されるhttps://qiita.com/taro-hida/items/f677abe2bc3b689002b3
- 投稿日:2019-04-05T09:28:50+09:00
test
1.Herokに登録
クレジットカードを登録しておきます。
2.Herok CLIをインストール
https://devcenter.heroku.com/articles/heroku-cli
$ heroku login
3.Gitリポジトリ作成
ここではデスクトップにheroku-appというディレクトリを作ります。
$ cd Desktop
$ mkdir heroku-app
$ cd heroku-app
$ git init
$ git remote add heroku https://git.heroku.com/アプリ名.git
- 投稿日:2019-04-05T09:28:50+09:00
下書き
- mac
- heroku
- php(フレームワーク無し)
- mysql
- コマンド操作多め
1.Herokuに登録
クレジットカードを登録しておきます。
2.Heroku CLIをインストール
https://devcenter.heroku.com/articles/heroku-cli
$ heroku login3.アプリ作成
ここではデスクトップにheroku-appというディレクトリを作ります。
$ cd Desktop $ mkdir heroku-app $ cd heroku-appアプリを作成します。アプリ名を指定しない場合は自動で割り振られます。
$ heroku create アプリ名4.composer
composer.jsonというファイルを作成します。
touch composer.jsonphpのバージョン7を指定します。これが正しいのか分かりませんがこれで動きました。
"ext-mbstring": "*"
とすることでmb_xxxの関数(mb_strlenとか)が使えるようになります。指定しないとCall to undefined function
エラーになります。composer.json{ "require": { "php": "~7.0", "ext-mbstring": "*" } }デフォルトのPHPが入っていることが前提です。
$ curl -sS https://getcomposer.org/installer | php $ mv composer.phar /usr/local/bin/composerターミナルを再起動して確認します。大丈夫ならインストールします。
$ composer -v $ composer install5.ClearDBアドオンをインストール
herokuでmysqlを使うための設定です。
$ heroku addons:add cleardb:ignite6.WorkBenchをインストール
phpMyAdminのようにGUIでデータベースを管理できます。
```
$ heroku config```
phpMyAdminからデータベース エクスポート デフォルトはテキスト出力なのでsqlファイルに出力する
Workbenchで インポート6.Gitリポジトリ作成
$ git init $ git remote add heroku https://git.heroku.com/アプリ名.git $ git remote -v7.herokuへデプロイ
$ git add -A $ git commit -m "commit" $ git push heroku masterその他
herokuはphpログファイルとかエラー見れないようなので
ini_set('display_errors',1)
を一時的に使います。参考:
herokuでPHPからMySQL(ClearDB)を使うhttp://blog.a-way-out.net/blog/2014/12/11/heroku-php-mysql/
herokuがcomposer.lock必須になったのでcomposerの入れ方をメモしておくhttp://kayakuguri.github.io/blog/2015/08/25/composer-lock-require/
Heroku データベースの管理(ClearDB アドオンによる MySQL の利用)http://www.ownway.info/Ruby/heroku/how/management/database/cleardb
- 投稿日:2019-04-05T09:28:50+09:00
HerokuでWebサービス公開(PHP+MySQL)
はじめに
localhostでPHP+MySQLで開発したものを、Herokuで公開するためのもの
OS: mac
PHP: フレームワーク無し
DB: MySQL1.Herokuに登録
https://www.heroku.com/
クレジットカードを登録しておきます。2.Heroku CLIをインストール
https://devcenter.heroku.com/articles/heroku-cli
$ heroku login3.アプリ作成
ここではデスクトップにheroku-appというディレクトリを作ります。
$ cd Desktop $ mkdir heroku-app $ cd heroku-appアプリを作成します。アプリ名を指定しない場合は自動で割り振られます。
$ heroku create アプリ名4.composer
composer.jsonというファイルを作成します。
touch composer.jsonphpのバージョン7を指定します。これが正しいのか分かりませんがこれで動きました。
"ext-mbstring": "*"
とすることでmb_xxxの関数(mb_strlenとか)が使えるようになります。指定しないでmb_xxxの関数を使うとCall to undefined function
エラーになります。composer.json{ "require": { "php": "~7.0", "ext-mbstring": "*" } }デフォルトのPHPが入っていることが前提です。
$ curl -sS https://getcomposer.org/installer | php $ mv composer.phar /usr/local/bin/composerターミナルを再起動して確認します。大丈夫ならインストールします。
$ cd Desktop/heroku-app $ composer -v $ composer install5.Gitリポジトリ作成
$ git init $ git remote add heroku https://git.heroku.com/アプリ名.git $ git remote -v6.ClearDBアドオンをインストール
herokuでmysqlを使うための設定です。
$ heroku addons:add cleardb:ignite7.Workbenchをインストール、設定
Workbenchを使うことで、phpMyAdminのようにGUIでデータベースを管理できます。
インストールが完了したら赤枠で囲ったMySQL Connections
の横の+
をクリックして設定画面を開きます。
heroku config
コマンドでCLEARDB_DATABASE_URL
を確認します。$ heroku configこれを元に入力していきます。
CLEARDB_DATABASE_URL: mysql://ユーザ名:パスワード@サーバ名/データベース?reconnect=true
Connection Name: 適当な名前
Hostname: サーバ名
Username: ユーザ名
Password: パスワード
Default Schema: データベース入力を終えたら、
Test Connection
をクリック→上で入力したパスワードを入力→Connection Warning
画面が出る→Continue Anyway
→Successfully made the MySQL connection
→OK
→OK
8.phpMyAdminからDBエクスポート→WorkbenchにDBインポート
phpMyAdminからエクスポート
phpMyAdminからエクスポートしたいDBを選択→
詳細
にチェック→出力をファイルに保存する
→実行
Workbenchにインポート
7.で設定したものに接続して
Administraion
タブからData Import/Restore
→Import from Self-Contained File
にチェックを入れてphpMyAdminからエクスポートしたsqlファイルを選択します。
Default Target Schema
を指定します。newの方ではなくてセレクトメニューでデフォルトのものを指定します。
なぜかフリーズしたように?選択できないときがありますが、謎です。
最後にStart Import
をクリックします。9.PHPのDB接続設定を変更する
local用から本番用に書き換える必要があります。
function.phpfunction dbConnect(){ $db = parse_url($_SERVER['CLEARDB_DATABASE_URL']); $db['dbname'] = ltrim($db['path'], '/'); $dsn = "mysql:host={$db['host']};dbname={$db['dbname']};charset=utf8"; $user = $db['user']; $password = $db['pass']; $options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>true, ); $dbh = new PDO($dsn,$user,$password,$options); return $dbh; }10.herokuへデプロイ
$ git add -A $ git commit -m "commit" $ git push heroku masterheroku openでブラウザを開いて表示してくれます。localと同じようにDBのデータ等ちゃんと表示できていれば完了です。
$ heroku open11.WorkbenchでDBの中身を確認する
赤枠で囲った
Schemas
→Tables
各テーブルにマウスホバーすると、右にアイコンが出る(赤枠で囲った所)ので一番右のカレンダーみたいなアイコンをクリックするとデータが確認できます。または、SQL文を打ち込んで雷アイコンを押してもデータ確認ができます。
その他エラー
・herokuはphpログファイルとかエラーが見れないようなので、見るには
ini_set('display_errors',1)
を一時的に使います。・
Error Code: 2013. Lost connection to MySQL server〜
というエラーが出る場合
→https://stackoverflow.com/questions/10563619/error-code-2013-lost-connection-to-mysql-server-during-query参考:
herokuでPHPからMySQL(ClearDB)を使うhttp://blog.a-way-out.net/blog/2014/12/11/heroku-php-mysql/
herokuがcomposer.lock必須になったのでcomposerの入れ方をメモしておくhttp://kayakuguri.github.io/blog/2015/08/25/composer-lock-require/
Heroku データベースの管理(ClearDB アドオンによる MySQL の利用)http://www.ownway.info/Ruby/heroku/how/management/database/cleardb
MySQL WorkbenchをMacにインストールする方法https://programmingnavi.com/77/
heroku環境でmb_xxx関数を使うと、Fatal error: Call to undefined function …のエラーが表示されるhttps://qiita.com/taro-hida/items/f677abe2bc3b689002b3
- 投稿日:2019-04-05T01:14:51+09:00
メモ: laravel で trait を使って static method で Event 登録するような時に Mockery を使って UnitTest を書く方法
よく忘れるので自分用のメモ。
何を実現したいか
例えば、以下のような Trait が存在して、
static::deleted()
の Closure も Mockery を使って UnitTest でカバーしたい時の方法。trait SomeTrait { /** * boot */ public static function bootSomeTrait() { static::deleted(function ($model) { $model->someModelsMethod(); }); } }どうやって実現するか
以下のようにすると closure 部分も UnitTest でカバーできるようになる。
final class SomeTraitTest extends TestCase { public function testSomeTrait() { $class = new class { use SomeTrait; public static function deleted($closure) { $mock = Mockery::mock(stdClass::class); $mock->shouldReceive('someModelsMethod')->once()->with(); $closure($mock); } }; $class::bootSomeTrait(); } }anonymous class 内で
use SomeTrait
して、SomeTrait
が用いるpublic static function deleted($closure)
を anonymous class に定義する。この anonymous class の引数である
$closure
はSomeTrait
のstatic::deleted(function ($model) {...}
の function 部分が引数なので、 callable となる。なので、ここで適切に Mockery 等を使って何らかの手続を確認するように書けば良い。
注意点とか
anonymous class(無名クラス)使ってるので、当然 PHP7 以上じゃないとダメ。
laravel で event を登録する処理を書くなら、素直に Observer 使うべきだと思う。 https://laravel.com/docs/5.8/eloquent#observers
ただ、外部のパッケージで便利なやつがあって、それが Trait で実現されてて…かつ、そのパッケージに少しだけオリジナルな処理を追加したい時とかに、 trait ベースで Event 登録が必要になったりする。
あと
parent::otherMethod();
とかある場合は、このアプローチは使えない。(anonymous class 使ってるので親が居ないので当然。)
- 投稿日:2019-04-05T00:10:24+09:00
getterの命名について(PHPの場合)
TL;DR
getterの命名とか、悩みを吐き出して冷静になります
「これgetterだから
getHoge()
の方がわかりやすくないですか?」と言われ、明確にNoと言えなかったので言語化しておきたいのです※ setterにはあまり触れません
結論
hoge
プロパティを参照する際は
*__get()
を実装し
*@property-read hoge
を定義し
* getterとしてのgetHoge()
は撲滅発端
ゲームとかで、IDと経験値を持った以下のPlayerクラスを考えます
class Player { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } }何も考えずにgetterをつけてやると、おそらく以下です
class Player { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function getId(): int { return $this->id; } public function getExp(): int { return $this->exp; } }要件
このPlayerクラスに「経験値を取得する」メソッドを定義したいですが、どういう命名が適切でしょうか?
いわゆるsetterとして
setExp()
を定義すべきでしょうか?class Player { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function getId(): int { return $this->id; } public function getExp(): int { return $this->exp; } public function setExp(int $exp): self { $this->exp = $exp; return $this; } }あるいはプロパティの
exp
に対して加算するので、addExp()
を定義すべきでしょうか?class Player { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function getId(): int { return $this->id; } public function getExp(): int { return $this->exp; } public function addExp(int $exp): self { $this->exp += $exp; return $this; } }違和感
addExp()
にしたとして、利用する側を考えると以下のようになりそうです$player = new Player($id, $exp); // 現在の経験値 echo $player->getExp() . PHP_EOL; $addExp = someFunction(); // 獲得したい経験値 $player->addExp($addExp); // 加算後の経験値 echo $player->getExp() . PHP_EOL;でも「経験値を取得する」って、本来はPlayerから見た振る舞いではないでしょうか?
setExp()
やaddExp()
は誰かがPlayerに「取得させてる」って感じの振る舞いです「経験値を取得する」をPlayerから見たら、それこそ
getExp()
なのでは?と思うわけですclass User { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function getId(): int { return $this->id; } public function getExp(): int { return $this->exp; } public function getExp(int $exp): self { $this->exp += $exp; return $this; } }でもこれは当然、本来のgetterとしての
getExp()
と重複してるのでエラーになりますそもそも
Playerの現在の経験値を参照するメソッドが
getExp()
っていうのも、やっぱり「誰かから見た」振る舞いですオブジェクト指向って、もっとオブジェクトが自身の振る舞いを定義するべきでは?と思うわけです
そう考えると、Playerの現在の経験値を参照するメソッドは、なんなら
takeExp()
とかshowExp()
とかの方がよっぽどオブジェクト指向的だと思うわけですでも文脈によって
take
やshow
は適切でない場合が多々あると思うので、プロパティそのものが参照できれば一番無難そうです解決
PHPではマジックメソッド__get()を実装することで、存在しないプロパティへアクセスした際の挙動を制御できます
「経験値を取得する」を
getExp()
として、合わせて定義すると以下です/** * @property-read int $exp */ class User { private $id; private $exp; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function __get(string $property) { if (!property_exists($this, $property)) { throw new \Exception("Invalid access: {$property}"); } return $this->$property; } public function getExp(int $exp): self { $this->exp += $exp; return $this; } }利用する側は以下のようになります
$player = new Player($id, $exp); // 現在の経験値 echo $player->exp . PHP_EOL; $addExp = someFunction(); // 獲得したい経験値 $player->getExp($addExp); // 加算後の経験値 echo $player->exp . PHP_EOL;
@property-read
アノテーションを付けておくことで、PhpStorm等のIDEでも認識してくれるようになりますしかしこの場合、
@property-read
アノテーションを付けていないid
プロパティも同様にアクセスできてしまいます
@property-read
したものしか見せたくないという場合は、__get()
でバリデーションするしかなさそうです/** * @property-read int $exp */ class User { private $id; private $exp; private const VISIBLE_PROPERTIES = [ 'exp', ]; public function __construct(int $id, int $exp) { $this->id = $id; $this->exp = $exp; } public function __get(string $property) { if (!property_exists($this, $property)) { throw new \Exception("Invalid access: {$property}"); } if (!in_array($property, self::VISIBLE_PROPERTIES)) { throw new \Exception("Cannot access: {$property}"); } return $this->$property; } public function getExp(int $exp): self { $this->exp += $exp; return $this; } }まとめ
さすがに
getExp()
は紛らわしいですよ…という意見はありそうですが、それはそれとして脳死でgetter定義せずに、もっともっとオブジェクティブな振る舞いを定義していきたいなと思う次第です