引入 Laravel Booth 之後,現在我們可以來開始嘗試撰寫程式了
以前要建立一個商品的 CRUD API,我們需要學習有關路由、migration、控制器等等的觀念,並且自己手刻這些檔案。
現在有了 AI 協助,我們可以更加便利的產生出這些檔案了!
我們在 Junie 的對話框內輸入
我想要寫一個商品的 CRUD API,請幫我生成需要的所有程式
[!NOTE]
每次執行可能結果不會完全相同,但是大致結果會是一樣的
可以看到 Junie 幫我們規劃需要生成的檔案
1. Explore existing codebase for Product-related files to avoid duplication.
2. Implement Product domain:
- Model with casts and fillable
- Migration for products table
- Factory for tests/seeding
3. Build API layer:
- Resource for response shaping
- Form Requests for validation
- Controller with CRUD actions returning proper responses
- API routes and register api routing in bootstrap/app.php
4. Write tests:
- Pest feature tests covering list, create, validate, show, update, delete
5. Run targeted tests:
- Run migrations and then run tests for Product API
6. Fix style with Pint and re-run targeted tests
7. Summarize changes and propose next steps; ask if full test suite should run
接著,比起從 chatGPT 問完之後複製貼上,Junie 已經幫我們生成對應的檔案放好了!
我們來看看 Junie 幫我們生成了哪些檔案,又改了哪些檔案
首先我們看 bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
這是 Laravel 12 新的做法,在 bootstrap/app.php
裡面註冊網頁和 API 對應的路由 PHP。這邊 Junie 幫我們新增了 api 的部分。
接著我們看 routes/api.php
<?php
use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;
Route::apiResource('products', ProductController::class);
這邊使用的是 apiResource
,能簡潔的生成 API 所需的所有路由,略過網頁建立和修改物件的部分。
我們進去看 ProductController
,可以看到對應的程式也已經生成完畢了!
甚至包含到對應的 migration 也都建立好了
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 10, 2);
$table->unsignedInteger('stock')->default(0);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};
到這邊可能有些人會躍躍欲試,想著要開啟 Postman 之類的測試工具,趕快來測試一下剛寫好的 API。
這也不用心急!因為在 Laravel Boost 幫我們生成的 guildline 裡面,其實包含了自動化測試的規範
=== pest/core rules ===
## Pest
### Testing
- If you need to verify a feature is working, write or update a Unit / Feature test.
所以即使我們的提示詞裡面沒有提到自動化測試, Junie 也幫我們生成了對應的自動化測試!
<?php
declare(strict_types=1);
use App\Models\Product;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('lists products (paginated)', function () {
Product::factory()->count(3)->create();
$response = $this->getJson('/api/products');
$response->assertSuccessful()
->assertJsonStructure([
'data' => [
'*' => ['id', 'name', 'description', 'price', 'stock', 'is_active', 'created_at', 'updated_at'],
],
'links',
'meta',
]);
});
it('creates a product', function () {
$payload = [
'name' => 'iPhone 42',
'description' => 'The future phone',
'price' => 1999.99,
'stock' => 5,
'is_active' => true,
];
$response = $this->postJson('/api/products', $payload);
$response->assertCreated();
$response->assertJsonFragment([
'name' => 'iPhone 42',
'description' => 'The future phone',
'price' => 1999.99,
'stock' => 5,
'is_active' => true,
]);
expect(Product::where('name', 'iPhone 42')->exists())->toBeTrue();
});
it('shows a product', function () {
$product = Product::factory()->create();
$this->getJson("/api/products/{$product->id}")
->assertSuccessful()
->assertJsonPath('data.id', $product->id);
});
it('updates a product', function () {
$product = Product::factory()->create();
$this->putJson("/api/products/{$product->id}", [
'name' => 'Updated',
'price' => 10,
])->assertSuccessful()
->assertJsonPath('data.name', 'Updated')
->assertJsonPath('data.price', 10);
});
it('deletes a product', function () {
$product = Product::factory()->create();
$this->deleteJson("/api/products/{$product->id}")
->assertNoContent();
expect(Product::find($product->id))->toBeNull();
});
it('validates store request', function () {
$this->postJson('/api/products', [
'price' => -1,
])->assertUnprocessable()
->assertJsonValidationErrors(['name', 'price']);
});
並且在生成之後,Junie 會先將生成的結果跑過測試,如果沒有全部通過的話會再進行調整。
所以我們可以肯定這段程式是會通過我們的自動化測試的
執行之後的結果如下
php artisan test
PASS Tests\Unit\ExampleTest
✓ that true is true 0.01s
PASS Tests\Feature\Auth\AuthenticationTest
✓ login screen can be rendered 0.20s
✓ users can authenticate using the login screen 0.34s
✓ users can not authenticate with invalid password 0.24s
✓ users can logout 0.01s
PASS Tests\Feature\Auth\EmailVerificationTest
✓ email verification screen can be rendered 0.02s
✓ email can be verified 0.01s
✓ email is not verified with invalid hash 0.01s
✓ already verified user visiting verification link is redirected without firing event again 0.01s
PASS Tests\Feature\Auth\PasswordConfirmationTest
✓ confirm password screen can be rendered 0.02s
✓ password can be confirmed 0.02s
✓ password is not confirmed with invalid password 0.22s
PASS Tests\Feature\Auth\PasswordResetTest
✓ reset password link screen can be rendered 0.02s
✓ reset password link can be requested 0.22s
✓ reset password screen can be rendered 0.25s
✓ password can be reset with valid token 0.28s
PASS Tests\Feature\Auth\RegistrationTest
✓ registration screen can be rendered 0.02s
✓ new users can register 0.06s
PASS Tests\Feature\DashboardTest
✓ guests are redirected to the login page 0.01s
✓ authenticated users can visit the dashboard 0.03s
PASS Tests\Feature\ExampleTest
✓ it returns a successful response 0.01s
PASS Tests\Feature\ProductApiTest
✓ it lists products (paginated) 0.01s
✓ it creates a product 0.01s
✓ it shows a product 0.01s
✓ it updates a product 0.01s
✓ it deletes a product 0.01s
✓ it validates store request 0.01s
PASS Tests\Feature\Settings\PasswordUpdateTest
✓ password can be updated 0.05s
✓ correct password must be provided to update password 0.04s
PASS Tests\Feature\Settings\ProfileUpdateTest
✓ profile page is displayed 0.03s
✓ profile information can be updated 0.04s
✓ email verification status is unchanged when email address is unchanged 0.04s
✓ user can delete their account 0.02s
✓ correct password must be provided to delete account 0.03s
Tests: 34 passed (116 assertions)
Duration: 2.39s
到這邊,我們不僅僅程式寫完了,對應的 migration 做好了,甚至還生成了對應的自動化測試檔案,並全部執行通過!
今天的部分就到這邊,我們明天見!