バックエンドとフロントエンドを行き来するWEBプログラマ―のメモ帳

WEBプログラマ―。バックエンドはPHP, MySQL, CentOS系, フロントエンドはJavaScript, jQuery, HTML, CSSで仕事してます。

テーブル名をrenameした後、php artisan migrate:refresh系のコマンドをうったときに出るエラーを迅速に解決するには

php artisan migrate:refreshをすると、
英語のサイトを大量に巡回するハメになるような、エラーが出てくる場合があります。
巡回した方法を試しまくるうちに、これをしておけば、エラーが減るという注意点をまとめました。

チェックするポイント

マイグレーションファイルのdown関数内と、外部キー制約の部分をチェックします。

特に、renameしたときや、カラムをaddしたときのマイグレーションファイルを要チェックします。

例:favorite から favorites に変更した時

例えば、最初はfavoriteだったが、favoritesにするパターンで、
renameのとき、以下のように、downに何も書いてないパターン

<?php
    public function up()
    {
        Schema::rename('favorite', 'favorites');
    }

    public function down()
    {
    }

migrate:refleshなどをすると、テーブルがないですよっというエラーが出る。
そりゃそうですね…。downにロールバック時の処理が書き忘れているから、ちゃんとロールバックできずに終わってしまいます。

down()に処理を書く

downにも名前を戻す処理を書いてあげます。

<?php
    public function down()
    {
        Schema::rename('favorites', 'favorite');
    }

外部キー制約をつけている場合

さらに、favoriteの user_id と photo_id には外部キー制約を付けている場合は、
ここに、外部キーも、付け直す処理を入れてあげます。

<?php
    public function up()
    {
        //old_table
        Schema::table('favorite', function (Blueprint $table) {
            $table->dropForeign(['user_id']);
            $table->dropForeign(['photo_id']);
        });

        Schema::rename('favorite', 'favorites');

        //new_table
        Schema::table('favorites', function (Blueprint $table) {
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('photo_id')->references('id')->on('photos')->onDelete('cascade');
        });
    }

    public function down()
    {

        //new_table
        Schema::table('favorites', function (Blueprint $table) {
            $table->dropForeign(['user_id']);
            $table->dropForeign(['photo_id']);
        });

        Schema::rename('favorites', 'favorite');

        //old_table
        Schema::table('favorite', function (Blueprint $table) {
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('photo_id')->references('id')->on('photos')->onDelete('cascade');
        });
    }

以上でおしまい。
けっこうはまってしまったため、次からはこのポイントに注意していきたいと思います。

リレーションをページネーションするには

リレーションをページネーションするには

f:id:mashiro_ruka:20190627143423p:plain
完成形はこのような感じで、20件ごとに1ページを表示できるようにします。

リレーションからページネーション関数を呼ぶとエラー!?

<?php
    $photos = $user->photos->paginate(20);

上のコードでエラーが出てしまったためメモします。

解決:photosにカッコをつけるだけでOKでした

<?php
    $photos = $user->photos()->paginate(20);

参考サイト

laracasts.com

factoryで流し込んだダミーデータでログイン -> 認証情報が記録と一致しません!?

factoryで流し込んだダミーデータでログインすると「認証情報が記録と一致しません」と出る場合

f:id:mashiro_ruka:20190626202117p:plain

原因

パスワードはハッシュ関数を通してから、入れないとダメだったようです。

f:id:mashiro_ruka:20190626202127p:plain

認証不一致のエラーが出たfactoryはこんな感じです。

<?php
$factory->define(User::class, function (Faker $faker) {
    return [
 ~省略~
        'password' => '12345678', // password
~省略~
    ];
});

ハッシュ関数を通すよう修正

<?php
$factory->define(User::class, function (Faker $faker) {
    return [
~省略~
        'password' => Hash::make('12345678'), // password
~省略~
    ];
});

無事完了!

f:id:mashiro_ruka:20190626202314p:plain

Laravelで、いいねした写真の一覧を表示するには

f:id:mashiro_ruka:20190625011942p:plain
写真素材:
pixabay.com


上のようないいねをした画像を取得して、表示する部分について解説します。

取りたい情報

・写真がいいねされた数
・写真のコメントの数

3つのテーブルを用意

まず、3つのテーブルを使用します。

ユーザーテーブル、写真テーブル、いいねテーブル

カラムは必要なところだけをかくと
●ユーザーテーブル
・id (これが他のテーブルと紐づくuser_id)
・他、nameなどのカラム

●写真テーブル
・id (これが他のテーブルと紐づくphoto_id)
・user_id
・他、titleなどのカラム

●いいねテーブル
・user_id (これが他のテーブルと紐づくuser_id)
・photo_id
※user_idとphoto_idは組み合わせが重複しないように複合キーにする。
マイグレーションファイル一部

<?php
...
Schema::create('skill_user', function (Blueprint $table) {
            $table->unsignedInteger('skill_id');
            $table->unsignedInteger('user_id');
            $table->primary(['skill_id', 'user_id']);//複合キー
...

SQLからデータを取得

例えば、ユーザー1が、いいねをした写真一覧を取得するということなので、
内部結合で表すと

 INNAR JOIN
SELECT photos.id FROM photos INNER JOIN favorites ON photos.id = favorites.photo_id and favorites.user_id = $user_id;

Laravelでは

<?php
        $photos = DB::table('photos')
         ->join('favorites', 'photos.id', '=', 'favorites.photo_id')
         ->where('favorites.user_id', '=', $user_id)
         ->get();

        return view('mypage')->with([
         'photos' => $photos,
         ...
      ]);

となりますが、
これだと、viewの方で、photosをforeachで回したとき、
子要素のphotoでリレーションを使う時にエラーが出ました。
まぁ…配列のデータしか取れてないことが原因なわけですが…

hasManyThroughを使おう

よってそのため、以下のように【hasManyThrough】を使って、3つのテーブルをまたぐリレーションを行います。

<?php
...
class User extends Authenticatable{

    //~省略~

    public function join_favorites_photos()
    {
        return $this->hasManyThrough(
            'App\Photo', //リレーションして取りたいテーブル「photos」
            'App\Favorite', //経由するテーブル「favorites」
            'user_id', //favoritesテーブルをusersテーブルと結ぶための外部キー
            'id', // photosテーブルの外部キー
            null, // usersテーブルのローカルキー
            'photo_id' //favoritesとphotosを結ぶために使うキー
        );
    }
}

リレーション先テーブル「posts」

rolesテーブルのidとmembersテーブルのroles_idを結合
membersテーブルのidとPostsテーブルのmembers_idを結合

bladeでいいねの数とコメントの数を表示する部分を作成

これをすると、2つのテーブルで行うhasManyなどのリレーションと同じように
子要素のリレーションもビューでデータを取得できます。
実際に、いいねの数と、コメントの数をビューで表示してみましょう。

<?php
    // いいねの数
    $photo->favorites->count();
    // コメントの数
    $photo->comments->count();

ビューの部分

<div class="gallery flex border mb-4 p-2">
  @foreach ($photos as $photo)
  <div class="photo border mb-4 p-2">
    <a href="/photo/{{ $photo->id }}">
      <div class="img_cover">
        <img src="/storage/thumbnail_images/{{ $photo->image_name }}" />
      </div>
    </a>
    <div class="flex space-between bottom-imginfo">
      <div>
        <img src="/img/heart-regular.svg" width="20">
        <u><b>{{ $photo->favorites->count() }}</b></u> ←ここでしっかりデータが取得できる
        <img src="/img/comment-dots-regular.svg" width="20">
        <u><b>{{ $photo->comments->count() }}</b></u> ←ここでしっかりデータが取得できる
      </div>
      <div>
        <form method="POST" action="{{ action('FavoriteController@destroy_redirect', ['photo_id' => $photo->id]) }}">
          @csrf
          @if ($page_kind === 'mypage')
          <button type="button" class="btn btn-primary btn-sm">編集</button>
          @endif
          <button type="submit" class="btn btn-danger btn-sm">
            @if ($page_kind === 'favorite')
            解除
            @else
            削除
            @endif
          </button>
        </form>
      </div>
    </div>
  </div>
  @endforeach
</div>

これで完成です。

参考サイト
qiita.com

php-junkie.net

Laravelでテーブル名をリネームするには

このupload_imagesをphotosという名前に変更しようと思って、
探していましたが、具体的な方法がようやく見つかったため、メモしておきます。


f:id:mashiro_ruka:20190621184523p:plain

まずは、リネーム用のマイグレーションファイルを新たに作成します。

[vagrant@localhost nekopic_website]$ php artisan make:migration rename_upload_images_to_photos_table
Created Migration: 2019_06_20_145539_rename_upload_images_to_photos_table
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class RenameUploadImagesToPhotosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
       //ここをリネーム処理に ←が元の名前、→がリネーム後
        Schema::rename('upload_images', 'photos');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('photos', function (Blueprint $table) {
            //
        });
    }
}

最後にマイグレーションを反映させる。

[vagrant@localhost nekopic_website]$ php artisan migrate
Migrating: 2019_06_20_145539_rename_upload_images_to_photos_table
Migrated:  2019_06_20_145539_rename_upload_images_to_photos_table

リネームが完了しました。

f:id:mashiro_ruka:20190621184536p:plain



参考サイト:
busy.org

Laravelでの開発時に個人的にAtomやコマンドラインツールのおすすめの配置

 

まずはAtomの配置について

Atomを三分割して、

左にphpファイル、中央にbladeファイル、右にcssjavascriptファイルを

配置するします。

f:id:mashiro_ruka:20190619142817p:plain

Atomでファイルの種類ごとに3分割で配置

 

いろんな種類のファイルがあるため、ごちゃごちゃしにくくて、なにより探しやすかったです。

例えば、さっき編集してた〇〇.phpはどこだっけな…というときは、PHPファイルは左に全部あるので、左を探すだけでいい。というような感じです。

 

コマンドラインツール

私はPuttyを使っているのですが、

これも3つ画面を出しておきます。

f:id:mashiro_ruka:20190619143144p:plain

1つはMySQLにログインしておき、

2つ目はビルドインウェブサーバ(php artisan serve)用のウィンドウにします。

最後に、php artisanでモデルやマイグレーションいしたり、gitコマンドなどを使うためのウィンドウを配置します。

 

以上になります。

個人的にけっこうこのスタイルはプログラミングしやすいな~と思います。

 

 

 

 

Laravelでお気に入り機能を追加しよう

お気に入り機能のために、お気に入りテーブルfavoriteを作ります。

投稿した写真をお気に入りする機能には、
「お気に入りテーブル」に以下の情報が必要です。
・ユーザーテーブルのid
・投稿した写真のid

つまり、どのユーザーの、どの写真か?という情報でDBから抽出する必要があります。

ここで、もし、ユーザーが退会したときや、写真が削除された場合を考えてみましょう。

その際、「お気に入りテーブル」に、お気に入りした写真のidやユーザーidが、残り続けることになります。
これを防ぐために、外部キー制約を使って、
userや投稿写真が削除されたとき、自動的に「お気に入りテーブル」内のそれに関連したユーザーID、写真IDも全て削除しておきます。

LaravelにはonDelete('cascade');という機能があり、これで外部キー制約をつけます。

現在のテーブル構成は以下になります。
・upload_images - 投稿した写真
・users - 登録ユーザー
・favorite - お気に入りした投稿した写真のidを格納

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFavoriteTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('favorite', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id');
            $table->integer('photo_id');
            $table->timestamps();

            // userが削除されたとき、関係しているお気に入りも全て削除
            $table->foreign('user_id')
                  ->references('id')
                  ->on('users')
                  ->onDelete('cascade'); 

            // upload_imagesの写真(photo)が削除されたとき、関係しているお気に入りも全て削除
            $table->foreign('photo_id')
                  ->references('id')
                  ->on('upload_images')
                  ->onDelete('cascade'); 
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('favorite');
    }
}