課題2021-01

次のサイトを参考に、同等の機能を有するシステムを開発する。

http://5842-180-6-177-190.jp.ngrok.io

システム概要

  • 書籍の管理とレビューの共有を目的とするシステム
  • ユーザは書籍情報を自由に登録できる。自身が登録した書籍はMy Booksとして管理され、編集・削除が行える
  • 他ユーザが登録した書籍情報も検索・閲覧することができる。ただし、自身が登録した書籍以外の編集・削除は行えない
  • ユーザは本棚を自由に作成することができ、本棚には任意の書籍を入れることができる。自身が作成した本棚はMy Shelvesとして管理され、編集・削除が行える
  • 書籍は必ず1つのジャンルに属する。書籍検索ではキーワード検索(タイトルと概要の部分一致)と併せてジャンルでの絞り込みも行える。必要なジャンルは後述のジャンル一覧を参照
  • 書籍にはレビューを書くことができ、コメントと評価点(スコア)をつけることができる。レビューはすべてのユーザが閲覧できる。一度書いたレビューは消すことができない
  • 書籍一覧や詳細では、レビューの評価点(スコア)の平均値を表示する

書籍情報

  • ジャンル
  • タイトル
  • 概要

本棚情報

  • 名前

書籍ジャンル一覧

  • 文学・評論
  • ノンフィクション
  • ビジネス・経済
  • 歴史・地理
  • 政治・社会
  • 芸能・エンタメ
  • アート・建築・デザイン
  • 人文・思想・宗教
  • 暮らし・健康・料理
  • サイエンス・テクノロジー
  • 趣味・実用
  • 教育・自己啓発
  • スポーツ・アウトドア
  • 辞典・年鑑・本・ことば
  • 音楽
  • 旅行・紀行
  • 絵本・児童書
  • コミック

ER図

解答例

マイグレーション

<?php

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

class CreateShelvesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('shelves', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('user_id');
            $table->string('name', 255);
            $table->timestamps();
        });
    }

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

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

class CreateItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('user_id');
            $table->integer('genre_id');
            $table->string('name', 255);
            $table->text('outline');
            $table->timestamps();
        });
    }

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

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

class CreateGenresTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('genres', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name', 255);
            $table->integer('order');
            $table->timestamps();
        });
    }

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

シーダー

<?php

use App\Genre;
use Illuminate\Database\Seeder;

class GenresSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $genres = [
            '文学・評論',
            'ノンフィクション',
            'ビジネス・経済',
            '歴史・地理',
            '政治・社会',
            '芸能・エンタメ',
            'アート・建築・デザイン',
            '人文・思想・宗教',
            '暮らし・健康・料理',
            'サイエンス・テクノロジー',
            '趣味・実用',
            '教育・自己啓発',
            'スポーツ・アウトドア',
            '辞典・年鑑・本・ことば',
            '音楽',
            '旅行・紀行',
            '絵本・児童書',
            'コミック'
        ];
        
        foreach($genres as $index => $name) {
            $genre = new Genre;
            $genre->name = $name;
            $genre->order = $index + 1;
            $genre->save();
        }
    }
}

ルーティング

// 棚の一覧画面
Route::get('/shelves', 'ShelvesController@index');

// 棚の登録画面
Route::get('/shelves/create', 'ShelvesController@create');
// 棚の登録処理
Route::post('/shelves', 'ShelvesController@store');

// 棚の編集画面
Route::get('/shelves/{id}/edit', 'ShelvesController@edit');
// 棚の更新処理
Route::post('/shelves/{id}', 'ShelvesController@update');

// 棚の削除処理
Route::delete('/shelves/{id}', 'ShelvesController@destroy');

// アイテム(書籍)の登録画面
Route::get('/items/create', 'ItemsController@create');
// アイテム(書籍)の登録処理
Route::post('/items', 'ItemsController@store');

コントローラ

<?php

namespace App\Http\Controllers;

use App\Shelf;
use App\Http\Requests\ShelfRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ShelvesController extends Controller
{
    public function __construct() {
        $this->middleware('auth');
    }

    /**
     * 棚の一覧画面
     */
    public function index() {
        $shelves = Shelf::get();
        return view('shelves.index', [
            'shelves' => $shelves
        ]);
    }
    
    /**
     * 棚の登録画面
     */
    public function create() {
        return view('shelves.create');
    }

    /**
     * 棚の登録処理
     */
    public function store(ShelfRequest $request) {
        $userId = Auth::user()->id;
        $name = $request->name;

        $shelf = new Shelf;
        $shelf->user_id = $userId;
        $shelf->name = $name;
        $shelf->save();

        return redirect('/shelves');
    }

    /**
     * 棚の編集画面
     */
    public function edit($id) {
        $shelf = Shelf::find($id);

        return view('shelves.edit', [
            'shelf' => $shelf
        ]);
    }

    /**
     * 棚の更新処理
     */
    public function update(ShelfRequest $request, $id) {
        $name = $request->name;

        $shelf = Shelf::find($id);
        $shelf->name = $name;
        $shelf->save();

        return redirect('/shelves');
    }

    /**
     * 棚の削除処理
     */
    public function destroy ($id) {
        $shelf = Shelf::find($id);
        $shelf->delete();

        return redirect('/shelves');
    }
}
<?php

namespace App\Http\Controllers;

use App\Genre;
use App\Item;
use App\Http\Requests\ItemRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ItemsController extends Controller
{
    public function __construct() {
        $this->middleware('auth');
    }

    /**
     * アイテム(書籍)の登録画面
     */
    public function create() {
        $genres = Genre::get();

        return view('items.create', [
            'genres' => $genres
        ]);
    }

    /**
     * アイテム(書籍)の登録処理
     */
    public function store(ItemRequest $request) {
        $user_id = Auth::user()->id;

        $item = new Item;
        $item->user_id = $user_id;
        $item->genre_id = $request->genre_id;
        $item->name = $request->name;
        $item->outline = $request->outline;
        $item->save();

        return redirect('/items');
    }
}

リクエスト

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ShelfRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|max:255'
        ];
    }
}
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'genre_id' => 'required|integer',
            'name' => 'required|max:255',
            'outline' => 'required|max:10000'
        ];
    }
}

モデル

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Shelf extends Model
{
    //
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
    //
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Genre extends Model
{
    //
}

ビュー

@extends('layouts.app')

@section('content')
<div class="container">
    <a href="/shelves/create" class="btn btn-primary">新規登録</a>
    <table class="table mt-3">
        <thead>
            <tr>
                <th>
                    棚名
                </th>
                <th>
                    操作
                </th>
            </tr>
        </thead>
        <tbody>
            @foreach($shelves as $shelf)
            <tr>
                <td>
                    {{ $shelf->name }}
                </td>
                <td>
                    <a href="/shelves/{{ $shelf->id }}/edit" class="btn btn-primary">編集</a>
                    <form method="POST" action="/shelves/{{ $shelf->id }}" class="d-inline-block">
                        @method('delete')
                        @csrf
                        <input type="submit" value="削除" class="btn btn-secondary">
                    </form>
                </td>
            </tr>
            @endforeach
        </tbody>
    </table>
</div>
@endsection
@extends('layouts.app')

@section('content')
<div class="container">
    <a href="/shelves">戻る</a>
    @if($errors->any())
    <ul>
        @foreach($errors->all() as $error)
        <li>{{ $error }}</li>
        @endforeach
    </ul>
    @endif
    <form method="POST" action="/shelves">
        @csrf
        <p>
            本棚名<br>
            <input type="text" name="name" value="{{ old('name') }}">
        </p>
        <p>
            <input type="submit" value="登録">
        </p>
    </form>
</div>
@endsection
@extends('layouts.app')

@section('content')
<div class="container">
    <a href="/shelves">戻る</a>
    @if($errors->any())
    <ul>
        @foreach($errors->all() as $error)
        <li>{{ $error }}</li>
        @endforeach
    </ul>
    @endif
    <form method="POST" action="/shelves/{{ $shelf->id }}">
        @csrf
        <p>
            本棚名<br>
            <input type="text" name="name" value="{{ old('name', $shelf->name) }}">
        </p>
        <p>
            <input type="submit" value="更新">
        </p>
    </form>
</div>
@endsection
@extends('layouts.app')

@section('content')
<div class="container">
    <a href="/shelves">戻る</a>
    @if($errors->any())
    <ul>
        @foreach($errors->all() as $error)
        <li>{{ $error }}</li>
        @endforeach
    </ul>
    @endif
    <form method="POST" action="/items">
        @csrf
        <p>
            ジャンル<br>
            <select name="genre_id">
                <option value=""></option>
                @foreach($genres as $genre)
                <option value="{{ $genre->id }}" {{ old('genre_id') == $genre->id ? 'selected' : '' }}>{{ $genre->name }}</option>
                @endforeach
            </select>
        </p>
        <p>
            タイトル<br>
            <input type="text" name="name" value="{{ old('name') }}">
        </p>
        <p>
            概要<br>
            <textarea name="outline">{{ old('outline') }}</textarea>
        </p>
        <p>
            <input type="submit" value="登録">
        </p>
    </form>
</div>
@endsection

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です