課題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
