This post is Private. Only a writer or those who know its URL can access this post.

「実践」Osaka Laravel Hands-on 2019.09.14

Osaka Laravel Hands-on 2019.09.14

今日のハンズオンの内容

  • Migration
  • Seeder
  • Auth認証ログイン
  • ツイート投稿
  • ツイート一覧

概要

今回はログインしてユーザが投稿したツイートを一覧する機能とツイートを投稿する機能まで。
もし物足りないという人は僕の記事でフォロワーだったりイイネ機能を実装したりしている記事があるので、そちらを試してみてください。

【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)

Migration

コマンドラインでファイルを生成する

Migrationを作ると同時にModelも作成したいので以下のコマンドを実行する

tweetsテーブル

php artisan make:model Tweet -m

usersのMigrationは標準で用意されているのでそのまま使用します。

Migrationファイルを編集する

2019_08_11_082006_create_tweets_table.php
<?php

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

class CreateTweetsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tweets', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedBigInteger('user_id')->comment('ユーザID');
            $table->string('text')->comment('本文');
            $table->softDeletes();
            $table->timestamps();

            $table->index('id');
            $table->index('user_id');
            $table->index('text');

            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');
        });
    }

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

この部分でusersテーブルと外部キー接続を宣言しています。

2019_08_11_082006_create_tweets_table.php
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');

usersテーブルは今回そのまま編集せずに使用します。

Seeder

UserTableSeeder

10ユーザを登録します。

database/seeds/UserTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use App\User;

class UserTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10; $i++) {
            User::create([
                'name'           => 'TEST' .$i,
                'email'          => 'test' .$i .'@test.com',
                'password'       => Hash::make('12345678'),
                'remember_token' => str_random(10),
                'created_at'     => now(),
                'updated_at'     => now()
            ]);
        }
    }
}

TweeTableSeeder

ツイート投稿も各ユーザー毎に1件登録しておきます。

database/seeds/TweetTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use App\Tweet;

class TweetTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10; $i++) {
            Tweet::create([
                'user_id'    => $i,
                'text'       => 'これはテスト投稿' .$i,
                'created_at' => now(),
                'updated_at' => now()
            ]);
        }
    }
}

DatabaseSeeder

database/seeds/TweetTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call([
            UserTableSeeder::class,
            TweetTableSeeder::class
        ]);
    }
}

Migration&Seeder同時に実行!

プロジェクトフォルダで以下のコマンドを実行

一度ミスした人はリセットしてください

php artisan migrate:fresh
php artisan migrate --seed

Homesteadの方はデータがが入ってるか確認してください

mysql
mysql> use homestead
mysql> select * from users;
mysql> select * from tweets;
mysql> exit

Auth認証

それではログイン認証を作っていきます。
以下のコマンドを実行してください。

php artisan make:auth

終わりです(笑)

ログインを確認してみる

先ほどのHOME画面に戻ってみましょう。

右上にLOGIONREGISTERが追加されている

スクリーンショット 2019-08-17 17.54.06.png

あとはLoginとRegisterを試してみてください!

ユーザ一覧表示

ユーザ一覧表示機能を作りたいのでまずはControllerファイルを生成したいと思います。

UsersController

php artisan make:controller UsersController --resource

TweetsController

php artisan make:controller TweetsController --resource

View

次にViewファイルを生成します。
生成するファイルはそれぞれのディレクトリとユーザ一覧表示/ツイート一覧表示&作成です。

mkdir resources/views/users
mkdir resources/views/tweets
touch resources/views/users/index.blade.php
touch resources/views/tweets/index.blade.php
touch resources/views/tweets/create.blade.php

Routing

LaravelではControllerのアクション(メソッド)に紐づける際はroutesweb.phpに記述します。

routes/web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::redirect('/', '/login', 301);

Route::get('/login', function () {
    return view('login');
});

Auth::routes();

// ログイン状態
Route::group(['middleware' => 'auth'], function() {

    // ユーザ関連
    Route::resource('users', 'UsersController');

    // ツイート関連
    Route::resource('tweets', 'TweetsController');
});

ユーザ一覧表示

まずはメソッドから作成します。

Model

引数で受け取ったログインしているユーザを除くユーザを1ページにつき5名取得しています。

app/User.php
    public function getAllUsers(Int $user_id)
    {
        return $this->Where('id', '<>', $user_id)->paginate(5);
    }

Controller

先ほど作成したgetAllUsers()メソッドにログインしているユーザIDを引数で渡しています。
Modelから返ってきた結果をViewに返します。

app/Http/Controllers/UsersController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;

class UsersController extends Controller
{
    public function index(User $user)
    {
        $all_users = $user->getAllUsers(auth()->user()->id);

        return view('users.index', [
            'all_users'  => $all_users
        ]);
    }

    // 省略

}

View

ユーザの一覧表示(ここはコピペしてください)

resources/views/users/index.blade.php
@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                @foreach ($all_users as $user)
                    <div class="card">
                        <div class="card-haeder p-3 w-100 d-flex">
                            <img src="https://placehold.jp/50x50.png" class="rounded-circle" width="50" height="50">
                            <div class="ml-2 d-flex flex-column">
                                <p class="mb-0">{{ $user->name }}</p>
                            </div>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
        <div class="my-4 d-flex justify-content-center">
            {{ $all_users->links() }}
        </div>
    </div>
@endsection

ユーザ一覧表示画面

URLの後ろに/usersと入力すると画面が表示される。

スクリーンショット 2019-09-13 13.47.32.png

ツイート一覧表示

Model

ここではusersと1対多の関係を宣言し、そのユーザ情報を使って登録しているユーザのツイートを全件取得してみます。

app/Tweet.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\softDeletes;

class Tweet extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'text'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function getTimeLines()
    {
        return $this->with('user')->orderBy('tweets.created_at', 'DESC')->paginate(5);
    }
}

Controller

app/Http/Controllers/TweetsController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Tweet;

class TweetsController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Tweet $tweet)
    {
        $timelines = $tweet->getTimeLines();

        return view('tweets.index', [
            'timelines' => $timelines
        ]);
    }

    // 省略

}

View

ツイートの一覧表示(ここはコピペしてください)

resources/views/tweets/index.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8 mb-3 text-right">
            <a href="{{ url('users') }}">ユーザ一覧</a>
        </div>
        @if (isset($timelines))
            @foreach ($timelines as $timeline)
                <div class="col-md-8 mb-3">
                    <div class="card">
                        <div class="card-haeder p-3 w-100 d-flex">
                            <img src="https://placehold.jp/50x50.png" class="rounded-circle" width="50" height="50">
                            <div class="ml-2 d-flex flex-column">
                                <p class="mb-0">{{ $timeline->user->name }}</p>
                            </div>
                            <div class="d-flex justify-content-end flex-grow-1">
                                <p class="mb-0 text-secondary">{{ $timeline->created_at->format('Y-m-d H:i') }}</p>
                            </div>
                        </div>
                        <div class="card-body">
                            {!! nl2br(e($timeline->text)) !!}
                        </div>
                    </div>
                </div>
            @endforeach
        @endif
    </div>
    <div class="my-4 d-flex justify-content-center">
        {{ $timelines->links() }}
    </div>
</div>
@endsection

ツイート一覧表示画面

スクリーンショット 2019-09-13 14.13.21.png

ツイート投稿画面

ログインしているユーザーをviewに渡します

Controller

app/Http/Controllers/TweetsController.php
    // 省略

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $user = auth()->user();

        return view('tweets.create', [
            'user' => $user
        ]);
    }

    // 省略

View

ツイート投稿画面(ここはコピペしてください)

resources/views/tweets/create.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Create</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('tweets.store') }}">
                        @csrf

                        <div class="form-group row mb-0">
                            <div class="col-md-12 p-3 w-100 d-flex">
                                <img src="https://placehold.jp/50x50.png" class="rounded-circle" width="50" height="50">
                                <div class="ml-2 d-flex flex-column">
                                    <p class="mb-0">{{ $user->name }}</p>
                                </div>
                            </div>
                            <div class="col-md-12">
                                <textarea class="form-control @error('text') is-invalid @enderror" name="text" required autocomplete="text" rows="4">{{ old('text') }}</textarea>

                                @error('text')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-12 text-right">
                                <p class="mb-4 text-danger">140文字以内</p>
                                <button type="submit" class="btn btn-primary">
                                    ツイートする
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ツイート投稿画面

スクリーンショット 2019-09-13 21.17.31.png

ツイート投稿機能

Controllerから与えられたデータをDBに保存します。

Model

app/Tweet.php
    public function tweetStore(Int $user_id, Array $data)
    {
        $this->user_id = $user_id;
        $this->text = $data['text'];
        $this->save();

        return;
    }

Controller

POSTで受け取ったデータをバリデーションにかけて140文字以内ならModelを呼び出します

app/Http/Controllers/TweetsController.php
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request, Tweet $tweet)
    {
        $user = auth()->user();
        $data = $request->all();
        $validator = Validator::make($data, [
            'text' => ['required', 'string', 'max:140']
        ]);

        $validator->validate();
        $tweet->tweetStore($user->id, $data);

        return redirect('tweets');
    }

終わり!!!

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.