- 投稿日: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-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:24:30+09:00
docker-composeでLaravelローカル環境を構築(Laradockは使わない)
初学者向けのものです。初学者がわからなくなるコマンド叩いていたらローカル側なのかコンテナ側なのかわからなくなってグチャグチャにならないように、ローカル側でのみコマンド実行すれば良いように構築しています。
前提
- 本番環境向けの最適化された設定ではないので、Dockerイメージをそのまま本番利用はしないように注意ください
- Macユーザ向けです
- 以下、インストール済みであること
- nginx,php-fpm,composer,mysqlの環境を作ります
インストール
ホームディレクトリに移動して、プロジェクトフォルダを作成
projectsフォルダが無いことを確認してください
HogeApp
は自由に変更して大丈夫です$ cd ~ $ mkdir -p projects/HogeApp/{docker,src}こんなフォルダができる
projects └── HogeApp ├── docker └── srcまずはディレクトリを作成
$ cd ~/projects/HogeApp/docker $ mkdir -p {nginx/conf.d,php-fpm,mysql-data}Nginxの設定
Dockerfile(Nginx)
$ vim nginx/Dockerfile以下を保存
nginx/Dockerfile
nginx/DockerfileFROM nginx:1.15.9-alpine EXPOSE 80 RUN apk update && apk upgrade \ && rm -r /var/cache/apk/* COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./conf.d/default.conf /etc/nginx/conf.d/default.conf COPY ./conf.d/php-upstream.conf /etc/nginx/conf.d/php-upstream.confnginx.conf
$ vim nginx/nginx.conf以下を保存
nginx/nginx.conf
nginx/nginx.confuser nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format json_combined escape=json '{ "time_local": "$time_local", ' '"remote_addr": "$remote_addr", ' '"remote_user": "$remote_user", ' '"request": "$request", ' '"status": "$status", ' '"body_bytes_sent": "$body_bytes_sent", ' '"request_time": "$request_time", ' '"http_referrer": "$http_referer", ' '"http_user_agent": "$http_user_agent" }'; access_log /var/log/nginx/access.log json_combined; sendfile on; #tcp_nopush on; keepalive_timeout 65; gzip on; server_tokens off; server_names_hash_max_size 512; include /etc/nginx/conf.d/*.conf; }default.conf
$ vim nginx/conf.d/default.conf以下を保存
nginx/conf.d/default.conf
nginx/conf.d/default.confserver { listen 80 default_server; listen [::]:80 default_server ipv6only=on; root /var/www/public; index index.php index.html index.htm; charset utf-8; location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|svg|mp3)$ { try_files $uri =404; } location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { fastcgi_pass php-upstream; fastcgi_index index.php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HOME /var/www; include fastcgi_params; } }php-upstream.conf
$ vim nginx/conf.d/php-upstream.conf以下を保存
nginx/conf.d/php-upstream.conf
nginx/conf.d/php-upstream.confupstream php-upstream { server php-fpm:9000; }PHP-FPMの設定
Dockerfile(php-fpm)
$ vim php-fpm/Dockerfile以下を保存
php-fpm/Dockerfile
php-fpm/DockerfileFROM php:7.3-fpm-alpine # Install PHP extensions RUN apk update && apk upgrade --update && apk --no-cache add \ git \ vim less \ icu-dev \ autoconf \ make \ g++ \ gcc \ mysql-client \ && rm -r /var/cache/apk/* RUN docker-php-ext-install -j$(nproc) \ iconv \ intl \ mbstring \ pdo_mysql COPY ./php.ini /usr/local/etc/php-fpm.d/my-php.ini COPY ./php-fpm.conf /usr/local/etc/php-fpm.d/my-php-fpm.confphp.ini
$ vim php-fpm/php.ini以下を保存
php-fpm/php.ini
php-fpm/php.ini; timezone date.timezone = Asia/Tokyo ; error reporing log_errors = On error_log = /dev/stderr display_errors = Off ; mbstring mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"php-fpm.conf
$ vim php-fpm/php-fpm.conf以下を保存
php-fpm/php-fpm.conf
php-fpm/php-fpm.conf[www] pm.status_path = /phpfpm_statusdocker-composeの設定
$ cd ~/projects/HogeApp $ vim docker-compose.yml以下を保存
docker-compose.yml
docker-compose.ymlversion: "3" services: nginx: build: ./docker/nginx container_name: "nginx" ports: - "8080:80" restart: always depends_on: - php-fpm php-fpm: build: ./docker/php-fpm container_name: "php-fpm" restart: always volumes: - ./src:/var/www working_dir: /var/www links: - mysql depends_on: - mysql composer: image: composer:1.8 container_name: "composer" volumes: - ./src:/var/www working_dir: /var/www mysql: image: mysql:5.7 container_name: "mysql" ports: - 33306:3306 restart: always volumes: - ./docker/mysql-data:/var/lib/mysql environment: MYSQL_DATABASE: hoge-db MYSQL_USER: hoge-user MYSQL_PASSWORD: hoge-password MYSQL_ROOT_PASSWORD: root TZ: "Asia/Tokyo" command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci以下の環境変数部分はアプリに合わせて変更してください
environment:
MYSQL_DATABASE: hoge-db
MYSQL_USER: hoge-user
MYSQL_PASSWORD: hoge-password
MYSQL_ROOT_PASSWORD: root
ここまでの作成ファイルたちとフォルダ構成
projects └── HogeApp ├── docker │ ├── mysql-data │ ├── nginx │ │ ├── Dockerfile │ │ ├── conf.d │ │ │ ├── default.conf │ │ │ └── php-upstream.conf │ │ └── nginx.conf │ └── php-fpm │ ├── Dockerfile │ ├── php-fpm.conf │ └── php.ini ├── docker-compose.yml └── srcコンテナ起動
$ cd ~/projects/HogeApp $ docker-compose up -dズラズラっとコマンドが流れて最後にこれが出ればOK
Creating composer ... done
Creating mysql ... done
Creating php-fpm ... done
Creating nginx ... done念の為、起動確認
$ docker-compose ps Name Command State Ports --------------------------------------------------------------------------------------- composer /bin/sh /docker-entrypoint ... Exit 0 mysql docker-entrypoint.sh mysql ... Up 0.0.0.0:33306->3306/tcp, 33060/tcp nginx nginx -g daemon off; Up 0.0.0.0:8080->80/tcp php-fpm docker-php-entrypoint php-fpm Up 9000/tcpこの時点では、http://localhost:8080/にアクセスしても
File not found.
となっているはず。Laravelインストール
$ cd ~/projects/HogeApp $ docker-compose run composer global require laravel/installer $ docker-compose run composer create-project --prefer-dist laravel/laravel .まぁまぁ時間かかるので、待ちます・・・
おわったら、srcフォルダにlaravelのファイル郡ができています。もし以下エラーが出たら、別のネットワーク(テザリングとか、会社とかスタバとか)で試してください。
※この記事を見てもらう想定の「某学校」の人たちは絶対エラーなります。
[Composer\Downloader\TransportException] The "https://repo.packagist.org/packages.json" file could not be downloaded: failed to open stream: Network unreachableディレクトリのパーミッション変更
$ chmod -R a+w src/storage/*確認
http://localhost:8080/にアクセスすると
Laravelの設定
.env
ファイルをいじって、設定していきます。$ vim src/.env
src/.env
- APP_NAME=Laravel + APP_NAME=ご自身のアプリの名称に変更 - APP_URL=http://localhost + APP_URL=http://localhost:8080 - DB_HOST=127.0.0.1 + DB_HOST=mysql - DB_DATABASE=homestead - DB_USERNAME=homestead - DB_PASSWORD=secret + DB_DATABASE=hoge-db # ← docker-compose.ymlのenvironmentで設定したやつ + DB_USERNAME=hoge-user + DB_PASSWORD=hoge-password最終的なフォルダ構成
projects └── HogeApp ├── docker │ ├── mysql-data │ ├── nginx │ │ ├── Dockerfile │ │ ├── conf.d │ │ │ ├── default.conf │ │ │ └── php-upstream.conf │ │ └── nginx.conf │ └── php-fpm │ ├── Dockerfile │ ├── php-fpm.conf │ └── php.ini ├── docker-compose.yml └── src ├── app ├── ~省略~ └── vendorあとはローカルのsrcフォルダ配下のLaravelを触っていけば基本的にOKです!
開発時の注意点
コマンドは
docker-compose.yml
ファイルがあるフォルダで実行してくださいcomposerの実行
docker-compose run
をコマンドの頭につけてください例$ docker-compose run composer dump-autoloadartisanの実行
docker-compose exec php-fpm
をコマンドの頭につけてください例$ docker-compose exec php-fpm php artisan route:listコンテナの起動/終了
起動$ docker-compose up -d終了$ docker-compose downコンテナにログインしたいとき
$ docker-compose exec php-fpm /bin/ash最後に
ご自身のアプリに合わせて必要応じてRedisやphpmyadminなどをdockerイメージで追加して、拡張していってください。
- 投稿日: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:39:37+09:00
中間テーブルつくってみた
中間テーブル教えてもらったからメモ。
環境
Laravel
mysqlテーブル
・profilesテーブル
1:1のデータ・businesstypesテーブル
1:nのデータモデル
・profileモデル
・businesstypesモデル
経緯
・profilesテーブルにフォーム情報送信
・今までは1:1のリレーションで動いていた
・フォーム情報の追加作業する
・追加情報の持っている値が文字列ではなく、配列だった
・1:nのリレーションが必要
・中間テーブルつくる必要な知識
関係 親 子 備考 1:1 hasOne belongsTo 1:N hasMany belongsTo N:N belongsToMany belongsToMany 中間テーブル必要 やったこと
1.businesstype_profileテーブル作成
2.profileモデルに追記
3.profileコントローラに追記1.businesstype_profileテーブル作成
Schema::create('businesstype_profile', function (Blueprint $table) { $table->increments('id'); $table->integer('profile_id')->unsigned()->index('profile_id')->comment('プロフィールID'); $table->integer('businesstype_id')->unsigned()->index('businesstype_id')->comment('業種ID'); $table->timestamps(); });※これが中間テーブルになる
カラム
businesstype_profile_id
profile_id
bisinesstype_id
created_at
updated_at2.profileモデルに追記
class Profile extends Model { public function businesstype_profile() { return $this->belongsToMany('App\Models\Businesstype', 'businesstype_profile', 'profile_id', 'businesstype_id')->orderBy('sort')->withTimestamps(); } }・第1引数では最終的な接続先モデルを指定する
・第2引数では中間テーブル名を指定する
・第3引数では接続元モデルIDを示す中間テーブル内のカラムを・指定する
・第4引数では接続先モデルIDを示す中間テーブル内のカラムを指定する3.profileコントローラに追記
$profile->businesstype_profile()->sync([1, 2]);更新したプロフィールに、businesstype_profileテーブルのID1とID2を関連付け
ID1 → profile_id
ID2 → businesstype_id
おまけ
sync()じゃなくて、attach()でもいける。
$profile->businesstype_profile()->attach(3); $profile->businesstype_profile()->attach(4);(3)、(4)はbusinesstype_profile関数の引数をみてる
- 投稿日: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:59:28+09:00
フォーム作ってたらArray to string conversion (0)って言われた
配列から文字列に変換できないよ
直訳しただけだけど、そのまま意味
原因
配列と文字列、両方の値を持っている
変数 値 a 1000 b 2000 c 3000 d 0 => "1000" e 0 => "2000" 解決
abcのように、deも文字列なら受け取れたが、
配列の場合は、1:nの関係になり、
1:1のデータベースでは受け取れないので
中間テーブルを作る。中間テーブルの記事書いた↓
https://qiita.com/kusano00/items/001bdc353136935bf4f4
- 投稿日: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 使ってるので親が居ないので当然。)