Open TM4423 opened 2 years ago
DBの準備(MariaDB)
sudo yum list installed | grep mariadb
sudo amazon-linux-extras install mariadb10.5 -y
sudo systemctl start mariadb
sudo mysql_secure_installation
sudo systemctl enable mariadb
sudo systemctl is-enabled mariadb
composerのインストール
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/bin/composer
composer
※ composerが最新版でない場合は下記を実行(今回は不要)
sudo composer update
Laravelプロジェクトの作成
composer create-project laravel/laravel cms
cd cms
php artisan serve --port=8080
sudo yum install php-cli php-common php-devel php-fpm php-gd php-mysqlnd php-mbstring php-pdo php-xml
sudo composer update
php artisan key:generate
phpMyAdminのインストール
新しいターミナル「New Terminal」を開く
cd cms/public
wget https://files.phpmyadmin.net/phpMyAdmin/5.1.2/phpMyAdmin-5.1.2-all-languages.zip
unzip phpMyAdmin-5.1.2-all-languages.zip
cd ..
phpMyAdminにアクセス
/phpMyAdmin/index.php
ターミナルでDB作成する場合
mysql -u root -p
root [Enterキー]
create database c9;
show databases;
exit;
phpMyAdminで作成する場合
phpMyAdminの管理画面で c9 というDBを作成
※照合順はutf8mb4_general_ciを選択
(授業では、utf8mb4_unicode_ciだった)
DBの作成&Laravelと接続
隠しファイルを表示
.envファイルを編集
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=c9
DB_USERNAME=root
DB_PASSWORD=root
Webサーバーを再起動
^9
php artisan serve --port
Laravelの初期設定(HTTPS設定)
/app/Providers/AppServiceProvider.php ファイルを修正
// 冒頭6行目あたり
use Illuminate\Support\Facades\URL; //この行を追加
// boot(){}内に追加
public function boot() {
URL::forceScheme('https'); //この行を追加
}
php artisan migrate
composer require laravel/ui
php artisan ui vue --auth
npm install
2回
npm run dev
npm run dev
CRUD実装(復習) 場所
php artisan make:model Book -r -m
コントローラー Http/Controllers/BookController.php モデル app/Models/Book.php マイグレーション database/migrations/
マイグレーションファイルを編集 database/migrationsにあるbooks_table.phpを編集
// この該当箇所を変更
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('item_name');
$table->integer('item_number');
$table->integer('item_amount');
$table->date('published');
$table->timestamps();
});
}
これで、bookテーブルの設計図ができた。
php artisan migrate
booksテーブルが作成されているか確認(重要) phpMyAdmin
/resources/views/
@extends('layouts.app')
@section('content')
<!-- Bootstrapの定形コード… -->
<div class="card-body">
<div class="card-title">
ブックマーク
</div>
<!-- ↓バリデーションエラーの表示に使用-->
@include('common.errors')
<!-- ↑バリデーションエラーの表示に使用-->
<!-- 本登録フォーム -->
<form action="{{ url('books') }}" method="POST" class="form-horizontal">
@csrf
<!-- 本のタイトル -->
<div class="form-group col-md-6 p-2">
<label for="item_name" class="col-sm-3 control-label">タイトル</label>
<input type="text" name="item_name" class="form-control" id="item_name" value="{{ old('item_name') }}">
</div>
<!-- 冊数 -->
<div class="form-group col-md-6 p-2">
<label for="item_number" class="col-sm-3 control-label">冊数</label>
<input type="text" name="item_number" class="form-control" id="item_number" value="{{ old('item_number') }}">
</div>
<!-- 金額 -->
<div class="form-group col-md-6 p-2">
<label for="item_amount" class="col-sm-3 control-label">金額</label>
<input type="text" name="item_amount" class="form-control" id="item_amount" value="{{ old('item_amount') }}">
</div>
<!-- 公開日 -->
<div class="form-group col-md-6 p-2">
<label for="published" class="col-sm-3 control-label">公開日</label>
<input type="date" name="published" class="form-control" id="published" value="{{ old('published') }}">
</div>
<!-- 本 登録ボタン -->
<div class="form-group p-2">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</form>
</div>
<!-- Book: 既に登録されてる本のリスト -->
@if (count($books) > 0)
<div class="card-body">
<table class="table table-striped task-table">
<!-- テーブルヘッダ -->
<thead>
<th>本一覧</th>
<th> </th>
</thead>
<!-- テーブル本体 -->
<tbody>
@foreach ($books as $book)
<tr>
<!-- 本タイトル -->
<td class="table-text">
<div>{{ $book->item_name }}</div>
</td>
<!-- 本: 削除ボタン -->
<td>
<form action="{{ url('book/'.$book->id) }}" method="POST">
@csrf
@method('delete')
<button type="submit" class="btn btn-danger">
削除
</button>
</form>
</td>
<!-- 本: 更新ボタン -->
<td>
<a href="{{ url('booksedit/'.$book->id) }}">
<button type="submit" class="btn btn-primary">更新</button>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@endsection
@extends('layouts.app')
@section('content')
<div class="row container">
<div class="col-md-12">
<!-- ↓バリデーションエラーの表示に使用-->
@include('common.errors')
<!-- ↑バリデーションエラーの表示に使用-->
<form action="{{ url('books/update') }}" method="POST">
<!-- item_name -->
<div class="form-group p-2">
<label for="item_name">タイトル</label>
<input type="text" name="item_name" class="form-control" id="item_name" value="{{$book->item_name}}">
</div>
<!--/ item_name -->
<!-- item_number -->
<div class="form-group p-2">
<label for="item_number">冊数</label>
<input type="text" name="item_number" class="form-control" id="item_number" value="{{$book->item_number}}">
</div>
<!--/ item_number -->
<!-- item_amount -->
<div class="form-group p-2">
<label for="item_amount">金額</label>
<input type="text" name="item_amount" class="form-control" id="item_amount" value="{{$book->item_amount}}">
</div>
<!--/ item_amount -->
<!-- published -->
<div class="form-group p-2">
<label for="published">公開日</label>
<input type="date" name="published" class="form-control" id="published" value="{{$book->published}}">
</div>
<!--/ published -->
<!-- Save ボタン/Back ボタン -->
<div class="form-group p-2">
<div class="well well-sm">
<button type="submit" class="btn btn-primary">Save</button>
<a class="btn btn-link pull-right" href="{{ url('/') }}"> Back</a>
</div>
</div>
<!--/ Save ボタン/Back ボタン -->
<!-- id 値を送信 -->
<input type="hidden" name="id" value="{{$book->id}}" />
<!--/ id 値を送信 -->
<!-- CSRF -->
@csrf
<!--/ CSRF -->
</form>
</div>
</div>
@endsection
@if (count($errors) > 0)
<!-- Form Error List -->
<div class="alert alert-danger">
<div><strong>入力した文字を修正してください。</strong></div>
<div>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
@endif
(🚨今回はコントローラ名をBooksControllerからBookControllerに変更しています)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Book;
use Validator;
class BookController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$books = Book::orderBy('created_at', 'asc')->get();
return view('books', ['books' => $books]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
// バリデーション
$validator = Validator::make($request->all(), [
'item_name' => 'required | min:3 | max:255',
'item_number' => 'required | min:1 | max:3',
'item_amount' => 'required | max:6',
'published' => 'required',
]);
// バリデーション:エラー時の処理
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
// 登録処理
$book = new Book;
$book->item_name = $request->item_name;
$book->item_number = $request->item_number;
$book->item_amount = $request->item_amount;
$book->published = $request->published;
$book->save();
return redirect('/');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(Book $book)
{
return view('booksedit', ['book' => $book]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
// バリデーション
$validator = Validator::make($request->all(), [
'id' => 'required', // storeに対しての追加分
'item_name' => 'required|min:3|max:255',
'item_number' => 'required|min:1|max:3',
'item_amount' => 'required|max:6',
'published' => 'required',
]);
// バリデーション:エラー
if ($validator->fails()) {
return redirect('/booksedit/'.$request->id)
->withInput()
->withErrors($validator);
}
$book = Book::find($request->id);
$book->item_name = $request->item_name;
$book->item_number = $request->item_number;
$book->item_amount = $request->item_amount;
$book->published = $request->published;
$book->save();
return redirect('/');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Book $book)
{
$book->delete();
return redirect('/');
}
}
冒頭部分に追加
use App\Http\Controllers\BookController;
ここは削除↓
Route::get('/', function () {
return view('welcome');
});
以下も追加 (🚨 ’/' のルートは元のコードに存在します↑。そこだけ削除してから以下コピペ)
// 本のダッシュボード表示
Route::get('/', [BookController::class, 'index']);
// 新「本」を追加
Route::post('/books', [BookController::class, 'store']);
//「本」の更新画面表示
Route::get('/booksedit/{book}',[BookController::class, 'edit']);
//「本」の更新処理
Route::post('books/update',[BookController::class, 'update']);
// 本を削除
Route::delete('/book/{book}', [BookController::class, 'destroy']);
web.php
Route::group(['middleware' => 'auth'], function () {
//
// この中に認証済み後のルート定義を入れる
//
});
【回答例】
// 本のダッシュボード表示
Route::get('/', [BookController::class, 'index']);
Route::group(['middleware' => 'auth'], function(){
// 新「本」を追加
Route::post('/books', [BookController::class, 'store']);
//「本」の更新画面表示
Route::get('/booksedit/{book}',[BookController::class, 'edit']);
//「本」の更新処理
Route::post('books/update',[BookController::class, 'update']);
// 本を削除
Route::delete('/book/{book}', [BookController::class, 'destroy']);
});
ユーザ登録して、本を登録してみる
💡 ['middleware' => 'auth']ってなにしてる?
簡単に言えば、入国審査(や出国審査)のような、門番の役割
コントローラーの処理を実行する前(やした後)に何かをチェックしてくれる機能
例えば「ある特定のユーザーにしかできない機能(role)」「特定のIPアドレスからだけ許可」「スマホ専用ページへリダイレクト」など、リクエストを送ったユーザーの状況に応じて門番が振り分けてくれる役割などがある
ルートグループは多くのルートで共通なもの(ミドルウェアや名前空間のようなルート属性)を一括して適用してくれる便利なルート定義の方法
例えば、管理者と利用者のサイトが分かれていて、ルート定義が多くなってきたときに
Route::prefix('admin')->group(function(){
// admin用のルート定義がここに入る
}
と書くことで、https://xxxxx.com/admin/yyyyのように、admin/以下のルート定義をgroup内に書くことができるようになる
認証済みユーザ情報を使って、表示・登録などの処理をユーザごとに切り替えましょう
❓ 【お題(要件)】ダッシュボード(books.blade.php)
「ある一冊の本のデータ(1レコードと呼びます)を誰が登録したか」を判別するためには、「その本を登録したユーザのid」が必要になります。
ここではbooksテーブルに user_id
のカラムを新たに追加しましょう。
マイグレーションファイルを編集し、user_idを追加する
books_table
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id'); //ここを追加
$table->string('item_name');
$table->integer('item_number');
$table->integer('item_amount');
$table->date('published');
$table->timestamps();
});
}
php artisan migrate:refresh
booksテーブルが更新されているかphpMyAdminで確認
登録処理(store)で、ログインユーザの user_id
も保存するように変更
BookController.php
use Auth; // 冒頭付近に追加
// storeメソッドを編集
public function store(Request $request)
{
// 前略
$book = new Book;
$book->user_id = Auth::id(); // ここを追加
$book->item_name = $request->item_name;
// 後略
認証しているユーザの情報を入手するには、
Auth::user()
だけで取得できてしまう
Auth::id()
で、ログインしているユーザのidを取得できる
表示の出しわけをする
books.blade.php
登録フォームを認証ありのときのみ表示 (L13~L46あたり)
formタグを @if(Auth::check() ~ @enfif
で囲う
<!-- 本登録フォーム -->
@if(Auth::check())
<form action="{{ url('books') }}" method="POST" class="form-horizontal">
<!-- 省略 -->
</form>
@endif
削除・更新ボタンを登録したユーザのみ表示 formタグおよびaタグを @if($book->user_id === Auth::id()) ~ @endifで囲う
<!-- 本: 削除ボタン -->
<td>
@if($book->user_id === Auth::id())
<form action="{{ url('book/'.$book->id) }}" method="POST">
@csrf
@method('delete')
<button type="submit" class="btn btn-danger">
削除
</button>
</form>
@endif
</td>
<!-- 本: 更新ボタン -->
<td>
@if($book->user_id === Auth::id())
<a href="{{ url('booksedit/'.$book->id) }}">
<button type="submit" class="btn btn-primary">更新</button>
</a>
@endif
</td>
BookController.php
public function edit(Book $book){
if($book->user_id === Auth::id()){
return view('booksedit', ['book' => $book]);
} else {
return "アクセス権がありません";
}
}
public function update(Request $request)
{
// 変更前
$book = Book::find($request->id); // これを下のコードに変更
// 変更後
$book = Book::where('user_id', Auth::id())->find($request->id);
BooksController.php
初めのうちは1つ1つのステップで細かく確認作業をすると、逆にスムーズに開発が進むようになることがわかるでしょう。Laravelではdd() という便利な確認ツールがあります。利用しない手はないですね! 使い方例: dd(Auth::id()) → ログインしているユーザのidが取得できます ( ※この例の場合、use Auth; をファイルの冒頭付近に記載するのを忘れずに!)
app/Providers/RouteServicePrivicer.php
変更前
public const HOME = '/home';
変更後
public const HOME = '/';
app\Models\User.php
// L45あたりに追加
public function books()
{
return $this->hasMany(Book::class);
}
resources\views\layouts\app.blade.php L60あたり
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<!-- ↓この1行を追加-->
<a class="dropdown-item" href="{{ url('/home') }}">マイページ</a>
resources\views\home.blade.php
L11あたり
card-bodyの中身を削除し以下コードを追加
<div class="card-body">
<!--↓ここから追加↓-->
<div class="alert alert-primary">
<div>マイページサンプル</div>
<div>ログインユーザーの情報のみ表示</div>
</div>
<table class="table table-striped task-table">
<!-- テーブルヘッダ -->
<thead>
<th>本一覧</th>
<th> </th>
</thead>
<!-- テーブル本体 -->
<tbody>
@if(isset($books))
@foreach ($books as $book)
<tr>
<!-- 本タイトル -->
<td class="table-text">
<div>{{ $book->item_name }}</div>
</td>
<td>
<form action="{{ url('book/'.$book->id.'/like') }}" method="POST">
@csrf
@method('delete')
<button type="submit" class="btn btn-danger">
削除
</button>
</form>
</td>
<!-- 本: 更新ボタン -->
<td>
<a href="{{ url('booksedit/'.$book->id) }}">
<button type="submit" class="btn btn-primary">更新</button>
</a>
</td>
</tr>
@endforeach
@endif
</tbody>
</table>
<!--↑ここまで↑-->
</div>
app\Http\Controllers\HomeController.php
冒頭付近に追加
use App\Models\User; // 追加
use Auth; // 追加
indexを修正
public function index()
{
$books = User::find(Auth::id())->books;
return view('home', ['books' => $books]);
}
routes\web.php
Route::group(['middleware' => 'auth'], function(){
// 中略
// ログインユーザの本を取得
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index']);
});
// 以下を削除
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
->name('home');
を取り去って、保護の中へいれてしまうんだ。
BookモデルからUserモデルを呼び出してuserの情報を受け取れるようにします
app\Models\Book.php
public function user()
{
return $this->belongsTo(User::class);
}
resources\views\books.blade.php L66あたり
<!-- テーブル本体 -->
<tbody>
@foreach ($books as $book)
<tr>
<!-- 本タイトル -->
<td class="table-text">
<div>{{ $book->item_name }}</div>
</td>
<!-- 登録者名 ↓ここを追加-->
<td class="table-text">
<div>{{ $book->user->name }}</div>
</td>
cms階層でコマンドを実行
php artisan make:migration create_book_user_table --create=book_user
database\migrations[日付]_create_book_user_table.phpの function upを編集
public function up()
{
Schema::create('book_user', function (Blueprint $table) {
$table->increments('id');
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('book_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('book_id')->references('id')->on('books')->onDelete('cascade');
$table->unique(['user_id', 'book_id']);
});
}
php artisan migrate
book_userテーブルができたか。
app\Models\User.php
public function favoriteBook()
{
return $this->belongsToMany(Book::class);
}
app\Models\Book.php
public function favoriteBook()
{
return $this->belongsToMany(User::class);
}
L139あたり(ファイル下部)に以下の関数を追加
// お気に入り(多対多)
public function likeBook(Book $book)
{
$book->favoriteBook()->attach(Auth::id());
return back();
}
public function unlikeBook(Book $book)
{
$book->favoriteBook()->detach(Auth::id());
return back();
}
routes\web.php
Route::group(['middleware' => 'auth'], function(){
// 中略
// 以下追加
// お気に入り機能
Route::post('book/{book}/like', [BookController::class, 'likeBook']);
Route::post('book/{book}/unlike', [BookController::class, 'unlikeBook']);
}
books.blade.php L89あたり(更新ボタンの下)に以下を追加
<!-- Likeボタン -->
<td>
@if($book->favoriteBook()->where('user_id',Auth::id())->exists() === false)
<form action="{{ url('book/'.$book->id.'/like') }}" method="POST">
{{ csrf_field() }}
<button type="submit" class="btn btn-outline-warning">
Like
</button>
</form>
@endif
@if($book->favoriteBook()->where('user_id',Auth::id())->exists() === true)
<form action="{{ url('book/'.$book->id.'/unlike') }}" method="POST">
{{ csrf_field() }}
<button type="submit" class="btn btn-warning">
Unlike
</button>
</form>
@endif
</td>
book_userテーブルの 3つのid が更新できたか。