下面做一个完整但尽量简洁的例子。
场景:
- 人类操作员通过 Blade 页面创建订单
- 外部系统也可以通过 API 创建同一类订单
- 两条入口共用同一个
OrderService - 订单落库后广播
OrderCreated - 监听器排入队列,在后台准备物料
先看总流转图:
浏览器表单 / API 请求
|
v
Route
|
v
EnsureWorkshopOpen
|
v
Controller
|
v
OrderService
|
v
Eloquent
|
v
orders 表
|
v
OrderCreated Event
|
v
Queued Listener
|
v
Queue Worker#1. 迁移:orders 仓库表
<?php
// database/migrations/xxxx_xx_xx_xxxxxx_create_orders_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('customer_name');
$table->string('product');
$table->unsignedInteger('quantity');
$table->string('status')->default('pending');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('orders');
}
};#2. 模型:账册翻译官
<?php
// app/Models/Order.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
protected $fillable = [
'customer_name',
'product',
'quantity',
'status',
];
}#3. 中间件:工坊是否接单
<?php
// app/Http/Middleware/EnsureWorkshopOpen.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureWorkshopOpen
{
public function handle(Request $request, Closure $next): Response
{
abort_if(config('workshop.closed'), 503, '工坊暂停接单');
return $next($request);
}
}#4. 事件:订单已创建
<?php
// app/Events/OrderCreated.php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderCreated
{
use Dispatchable, SerializesModels;
public function __construct(
public Order $order,
) {}
}#5. 队列监听器:后台准备物料
<?php
// app/Listeners/PrepareWorkshopMaterials.php
namespace App\Listeners;
use App\Events\OrderCreated;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class PrepareWorkshopMaterials implements ShouldQueue
{
use InteractsWithQueue, Queueable;
public function handle(OrderCreated $event): void
{
Log::info('后台物料准备中', [
'order_id' => $event->order->id,
'product' => $event->order->product,
'quantity' => $event->order->quantity,
]);
}
}#6. 事件注册:把广播接到后场
<?php
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use App\Events\OrderCreated;
use App\Listeners\PrepareWorkshopMaterials;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
//
}
public function boot(): void
{
Event::listen(OrderCreated::class, PrepareWorkshopMaterials::class);
}
}#7. 服务层:真正接活的工位
<?php
// app/Services/OrderService.php
namespace App\Services;
use App\Events\OrderCreated;
use App\Models\Order;
class OrderService
{
public function create(array $input): Order
{
$order = Order::query()->create([
'customer_name' => $input['customer_name'],
'product' => $input['product'],
'quantity' => (int) $input['quantity'],
'status' => 'pending',
]);
OrderCreated::dispatch($order);
return $order;
}
}#8. Blade 控制器:给人看的窗口
<?php
// app/Http/Controllers/OrderPageController.php
namespace App\Http\Controllers;
use App\Models\Order;
use App\Services\OrderService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class OrderPageController extends Controller
{
public function __construct(
private OrderService $orders,
) {}
public function index(): View
{
return view('orders.index', [
'orders' => Order::query()->latest()->get(),
]);
}
public function store(Request $request): RedirectResponse
{
$data = $request->validate([
'customer_name' => ['required', 'string', 'max:50'],
'product' => ['required', 'string', 'max:100'],
'quantity' => ['required', 'integer', 'min:1'],
]);
$order = $this->orders->create($data);
return redirect()
->route('orders.index')
->with('status', "工单 #{$order->id} 已创建");
}
}#9. API 控制器:给程序看的窗口
<?php
// app/Http/Controllers/OrderApiController.php
namespace App\Http\Controllers;
use App\Services\OrderService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class OrderApiController extends Controller
{
public function __construct(
private OrderService $orders,
) {}
public function index(): JsonResponse
{
return response()->json(
\App\Models\Order::query()->latest()->get()
);
}
public function store(Request $request): JsonResponse
{
$data = $request->validate([
'customer_name' => ['required', 'string', 'max:50'],
'product' => ['required', 'string', 'max:100'],
'quantity' => ['required', 'integer', 'min:1'],
]);
$order = $this->orders->create($data);
return response()->json($order, 201);
}
}#10. 路由:两种入口,共用同一工坊核心
<?php
// routes/web.php
use App\Http\Controllers\OrderPageController;
use App\Http\Middleware\EnsureWorkshopOpen;
use Illuminate\Support\Facades\Route;
Route::middleware(EnsureWorkshopOpen::class)->group(function () {
Route::get('/orders', [OrderPageController::class, 'index'])->name('orders.index');
Route::post('/orders', [OrderPageController::class, 'store'])->name('orders.store');
});<?php
// routes/api.php
use App\Http\Controllers\OrderApiController;
use App\Http\Middleware\EnsureWorkshopOpen;
use Illuminate\Support\Facades\Route;
Route::middleware(EnsureWorkshopOpen::class)->group(function () {
Route::get('/orders', [OrderApiController::class, 'index']);
Route::post('/orders', [OrderApiController::class, 'store']);
});#11. Blade 视图:直接给人看的页面
{{-- resources/views/orders/index.blade.php --}}
<h1>订单工坊</h1>
@if (session('status'))
<p>{{ session('status') }}</p>
@endif
<form method="POST" action="{{ route('orders.store') }}">
@csrf
<label>
客户名
<input type="text" name="customer_name" required>
</label>
<label>
产品
<input type="text" name="product" required>
</label>
<label>
数量
<input type="number" name="quantity" min="1" required>
</label>
<button type="submit">创建订单</button>
</form>
<hr>
<ul>
@foreach ($orders as $order)
<li>#{{ $order->id }} / {{ $order->customer_name }} / {{ $order->product }} / {{ $order->status }}</li>
@endforeach
</ul>#12. 运行这套工坊
php artisan migrate
php artisan queue:work这套示例最值得你注意的不是语法,而是结构:
人类入口和程序入口不同
但它们没有各写一套业务
两边都经过:
Route
-> Middleware
-> Controller
-> OrderService
-> Eloquent
-> Event
-> Queue这就是 Laravel 的城区感。