メモ > 技術 > フレームワーク: Laravel9 > データベースを扱う
データベースを扱う
■データベースの作成
$ sail mysql
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| laravel9 |
| testing |
+--------------------+
3 rows in set (0.00 sec)
mysql> USE laravel9;
mysql> SHOW TABLES;
Empty set (0.00 sec)
もともとデータベース「laravel9」が作成済みになっているので、これをそのまま利用する
■テーブルの作成
マイグレーションを作成
$ sail artisan make:migration create_articles_table
以下のファイルが作成される
database/migrations/2024_04_02_101902_create_articles_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
};
以下のとおり、マイグレーションを修正する(titleとcontentを追加する)
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
↓
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
マイグレーションを実行する
$ sail artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (57.38ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (46.95ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (50.15ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (69.67ms)
Migrating: 2024_04_02_101902_create_articles_table
Migrated: 2024_04_02_101902_create_articles_table (25.10ms)
テーブルの作成を確認できる
$ sail mysql
mysql> USE laravel9;
mysql> SHOW TABLES;
+------------------------+
| Tables_in_laravel9 |
+------------------------+
| articles |
| failed_jobs |
| migrations |
| password_resets |
| personal_access_tokens |
| users |
+------------------------+
6 rows in set (0.00 sec)
mysql> SHOW COLUMNS FROM articles;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(255) | NO | | NULL | |
| content | text | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+-----------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)
■データの作成
シーダーを作成
$ sail artisan make:seeder ArticlesSeeder
以下のファイルが作成される
database/seeders/ArticlesSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ArticlesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
以下のとおり編集する
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ArticlesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('articles')->insert([
'title' => Str::random(20),
'content' => Str::random(200),
'created_at' => now(),
'updated_at' => null,
]);
}
}
以下のファイルを編集して、シーダーを呼び出す
database/seeders/DatabaseSeeder.php
// \App\Models\User::factory(10)->create();
$this->call([ArticlesSeeder::class]);
シーダーを実行する
$ sail artisan db:seed
Seeding: Database\Seeders\ArticlesSeeder
Seeded: Database\Seeders\ArticlesSeeder (15.77ms)
Database seeding completed successfully.
データの登録を確認できる
$ sail mysql
mysql> USE laravel9;
mysql> SELECT id, title FROM articles;
+----+----------------------+
| id | title |
+----+----------------------+
| 1 | bhkWrdUVSauoQh9h5lcX |
+----+----------------------+
1 row in set (0.00 sec)
確認できたら、いったんデータは削除しておく
mysql> TRUNCATE TABLE articles;
■モデルの作成
モデルを作成
$ sail artisan make:model Article
以下のファイルが作成される
app/Models/Article.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasFactory;
}
■ファクトリーの作成
ファクトリーを作成
$ sail artisan make:factory ArticleFactory --model=Article
以下のファイルが作成される
database/factories/ArticleFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Article>
*/
class ArticleFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
//
];
}
}
以下のとおり編集する
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Article>
*/
class ArticleFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'title' => $this->faker->realText(20),
'content' => $this->faker->realText(200),
'created_at' => now(),
'updated_at' => null,
//'created_at' => $this->faker->datetime($max = 'now', $timezone = date_default_timezone_get()),
//'updated_at' => $this->faker->datetime($max = 'now', $timezone = date_default_timezone_get()),
];
}
}
日本語の設定に変更する
config/app.php
'faker_locale' => 'en_US',
↓
'faker_locale' => 'ja_JP',
以下のファイルを編集する
database/seeders/ArticlesSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
//use Illuminate\Support\Facades\DB;
//use Illuminate\Support\Str;
use App\Models\Article;
class ArticlesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//DB::table('articles')->insert([
// 'title' => Str::random(20),
// 'content' => Str::random(200),
// 'created_at' => now(),
// 'updated_at' => null,
//]);
Article::factory()->count(10)->create();
}
}
シーダーを実行する
$ sail artisan db:seed
Seeding: Database\Seeders\ArticlesSeeder
Seeded: Database\Seeders\ArticlesSeeder (171.08ms)
Database seeding completed successfully.
データの登録を確認できる
$ sail mysql
mysql> USE laravel9;
mysql> SELECT id, title, created_at, updated_at FROM articles;
+----+--------------------------------------------------------------+---------------------+------------+
| id | title | created_at | updated_at |
+----+--------------------------------------------------------------+---------------------+------------+
| 1 | かけにはないのが、ぱっと窓まどから私の。 | 2024-04-03 10:48:49 | NULL |
| 2 | かたあとだと思い出していると包つつまし。 | 2024-04-03 10:48:49 | NULL |
| 3 | のあるい実みもらだの今だってそのいらっ。 | 2024-04-03 10:48:49 | NULL |
| 4 | 覆ひおおきく天井てんじゅうに、一足さき。 | 2024-04-03 10:48:49 | NULL |
| 5 | ドをかすが少しおずおずしない水に落おと。 | 2024-04-03 10:48:49 | NULL |
| 6 | れたまって、まあ、すすんで、そのまって。 | 2024-04-03 10:48:49 | NULL |
| 7 | まにもついているんです」「お母さんも眼。 | 2024-04-03 10:48:49 | NULL |
| 8 | といつかまわりにいっぱんの方で、黒い洋。 | 2024-04-03 10:48:49 | NULL |
| 9 | ついてあらまあ、切符きっとみちがすと喧。 | 2024-04-03 10:48:49 | NULL |
| 10 | いせいよ光っていました。「ああ、孔雀く。 | 2024-04-03 10:48:49 | NULL |
+----+--------------------------------------------------------------+---------------------+------------+
10 rows in set (0.00 sec)
■記事の表示
以下のとおり編集する
app/Http/Controllers/Article/IndexController.php
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\Request;
class IndexController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
$articles = Article::all();
return view('article.index', [
'articles' => $articles,
]);
}
}
resources/views/article/index.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Article</title>
</head>
<body>
<h1>Article</h1>
<ul>
@foreach ($articles as $article)
<li>{{ $article->id }} / {{ $article->title }}</li>
@endforeach
</ul>
</body>
</html>
以下にアクセスして表示を確認する
http://laravel.local/article/
■記事の投稿
リクエストを作成
$ sail artisan make:request Article/CreateRequest
以下のファイルが作成される
app/Http/Requests/Article/CreateRequest.php
<?php
namespace App\Http\Requests\Article;
use Illuminate\Foundation\Http\FormRequest;
class CreateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
//
];
}
}
以下のとおり編集する
<?php
namespace App\Http\Requests\Article;
use Illuminate\Foundation\Http\FormRequest;
class CreateRequest 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<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|max:20',
'content' => 'required|max:200',
];
}
}
コントローラーを作成
$ sail artisan make:controller Article/CreateController --invokable
以下のファイルが作成される
app/Http/Controllers/Article/CreateController.php
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class CreateController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
//
}
}
以下のとおり編集する
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use App\Models\Article;
use App\Http\Requests\Article\CreateRequest;
class CreateController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(CreateRequest $request)
{
$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->save();
return redirect()->route('article.index')->with('message', '投稿しました。');
}
}
routes/web.php
Route::get('/article', \App\Http\Controllers\Article\IndexController::class);
↓
Route::get('/article', \App\Http\Controllers\Article\IndexController::class)->name('article.index');
Route::post('/article/create', \App\Http\Controllers\Article\CreateController::class)->name('article.create');
resources/views/article/index.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Article</title>
</head>
<body>
<h1>Article</h1>
@if (session('message'))
<p>{{ session('message') }}</p>
@endif
<form action="{{ route('article.create') }}" method="post">
@csrf
<dl>
<dt>タイトル</dt>
<dd>
<input type="text" name="title" value="{{ old('title', isset($article) ? $article->title : '') }}">
@error('title')
<p>{{ $message }}</p>
@enderror
</dd>
<dt>本文</dt>
<dd>
<textarea name="content" rows="5" cols="50">{{ old('content', isset($article) ? $article->content : '') }}</textarea>
@error('content')
<p>{{ $message }}</p>
@enderror
</dd>
</dl>
<p><button type="submit">投稿</button></p>
</form>
<ul>
@foreach ($articles as $article)
<li>{{ $article->id }} / {{ $article->title }}</li>
@endforeach
</ul>
</body>
</html>
config/app.php
'locale' => 'en',
'fallback_locale' => 'en',
↓
'locale' => 'ja',
'fallback_locale' => 'ja',
$ sail composer require laravel-lang/lang:~10.3
$ cp -R vendor/laravel-lang/lang/locales/ja lang/ja
lang/ja/validation.php
],
'attributes' => [
'title' => 'タイトル',
'content' => '本文',
],
];
以下にアクセスして表示を確認する
http://laravel.local/article/
■記事の編集
※コントローラーを Update 内に置くか否かは考えたい
※登録処理も含めて、この時点では authorize() を false にしておく方が自然か。検証したい
ファイルを作成
$ sail artisan make:request Article/UpdateRequest
$ sail artisan make:controller Article/EditController --invokable
$ sail artisan make:controller Article/UpdateController --invokable
以下のとおり編集する
app/Http/Requests/Article/UpdateRequest.php (変更箇所は CreateRequest.php と同じ)
<?php
namespace App\Http\Requests\Article;
use Illuminate\Foundation\Http\FormRequest;
class UpdateRequest 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<string, mixed>
*/
public function rules()
{
return [
'title' => 'required|max:20',
'content' => 'required|max:200',
];
}
}
app/Http/Controllers/Article/EditController.php
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\Request;
class EditController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
$id = (int) $request->route('id');
$article = Article::where('id', $id)->firstOrFail();
return view('article.edit', [
'article' => $article,
]);
}
}
app/Http/Controllers/Article/UpdateController.php
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\Request;
class UpdateController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
$article = Article::where('id', $request->id)->firstOrFail();
$article->title = $request->title;
$article->content = $request->content;
$article->save();
return redirect()->route('article.edit', ['id' => $request->id])->with('message', '編集しました。');
}
}
resources/views/article/edit.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Article</title>
</head>
<body>
<h1>Article</h1>
<p><a href="{{ route('article.index') }}">戻る</a></p>
@if (session('message'))
<p>{{ session('message') }}</p>
@endif
<form action="{{ route('article.update', ['id' => $article->id]) }}" method="post">
@method('PUT')
@csrf
<dl>
<dt>タイトル</dt>
<dd>
<input type="text" name="title" value="{{ old('title', isset($article) ? $article->title : '') }}">
@error('title')
<p>{{ $message }}</p>
@enderror
</dd>
<dt>本文</dt>
<dd>
<textarea name="content" rows="5" cols="50">{{ old('content', isset($article) ? $article->content : '') }}</textarea>
@error('content')
<p>{{ $message }}</p>
@enderror
</dd>
</dl>
<p><button type="submit">編集</button></p>
</form>
</body>
</html>
resources/views/article/index.blade.php
<li>{{ $article->id }} / {{ $article->title }}</li>
↓
<li>{{ $article->id }} / {{ $article->title }} <a href="{{ route('article.edit', ['id' => $article->id]) }}">編集</a></li>
routes/web.php
Route::get('/article/edit/{id}', \App\Http\Controllers\Article\EditController::class)->name('article.edit')->where('id', '[0-9]+');
Route::put('/article/update/{id}', \App\Http\Controllers\Article\UpdateController::class)->name('article.update')->where('id', '[0-9]+');
以下にアクセスして表示を確認する
http://laravel.local/article/
■記事の削除
ファイルを作成
$ sail artisan make:controller Article/DeleteController --invokable
以下のとおり編集する
app/Http/Controllers/Article/DeleteController.php
<?php
namespace App\Http\Controllers\Article;
use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\Request;
class DeleteController extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
$id = (int) $request->route('id');
$article = Article::where('id', $id)->firstOrFail();
$article->delete();
return redirect()->route('article.index')->with('message', '削除しました。');
}
}
resources/views/article/edit.blade.php
<form action="{{ route('article.delete', ['id' => $article->id]) }}" method="post">
@method('DELETE')
@csrf
<p><button type="submit">削除</button></p>
</form>
routes/web.php
Route::delete('/article/delete/{id}', \App\Http\Controllers\Article\DeleteController::class)->name('article.delete');