Laravel 11 CRUD Hands-On
CRUD
お盆に実家に帰って特にやることもなかったのでLaravelでCRUDをやってみました。
ググりながらやったのですがLaravelのバージョンが古かったりLaravel Sailの使用が前提だったりLaravelやArtisanの機能をフルに使っていないものばかりでしたので自分なりのやり方を残しておくことにします。
開発環境はGitpodです。
Laravel 11のDBでSQLiteがデフォルトで使用されるようになりましたので、別にGitpodを使わなくても、PHPとComposerがインストールされていてLaravelに必要なPHP拡張機能が有効になっていれば、様々なOSで簡単に開発を始めることができます。
例えばGlitchでもできますので、以下の記事を参考にして試してみてください。
$ php -v
PHP 8.3.8 (cli) (built: Jun 8 2024 21:34:22) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.8, Copyright (c) Zend Technologies
with Zend OPcache v8.3.8, Copyright (c), by Zend Technologies
$ composer -V
Composer version 2.7.7 2024-06-10 22:11:12
PHP version 8.3.8 (/usr/bin/php8.3)
Run the "diagnose" command to get more detailed diagnostics output.
$ php artisan -V
Laravel Framework 11.20.0
Create Project
まずはComposerでプロジェクトを作成します。
$ composer create-project laravel/laravel example-app
Creating a "laravel/laravel" project at "./example-app"
Installing laravel/laravel (v11.1.4)
...
INFO Discovering packages.
...
INFO No publishable resources for tag [laravel-assets].
...
> @php artisan key:generate --ansi
INFO Application key set successfully.
> @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
> @php artisan migrate --graceful --ansi
INFO Preparing database.
Creating migration table ............................................................................................................. 9.82ms DONE
INFO Running migrations.
0001_01_01_000000_create_users_table ................................................................................................. 9.37ms DONE
0001_01_01_000001_create_cache_table ................................................................................................. 2.88ms DONE
0001_01_01_000002_create_jobs_table .................................................................................................. 7.62ms DONE
プロジェクトの作成が終わったらカレントディレクトリを移動します。
$ cd example-app
続いてartisanコマンドでPostモデルを作ります。この時--all
や-a
オプションをつけるとモデルに紐づいたMigration, Seeder, Factory, Policy, Resource Controller, Form Requestも全部作ってくれます。
今回はPolicy以外全部使います。
$ php artisan make:model Post -a
INFO Model [app/Models/Post.php] created successfully.
INFO Factory [database/factories/PostFactory.php] created successfully.
INFO Migration [database/migrations/2024_08_17_120000_create_posts_table.php] created successfully.
INFO Seeder [database/seeders/PostSeeder.php] created successfully.
INFO Request [app/Http/Requests/StorePostRequest.php] created successfully.
INFO Request [app/Http/Requests/UpdatePostRequest.php] created successfully.
INFO Controller [app/Http/Controllers/PostController.php] created successfully.
INFO Policy [app/Policies/PostPolicy.php] created successfully.
またViewは作ってくれないので別途作成します。
$ php artisan make:view posts/index
INFO View [resources/views/posts/index.blade.php] created successfully.
$ php artisan make:view posts/create
INFO View [resources/views/posts/create.blade.php] created successfully.
$ php artisan make:view posts/show
INFO View [resources/views/posts/show.blade.php] created successfully.
$ php artisan make:view posts/edit
INFO View [resources/views/posts/edit.blade.php] created successfully.
これでMVCで使う初期ファイルがすべて完成しました。
Model
初めにデータベースのマイグレーションファイルにcontentカラムを追加します。
diff -ur -x composer.lock -x vendor example-app-d/database/migrations/2024_08_17_120000_create_posts_table.php example-app/database/migrations/2024_08_17_120000_create_posts_table.php
--- example-app-d/database/migrations/2024_08_17_120000_create_posts_table.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/migrations/2024_08_17_120000_create_posts_table.php 2024-08-17 12:00:00.000000000 +0000
@@ -13,6 +13,7 @@
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
+ $table->text('content');
$table->timestamps();
});
}
ファクトリーファイルにFaker機能でコンテンツを自動生成できるようにします。
diff -ur -x composer.lock -x vendor example-app-d/database/factories/PostFactory.php example-app/database/factories/PostFactory.php
--- example-app-d/database/factories/PostFactory.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/factories/PostFactory.php 2024-08-17 12:00:00.000000000 +0000
@@ -17,7 +17,7 @@
public function definition(): array
{
return [
- //
+ 'content' => fake()->paragraph(),
];
}
}
シーダーファイルからFactoryを呼び出します。
とりあえず20回。
useでPostモデルも一緒に呼び出しましょう。
diff -ur -x composer.lock -x vendor example-app-d/database/seeders/PostSeeder.php example-app/database/seeders/PostSeeder.php
--- example-app-d/database/seeders/PostSeeder.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/seeders/PostSeeder.php 2024-08-17 12:00:00.000000000 +0000
@@ -4,6 +4,7 @@
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
+use App\Models\Post;
class PostSeeder extends Seeder
{
@@ -12,6 +13,6 @@
*/
public function run(): void
{
- //
+ Post::factory()->count(20)->create();
}
}
このままだといちいちPostSeederを指定しないといけなくなるのでDatabaseSeederもいじります。
元々あったUserモデルに関するFactoryは今回使わないのでコメントアウトしておきましょう。
diff -ur -x composer.lock -x vendor example-app-d/database/seeders/DatabaseSeeder.php example-app/database/seeders/DatabaseSeeder.php
--- example-app-d/database/seeders/DatabaseSeeder.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/database/seeders/DatabaseSeeder.php 2024-08-17 12:00:00.000000000 +0000
@@ -13,11 +13,13 @@
*/
public function run(): void
{
+ $this->call(PostSeeder::class);
+
// User::factory(10)->create();
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
+ // User::factory()->create([
+ // 'name' => 'Test User',
+ // 'email' => 'test@example.com',
+ // ]);
}
}
最後にPostモデルでfillableプロパティを指定しましょう。
指定しないとデータベースに書き込む時にエラーが発生することがあります。
diff -ur -x composer.lock -x vendor example-app-d/app/Models/Post.php example-app/app/Models/Post.php
--- example-app-d/app/Models/Post.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Models/Post.php 2024-08-17 12:00:00.000000000 +0000
@@ -8,4 +8,6 @@
class Post extends Model
{
use HasFactory;
+
+ protected $fillable = ['content'];
}
Postモデルが完成したのでマイグレーションとシーディングをやってみましょう。
この時初回のマイグレーションが残っていると不整合が発生するかもしれないのでmigrate:refresh
でartisanコマンドを実行します。
$ php artisan migrate:refresh --seed
...
INFO Rolling back migrations.
0001_01_01_000002_create_jobs_table .................................................................................................. 6.06ms DONE
0001_01_01_000001_create_cache_table ................................................................................................. 2.87ms DONE
0001_01_01_000000_create_users_table ................................................................................................. 4.10ms DONE
INFO Running migrations.
0001_01_01_000000_create_users_table ................................................................................................. 7.88ms DONE
0001_01_01_000001_create_cache_table ................................................................................................. 2.50ms DONE
0001_01_01_000002_create_jobs_table .................................................................................................. 6.76ms DONE
2024_08_17_120000_create_posts_table ................................................................................................. 1.66ms DONE
INFO Seeding database.
Database\Seeders\PostSeeder .............................................................................................................. RUNNING
Database\Seeders\PostSeeder ........................................................................................................... 43 ms DONE
SQLiteのSQLを書いて実際に見てみましょう。
$ php artisan db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .mode line
sqlite> SELECT * FROM posts;
id = 1
content = Quod laudantium dolore qui architecto nemo unde inventore. Cupiditate dolor impedit cupiditate est non. Sed et exercitationem nemo facere laboriosam id. Ab voluptatem qui illum.
created_at = 2024-08-17 12:00:00
updated_at = 2024-08-17 12:00:00
...
SCHEMA
CREATE TABLE "posts" (
"id" integer PRIMARY KEY autoincrement NOT NULL,
"content" text NOT NULL,
"created_at" datetime,
"updated_at" datetime
)
FactoryとSeederによって生成されたダミーデータが追加されていることを確認できました。
Controller
続いてPostモデルに紐づいたリソースコントローラーファイルのPostコントローラーにデータベースに関する処理やページを表示する処理を追加します。
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Controllers/PostController.php example-app/app/Http/Controllers/PostController.php
--- example-app-d/app/Http/Controllers/PostController.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Controllers/PostController.php 2024-08-17 12:00:00.000000000 +0000
@@ -13,7 +13,8 @@
*/
public function index()
{
- //
+ $posts = Post::oldest()->get();
+ return view('posts.index', compact('posts'));
}
/**
@@ -21,7 +22,7 @@
*/
public function create()
{
- //
+ return view('posts.create');
}
/**
@@ -29,7 +30,8 @@
*/
public function store(StorePostRequest $request)
{
- //
+ Post::create($request->all());
+ return redirect()->route('posts.index')->with('success', 'Create');
}
/**
@@ -37,7 +39,7 @@
*/
public function show(Post $post)
{
- //
+ return view('posts.show', compact('post'));
}
/**
@@ -45,7 +47,7 @@
*/
public function edit(Post $post)
{
- //
+ return view('posts.edit',compact('post'));
}
/**
@@ -53,7 +55,8 @@
*/
public function update(UpdatePostRequest $request, Post $post)
{
- //
+ $post->update($request->all());
+ return redirect()->route('posts.index')->with('success', 'Update');
}
/**
@@ -61,6 +64,7 @@
*/
public function destroy(Post $post)
{
- //
+ $post->delete();
+ return redirect()->route('posts.index')->with('success', 'Delete');
}
}
更にStorePostRequestファイルとUpdatePostRequestファイルを編集します。
このままだとフォームからデータベースに書き込む際403エラーが表示されてしまうため、authorize()でtrueを返します。
併せてフォームから空や空白のコンテンツを追加できないようにするため、contentカラムにrequiredを追加して必須項目にします。
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Requests/StorePostRequest.php example-app/app/Http/Requests/StorePostRequest.php
--- example-app-d/app/Http/Requests/StorePostRequest.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Requests/StorePostRequest.php 2024-08-17 12:00:00.000000000 +0000
@@ -11,7 +11,7 @@
*/
public function authorize(): bool
{
- return false;
+ return true;
}
/**
@@ -22,7 +22,7 @@
public function rules(): array
{
return [
- //
+ 'content' => 'required',
];
}
}
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Requests/UpdatePostRequest.php example-app/app/Http/Requests/UpdatePostRequest.php
--- example-app-d/app/Http/Requests/UpdatePostRequest.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Requests/UpdatePostRequest.php 2024-08-17 12:00:00.000000000 +0000
@@ -11,7 +11,7 @@
*/
public function authorize(): bool
{
- return false;
+ return true;
}
/**
@@ -22,7 +22,7 @@
public function rules(): array
{
return [
- //
+ 'content' => 'required',
];
}
}
最後にルーティングファイルをしっかり編集してPostコントローラーは完成です。
diff -ur -x composer.lock -x vendor example-app-d/routes/web.php example-app/routes/web.php
--- example-app-d/routes/web.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/routes/web.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,7 +1,9 @@
<?php
use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\PostController;
Route::get('/', function () {
- return view('welcome');
+ return '<p><a href="/posts">Post</a></p>';
});
+Route::resource('posts', PostController::class);
正常に起動できるか確認します。
$ php artisan serve
先ほどweb.phpに直接書いたHTMLがルートパスに表示されていることを確認できました。
View
最後にViewのbladeファイルにHTMLを書いていきます。
偉人の名言がコメントアウトされているのですが、今回は省略しています。
index, create, show, editページの順番に編集しています。
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/index.blade.php example-app/resources/views/posts/index.blade.php
--- example-app-d/resources/views/posts/index.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/index.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,19 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Laravel 11 CRUD</h1>
+@if ($message = Session::get('success'))
+<p>Success: {{ $message }}</p>
+@endif
+<p><a href="{{ route('posts.create') }}">Create</a></p>
+<table>
+ <tr>
+ <th>ID</th>
+ <th>Content</th>
+ <th>Control</th>
+ </tr>
+ @foreach ($posts as $post)
+ <tr>
+ <td>{{ $post->id }}</td>
+ <td>{{ $post->content }}</td>
+ <td><a href="{{ route('posts.show', $post->id) }}">Show</a> | <a href="{{ route('posts.edit', $post->id) }}">Edit</a></td>
+ </tr>
+ @endforeach
+</table>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/create.blade.php example-app/resources/views/posts/create.blade.php
--- example-app-d/resources/views/posts/create.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/create.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,7 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Create</h1>
+<form action="{{ route('posts.store') }}" method="POST">
+ @csrf
+ <input type="text" name="content" autocomplete="off" value="Hello, World!" autofocus />
+ <button>Create</button>
+</form>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/show.blade.php example-app/resources/views/posts/show.blade.php
--- example-app-d/resources/views/posts/show.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/show.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,3 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Show</h1>
+<p>{{ $post->content }}</p>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/edit.blade.php example-app/resources/views/posts/edit.blade.php
--- example-app-d/resources/views/posts/edit.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/edit.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,13 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Edit</h1>
+<form action="{{ route('posts.update', $post->id) }}" method="POST">
+ @csrf
+ @method('PUT')
+ <input type="text" name="content" autocomplete="off" value="{{ $post->content }}" autofocus />
+ <button>Update</button>
+</form>
+<form action="{{ route('posts.destroy', $post->id) }}" method="POST">
+ @csrf
+ @method('DELETE')
+ <button>Delete</button>
+</form>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
INSPIRE
$ php artisan inspire
“ The only way to do great work is to love what you do. ”
— Steve Jobs
もしhttpsでサーバーを立ち上げた場合、このままだとFormリクエストを送信した際httpを使用し安全でないフォームで処理されてしまうのでAppServiceProviderでhttpsを強制します。
useでURLに関するファサードを呼び出す必要があります。
diff -ur -x composer.lock -x vendor example-app-d/app/Providers/AppServiceProvider.php example-app/app/Providers/AppServiceProvider.php
--- example-app-d/app/Providers/AppServiceProvider.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/app/Providers/AppServiceProvider.php 2024-08-17 12:00:00.000000000 +0000
@@ -3,6 +3,7 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
+use Illuminate\Support\Facades\URL;
class AppServiceProvider extends ServiceProvider
{
@@ -19,6 +20,6 @@
*/
public function boot(): void
{
- //
+ URL::forceScheme('https');
}
}
PRDUCTION ONLY
- URL::forceScheme('https');
+ if (app()->isProduction()) URL::forceScheme('https');
最後に起動して動作確認します。
$ php artisan serve
終わりです。お疲れ様でした。
Diff
DIFF ALL
$ diff -ur -x composer.lock -x vendor example-app-d example-app
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Controllers/PostController.php example-app/app/Http/Controllers/PostController.php
--- example-app-d/app/Http/Controllers/PostController.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Controllers/PostController.php 2024-08-17 12:00:00.000000000 +0000
@@ -13,7 +13,8 @@
*/
public function index()
{
- //
+ $posts = Post::oldest()->get();
+ return view('posts.index', compact('posts'));
}
/**
@@ -21,7 +22,7 @@
*/
public function create()
{
- //
+ return view('posts.create');
}
/**
@@ -29,7 +30,8 @@
*/
public function store(StorePostRequest $request)
{
- //
+ Post::create($request->all());
+ return redirect()->route('posts.index')->with('success', 'Create');
}
/**
@@ -37,7 +39,7 @@
*/
public function show(Post $post)
{
- //
+ return view('posts.show', compact('post'));
}
/**
@@ -45,7 +47,7 @@
*/
public function edit(Post $post)
{
- //
+ return view('posts.edit',compact('post'));
}
/**
@@ -53,7 +55,8 @@
*/
public function update(UpdatePostRequest $request, Post $post)
{
- //
+ $post->update($request->all());
+ return redirect()->route('posts.index')->with('success', 'Update');
}
/**
@@ -61,6 +64,7 @@
*/
public function destroy(Post $post)
{
- //
+ $post->delete();
+ return redirect()->route('posts.index')->with('success', 'Delete');
}
}
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Requests/StorePostRequest.php example-app/app/Http/Requests/StorePostRequest.php
--- example-app-d/app/Http/Requests/StorePostRequest.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Requests/StorePostRequest.php 2024-08-17 12:00:00.000000000 +0000
@@ -11,7 +11,7 @@
*/
public function authorize(): bool
{
- return false;
+ return true;
}
/**
@@ -22,7 +22,7 @@
public function rules(): array
{
return [
- //
+ 'content' => 'required',
];
}
}
diff -ur -x composer.lock -x vendor example-app-d/app/Http/Requests/UpdatePostRequest.php example-app/app/Http/Requests/UpdatePostRequest.php
--- example-app-d/app/Http/Requests/UpdatePostRequest.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Http/Requests/UpdatePostRequest.php 2024-08-17 12:00:00.000000000 +0000
@@ -11,7 +11,7 @@
*/
public function authorize(): bool
{
- return false;
+ return true;
}
/**
@@ -22,7 +22,7 @@
public function rules(): array
{
return [
- //
+ 'content' => 'required',
];
}
}
diff -ur -x composer.lock -x vendor example-app-d/app/Models/Post.php example-app/app/Models/Post.php
--- example-app-d/app/Models/Post.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/app/Models/Post.php 2024-08-17 12:00:00.000000000 +0000
@@ -8,4 +8,6 @@
class Post extends Model
{
use HasFactory;
+
+ protected $fillable = ['content'];
}
diff -ur -x composer.lock -x vendor example-app-d/app/Providers/AppServiceProvider.php example-app/app/Providers/AppServiceProvider.php
--- example-app-d/app/Providers/AppServiceProvider.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/app/Providers/AppServiceProvider.php 2024-08-17 12:00:00.000000000 +0000
@@ -3,6 +3,7 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
+use Illuminate\Support\Facades\URL;
class AppServiceProvider extends ServiceProvider
{
@@ -19,6 +20,6 @@
*/
public function boot(): void
{
- //
+ URL::forceScheme('https');
}
}
Binary files example-app-d/database/database.sqlite and example-app/database/database.sqlite differ
diff -ur -x composer.lock -x vendor example-app-d/database/factories/PostFactory.php example-app/database/factories/PostFactory.php
--- example-app-d/database/factories/PostFactory.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/factories/PostFactory.php 2024-08-17 12:00:00.000000000 +0000
@@ -17,7 +17,7 @@
public function definition(): array
{
return [
- //
+ 'content' => fake()->paragraph(),
];
}
}
diff -ur -x composer.lock -x vendor example-app-d/database/migrations/2024_08_17_120000_create_posts_table.php example-app/database/migrations/2024_08_17_120000_create_posts_table.php
--- example-app-d/database/migrations/2024_08_17_120000_create_posts_table.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/migrations/2024_08_17_120000_create_posts_table.php 2024-08-17 12:00:00.000000000 +0000
@@ -13,6 +13,7 @@
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
+ $table->text('content');
$table->timestamps();
});
}
diff -ur -x composer.lock -x vendor example-app-d/database/seeders/DatabaseSeeder.php example-app/database/seeders/DatabaseSeeder.php
--- example-app-d/database/seeders/DatabaseSeeder.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/database/seeders/DatabaseSeeder.php 2024-07-16 14:39:20.000000000 +0000
@@ -13,11 +13,13 @@
*/
public function run(): void
{
+ $this->call(PostSeeder::class);
+
// User::factory(10)->create();
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
+ // User::factory()->create([
+ // 'name' => 'Test User',
+ // 'email' => 'test@example.com',
+ // ]);
}
}
diff -ur -x composer.lock -x vendor example-app-d/database/seeders/PostSeeder.php example-app/database/seeders/PostSeeder.php
--- example-app-d/database/seeders/PostSeeder.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/database/seeders/PostSeeder.php 2024-08-17 12:00:00.000000000 +0000
@@ -4,6 +4,7 @@
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
+use App\Models\Post;
class PostSeeder extends Seeder
{
@@ -12,6 +13,6 @@
*/
public function run(): void
{
- //
+ Post::factory()->count(20)->create();
}
}
diff -ur -x composer.lock -x vendor example-app-d/.env example-app/.env
--- example-app-d/.env 2024-08-17 12:00:00.000000000 +0000
+++ example-app/.env 2024-08-17 12:00:00.000000000 +0000
@@ -1,7 +1,7 @@
APP_NAME=Laravel
-APP_ENV=local
-APP_KEY=base64:...
-APP_DEBUG=true
+APP_ENV=production
+APP_KEY=base64:...
+APP_DEBUG=false
APP_TIMEZONE=UTC
APP_URL=http://localhost
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/create.blade.php example-app/resources/views/posts/create.blade.php
--- example-app-d/resources/views/posts/create.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/create.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,7 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Create</h1>
+<form action="{{ route('posts.store') }}" method="POST">
+ @csrf
+ <input type="text" name="content" autocomplete="off" value="Hello, World!" autofocus />
+ <button>Create</button>
+</form>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/edit.blade.php example-app/resources/views/posts/edit.blade.php
--- example-app-d/resources/views/posts/edit.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/edit.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,13 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Edit</h1>
+<form action="{{ route('posts.update', $post->id) }}" method="POST">
+ @csrf
+ @method('PUT')
+ <input type="text" name="content" autocomplete="off" value="{{ $post->content }}" autofocus />
+ <button>Update</button>
+</form>
+<form action="{{ route('posts.destroy', $post->id) }}" method="POST">
+ @csrf
+ @method('DELETE')
+ <button>Delete</button>
+</form>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/index.blade.php example-app/resources/views/posts/index.blade.php
--- example-app-d/resources/views/posts/index.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/index.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,19 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Laravel 11 CRUD</h1>
+@if ($message = Session::get('success'))
+<p>Success: {{ $message }}</p>
+@endif
+<p><a href="{{ route('posts.create') }}">Create</a></p>
+<table>
+ <tr>
+ <th>ID</th>
+ <th>Content</th>
+ <th>Control</th>
+ </tr>
+ @foreach ($posts as $post)
+ <tr>
+ <td>{{ $post->id }}</td>
+ <td>{{ $post->content }}</td>
+ <td><a href="{{ route('posts.show', $post->id) }}">Show</a> | <a href="{{ route('posts.edit', $post->id) }}">Edit</a></td>
+ </tr>
+ @endforeach
+</table>
diff -ur -x composer.lock -x vendor example-app-d/resources/views/posts/show.blade.php example-app/resources/views/posts/show.blade.php
--- example-app-d/resources/views/posts/show.blade.php 2024-08-17 12:00:00.000000000 +0000
+++ example-app/resources/views/posts/show.blade.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,3 +1,3 @@
-<div>
- <!-- ... -->
-</div>
+<h1>Show</h1>
+<p>{{ $post->content }}</p>
+<p><a href="{{ route('posts.index') }}">Back</a></p>
diff -ur -x composer.lock -x vendor example-app-d/routes/web.php example-app/routes/web.php
--- example-app-d/routes/web.php 2024-07-16 14:39:20.000000000 +0000
+++ example-app/routes/web.php 2024-08-17 12:00:00.000000000 +0000
@@ -1,7 +1,9 @@
<?php
use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\PostController;
Route::get('/', function () {
- return view('welcome');
+ return '<p><a href="/posts">Post</a></p>';
});
+Route::resource('posts', PostController::class);
Discussion