20190405のlaravelに関する記事は8件です。

Laravel 5.8 にて["SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint"]エラー

Laravel 5.8 にて Cannot add foreign key constraint が出たときの対応

さきに答え書くと、

型がbigIncrementsorbigIntegerで一致してるか確認してみて〜

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
には英語ですが、そのように記述が。
公式が変更点としてbigIncrementsorbigIntegerを取り上げてなかったことに、この記事の方おかんむりでした。

参考URL
Be Careful: Laravel 5.8 Added bigIncrements As Defaults
【メモ】Laravelで外部キー制約付きのテーブルをマイグレーションする時に気をつけた方がよかった点
laravel5.7 外部キーエラー SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint

Added bigIncrements

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HomesteadでLaravel環境構築(Windows10)

前置き

もう各所で散々書かれている内容だから私が書かなくても…と思ったのですが、

というsuinさんのツイートに背中を押されたので書くことにしました。
七転八倒したポイントを重点的に書けばいいよね。うんうん。

環境

※ 構築したのは2019年1月頃

  • Windows10
  • Oracle VM VirtualBox 6.0
  • vagrant 2.2.2
  • PHP 7.2
  • Laravel 5.7.21

準備

BOX追加

Git Bashまたはコマンドプロンプトを起動して以下を入力

GitBash
cd 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にしました。

GitBash
git clone https://github.com/laravel/homestead.git ~/Homestead

設定の初期化

GitBash
cd ~/Homestead

./init.sh
# Homestead initialized! と表示されたらOK

Homestead.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.apphttps://homestead.app とリダイレクトされるのですが、
Chromeだと自己署名証明書がエラーとなるためアクセスできないためです。

ただ、昨今の情勢としてはSSL通信で動かすことがベターだと思うので、
余裕があれば自己証明が通るよう設定した方がいいかもしれません。
(私はまだやってない……)

SSH鍵ファイル作成

Homestead.yaml の authorize と keys にあたるファイルを作成します

Homestead.yaml
authorize: ~/.ssh/id_rsa.pub # 公開鍵

keys:
    - ~/.ssh/id_rsa # 秘密鍵
GitBash
cd ~

# 鍵が既にあるかどうか
ls -la .ssh

# 無ければ以下を実行

mkdir .ssh
cd .ssh # ~/.ssh へ移動

# 鍵ファイル作成
ssh-keygen -t rsa

# id_rsa.pub と id_rsa が作成されたことを確認する
ls -l

hostsファイル修正

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 up

502 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.conf
http {
# (中略)
        ##
        # 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.test
server {
# (中略)
    access_log off;
    error_log  /var/log/nginx/homestead.test-error.log error;

エラーログの場所が分かったので、そちらを見ます。
(と言いつつ、調査当時のメモによるとなぜか homestead.app-error.log の方を見ている私…)

/var/log/nginx/homestead.app-error.log
2019/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@homestead
sudo 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@homestead
sudo service php7.2-fpm restart
sudo service nginx restart

これで502エラーが解消されました!

No input file specified が表示される

こちら によると
Homestead.yamlsites:to のディレクトリが間違っているかもということで確認しましたが、私の場合その問題ではなさそうです。

vagrant@homestead
ls -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.log
2019/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 の方に出ていたのもそれが原因です。
強制プロビジョニングで解消しました。

GitBash
vagrant up --provision

これで例の初期画面が表示されました。
その後、エラーログも /var/log/nginx/homestead.test-error.log の方に出力されるようになりました。

お疲れ様でした……!

その他参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/Dockerfile
FROM 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.conf

nginx.conf

$ vim nginx/nginx.conf

以下を保存

nginx/nginx.conf
nginx/nginx.conf
user  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.conf
server {
    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.conf
upstream php-upstream {
  server php-fpm:9000;
}

PHP-FPMの設定

Dockerfile(php-fpm)

$ vim php-fpm/Dockerfile

以下を保存

php-fpm/Dockerfile
php-fpm/Dockerfile
FROM 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.conf

php.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_status

docker-composeの設定

$ cd ~/projects/HogeApp
$ vim docker-compose.yml

以下を保存

docker-compose.yml
docker-compose.yml
version: "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/にアクセスすると
image.png

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-autoload

artisanの実行

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イメージで追加して、拡張していってください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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(値):引数の値にコンテンツを変更する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

中間テーブルつくってみた

中間テーブル教えてもらったからメモ。

環境

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_at

2.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関数の引数をみてる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フォーム作ってたら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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メモ: 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 の引数である $closureSomeTraitstatic::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 使ってるので親が居ないので当然。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む