LoginSignup
1
1

More than 3 years have passed since last update.

Laravel で Ruby on Rails チュートリアル【14章】

Posted at

環境

  • Laravel 5.5
  • PHP 7.2

この章でやること

14.1 Relationshipモデル

14.1.1 データモデルの問題(および解決策)

(/app/Relationship.php)
php
class Relationship extends Model
{
protected $guarded = ['id'];
}

(/database/migrations/[timestamp]_createrelationships.php)
```php
class CreateRelationships extends Migration
{
public function up()
{
Schema::create('relationships', function (Blueprint $table) {
$table->increments('id');
$table->integer('follower_id');
$table->integer('followed_id');
$table->timestamps();

        $table->index('follower_id');
        $table->index('followed_id');
        $table->unique(['follower_id', 'followed_id']);

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

public function down()
{
    Schema::dropIfExists('relationships');
}

}
```

14.1.2 User/Relationshipの関連付け

(/app/User.php)
php
public function activeRelationships()
{
return $this->hasMany("App\Relationship", "follower_id");
}

(/app/Relationship.php)
```php
public function follower()
{
return $this->belongsTo("App\User", "id", "follower_id");
}

public function followed()
{
    return $this->belongsTo("App\User", "id", "followed_id");
}

# 14.1.3 Relationshipのバリデーション

# 14.1.4 フォローしているユーザー

(/app/User.php)
```php
    public function following()
    {
        return $this->hasManyThrough("App\User", "App\Relationship", "follower_id", "id", "id", "followed_id");
    }

    public function isFollowing($user_id)
    {
        return (bool) Relationship::where("follower_id", $this->id)->where("followed_id", $user_id)->count();
    }

(/tests/Unit/UserTest.php)
php
public function testFollowAndUnfollow()
{
$user_1 = User::find(1);
$user_2 = User::find(2);
$this->assertFalse($user_1->isFollowing($user_2->id));
$user_1->follow($user_2->id);
$this->assertTrue($user_1->isFollowing($user_2->id));
$user_1->unfollow($user_2->id);
$this->assertFalse($user_1->isFollowing($user_2->id));
}

(/app/User.php)
```php
public function follow($user_id)
{
$this->activeRelationships()->create(["followed_id" => $user_id]);
}

public function unfollow($user_id)
{
    $this->activeRelationships()->where("followed_id", $user_id)->delete();
}

public function isFollowed($user_id)
{

    return (bool) Relationship::where("follower_id", $user_id)->where("followed_id", $this->id)->count();
}

# 14.1.5 フォロワー

(/app/User.php)
```php
    public function passiveRelationships()
    {
        return $this->hasMany("App\Relationship", "followed_id");
    }

    public function followers()
    {
        return $this->hasManyThrough("App\User", "App\Relationship", "followed_id", "id", "id", "follower_id");
    }

(/tests/Unit/UserTest.php)
php
$this->assertEquals(1, $user_1->following()->where("followed_id", $user_2->id)->count());

14.2 Follow のWebインターフェイス

14.2.1 フォローのサンプルデータ

(/database/seeds/UsersTableSeeder.php)
php
$user = User::first();
$following = User::where("id", ">=", 2)->where("id", "<=", 50);
$followers = User::where("id", ">=", 3)->where("id", "<=", 40);
$following->each(function ($following) use ($user) { $user->follow($following->id); });
$followers->each(function ($follower) use ($user) { $follower->follow($user->id); });

14.2.2 統計と Follow フォーム

(/routes/web.php)
php
Route::prefix('users')->group(function () {
Route::get('{user}/following', "UsersController@following")->name("following");
Route::get('{user}/followers', "UsersController@followers")->name("followers");
});

(/resources/views/shared/stats.blade.php)
html
@php empty($user) && $user = Auth::user() @endphp
<div class="stats">
<a href="{{ route("following", $user->id) }}">
<strong id="following" class="stat">
{{ $user->following()->count() }}
</strong>
following
</a>
<a href="{{ route("followers", $user->id) }}">
<strong id="followers" class="stat">
{{ $user->followers()->count() }}
</strong>
followers
</a>
</div>

(/resources/views/static_pages/home.blade.php)
html
<section class="stats">
@include ("shared.stats")
</section>

(/routes/web.php)
php
Route::resource('relationships', "RelationshipsController", ["only" => ['store', 'destroy']]);

(/resources/views/users/follow_form.blade.php)
html
@unless ($user == Auth::user())
<div id="follow_form">
@if (Auth::user()->isFollowing($user->id))
@include("users.unfollow")
@else
@include("users.follow")
@endif
</div>
@endunless

(/resources/views/users/follow.blade.php)
html
{{ Form::open(["route" => "relationships.store"]) }}
{{ Form::hidden('followed_id', $user->id) }}
{{ Form::submit("Follow", ["class" => "btn btn-primary"]) }}
{{ Form::close() }}

(/resources/views/users/unfollow.blade.php)
html
{{ Form::open(["route" => ["relationships.destroy", $user->id], "method" => "delete"]) }}
{{ Form::submit("Unfollow", ["class" => "btn"]) }}
{{ Form::close() }}

(/resources/views/users/show.blade.php)
```html
@extends('layouts.application')

@section('title', $user->name)

@section('content')





{!! gravatar_for($user) !!}
{{ $user->name }}




@include("shared.stats")



@includeWhen (Auth::check(), "users.follow_form")
@if ($user->microposts())

Microposts ({{ $user->microposts()->count() }})



    @foreach ($microposts as $micropost)
    @include ("microposts.micropost")
    @endforeach

{{ $microposts->links() }}
@endif


@endsection
```

14.2.3 Following と Followers ページ

(/tests/Unit/UsersControllerTest.php)
```php
public function testRedirectFollowing()
{
$response = $this->get(route("following", 1));
$response->assertRedirect(route("login"));
}

public function testRedirectFollowers()
{
    $response = $this->get(route("followers", 1));
    $response->assertRedirect(route("login"));
}
(/app/Http/Controllers/UsersController.php)
```php
    public function __construct()
    {
        $this->middleware('guest')->only(["index", "edit", "update", "destroy", "following", "followers"]);
    }

    public function following($user)
    {
        $title = "Following";
        $user  = User::find($user);
        $users = $user->following()->paginate(30);
        return view("users.show_follow")->with(["title" => $title, "user" => $user, "users" => $users]);
    }

    public function followers($user)
    {
        $title = "Following";
        $user  = User::find($user);
        $users = $user->followers()->paginate(30);
        return view("users.show_follow")->with(["title" => $title, "user" => $user, "users" => $users]);
    }

(/resources/views/users/show_follow.blade.php)
```html
@extends('layouts.application')

@section('title', $title)

@section('content')




{!! gravatar_for($user) !!}

{{ $user->name }}


{{ Html::linkRoute("users.show", "view my profile", $user->id) }}
Microposts: {{ $user->microposts()->count() }}


@include("shared.stats")
@if ($users)

@endif



{{ $title }}


@if ($users)

{{ $users->links() }}
@endif


@endsection

(/database/seeds/test/TestSeeder.php)
php
Relationship::create(["follower_id" => 1, "followed_id" => 3]);
Relationship::create(["follower_id" => 1, "followed_id" => 4]);
Relationship::create(["follower_id" => 3, "followed_id" => 1]);
Relationship::create(["follower_id" => 2, "followed_id" => 1]);

(/tests/Feature/FollowingTest.php)
php
class FollowingTest extends TestCase
{
private $user;
protected function setUp()
{
    parent::setUp();
    Artisan::call('migrate:refresh');
    $this->seed('TestSeeder');
    $this->user = User::find(1);
    $this->be($this->user);
}

public function testFollowingPage()
{
    $response = $this->get(route("following", $this->user->id));
    $this->assertNotEmpty($this->user->following());
    $response->assertSeeText((string) $this->user->following()->count());
    $dom = $this->dom($response->content());
    foreach ($this->user->following as $following) {
        $this->assertSame(route("users.show", $following->id), $dom->filter("a:contains(\"{$following->name}\")")->attr("href"));
    }
}

public function testFollowersPage()
{
    $response = $this->get(route("followers", $this->user->id));
    $this->assertNotEmpty($this->user->followers());
    $response->assertSeeText((string) $this->user->followers()->count());
    $dom = $this->dom($response->content());
    foreach ($this->user->followers as $follower) {
        $this->assertSame(route("users.show", $follower->id), $dom->filter("a:contains(\"{$follower->name}\")")->attr("href"));
    }
}

}
```

14.2.4 Follow ボタン (基本編)

(/tests/Unit/RelationshipsControllerTest.php)
```php
class RelationshipsControllerTest extends TestCase
{
public function testRedirectCreatePage()
{
$count = Relationship::all()->count();
$response = $this->post(route("relationships.create"));
$this->assertEquals($count, Relationship::all()->count());
$response->assertRedirect(route("login"));
}

public function testRedirectDestroyPage()
{
    $count = Relationship::all()->count();
    $response = $this->delete(route("relationships.destory", 1));
    $this->assertEquals($count, Relationship::all()->count());
    $response->assertRedirect(route("login"));
}

}

(/app/Http/Controllers/RelationshipsController.php)
php
class RelationshipsController extends Controller
{
public function __construct()
{
$this->middleware('authenticate');
}

public function store(Request $request)
{
    Auth::user()->follow($request->followed_id);
    return redirect()->route("users.show", Auth::id());
}

public function destroy($id)
{
    Auth::user()->unfollow($id);
    return redirect()->route("users.show", Auth::id());
}

}
```

14.2.5 Follow ボタン (Ajax編)

14.2.6 フォローをテストする

(/tests/Feature/FollowingTest.php)
```php
class FollowingTest extends TestCase
{
private $user;
private $other_user;

protected function setUp()
{
    parent::setUp();
    Artisan::call('migrate:refresh');
    $this->seed('TestSeeder');
    $this->user = User::find(1);
    $this->other_user = User::find(2);
    $this->be($this->user);
}

public function testFollowUser()

{
    $count = Relationship::all()->count();
    $this->post(route("relationships.store"), ["followed_id" => $this->other_user->id]);
    $this->assertEquals($count + 1, Relationship::all()->count());
}

public function testUnfollowUser()
{
    $this->user->follow($this->other_user->id);
    $count = Relationship::all()->count();
    $this->delete(route("relationships.destroy", $this->other_user->id));
    $this->assertEquals($count - 1, Relationship::all()->count());
}

}
```

14.3 ステータスフィード

14.3.1 動機と計画

(/tests/Unit/UserTest.php)
php
public function testStatusFeed()
{
$user_1 = User::find(1);
$user_2 = User::find(2);
$user_3 = User::find(3);
foreach ($user_3->microposts as $micropost) {
$this->assertTrue($user_1->feed()->get()->contains($micropost));
}
foreach ($user_1->microposts as $micropost) {
$this->assertTrue($user_1->feed()->get()->contains($micropost));
}
foreach ($user_2->microposts as $micropost) {
$this->assertFalse($user_1->feed()->get()->contains($micropost));
}
}

14.3.2 フィードを初めて実装する

(/app/User.php)
php
public function feed()
{
$relations = $this->activeRelationships()->get()->toArray();
$followed_ids = array_pluck($relations, "followed_id");
return Micropost::whereIn("user_id", $followed_ids)->orWhere("user_id", $this->id);
}

(/app/Http/Controllers/StaticPagesController.php)
php
public function home()
{
$feed_items = null;
if (Auth::check()) {
$feed_items = Auth::user()->feed()->paginate(30);
}
return view('static_pages/home')->with("feed_items", $feed_items);
}

14.3.3 サブセレクト

14.4 最後に

14.4.1 サンプルアプリケーションの機能を拡張する

14.4.2 読み物ガイド

14.4.3 本省のまとめ

14.4.4 役者あとがき

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1