You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel 11餐厅菜单CRUD应用最佳实践审核及关键缺失项排查请求

Laravel 11餐厅菜单CRUD应用最佳实践审核及关键缺失项排查请求

看起来你的Laravel 11餐厅菜单CRUD应用已经有了扎实的基础,但我们可以从最佳实践鲁棒性角度做不少优化,同时补上一些关键缺失的部分。下面我分模块拆解你的实现,给出具体改进建议:


一、本地化中间件(LocalizationMiddleware)优化

当前实现仅依赖Session存储语言选择,存在体验和安全性短板,以下是针对性改进:

现有问题

  1. 未验证语言是否为系统支持的合法值,存在注入风险
  2. 仅支持Session切换,未提供URL参数(如?lang=en)的便捷切换方式
  3. 无默认语言 fallback 逻辑,首次访问或Session过期时体验不佳

改进后的中间件代码

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class LocalizationMiddleware
{
    // 定义系统支持的语言列表(可抽离到config/app.php配置项)
    protected $supportedLocales = ['en', 'id'];

    public function handle(Request $request, Closure $next): Response
    {
        // 优先级:URL参数 > Session > 浏览器Accept-Language > 系统默认
        $locale = $this->getPreferredLocale($request);
        
        if (in_array($locale, $this->supportedLocales)) {
            app()->setLocale($locale);
            session()->put('locale', $locale); // 持久化选择
        }

        return $next($request);
    }

    protected function getPreferredLocale(Request $request): string
    {
        // 1. 优先处理URL参数
        if ($request->has('lang') && in_array($request->lang, $this->supportedLocales)) {
            return $request->lang;
        }

        // 2. 读取Session已保存的语言
        if (session()->has('locale')) {
            return session('locale');
        }

        // 3. 读取浏览器偏好语言
        $browserLocale = $request->getPreferredLanguage($this->supportedLocales);
        if ($browserLocale) {
            return $browserLocale;
        }

        // 4. 系统默认语言
        return config('app.locale');
    }
}

配套优化

  • 简化语言切换路由,支持友好的URL:
Route::get('set-lang/{locale}', function ($locale) {
    if (in_array($locale, ['en', 'id'])) {
        session()->put('locale', $locale);
    }
    return back()->with('success', __('messages.language_updated'));
})->name('set.lang');

二、Bootstrap表单验证优化

当前仅依赖后端验证,缺乏前端即时反馈,以下是前后端结合的优化方案:

1. 后端验证规则增强

MenuControllerstore/update方法中,细化验证规则,与数据库字段约束对齐:

$data = $request->validate([
    'name' => 'required|string|max:100', // 匹配数据库name字段长度
    'price' => 'required|numeric|min:0|max:999999.99', // 限制价格范围
    'category' => 'required|string|max:50',
    'description' => 'required|string|max:1000',
    'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', // 限制文件类型/大小(2MB)
]);

2. 前端Bootstrap整合

在Blade视图中,结合Laravel错误消息和Bootstrap的表单状态类,实现即时反馈:

<!-- 示例:菜单名称输入框 -->
<div class="mb-3">
    <label for="name" class="form-label">{{ __('messages.menu_name') }}</label>
    <input type="text" class="form-control @error('name') is-invalid @enderror" 
           id="name" name="name" value="{{ old('name', $menu->name ?? '') }}" required>
    @error('name')
        <div class="invalid-feedback">{{ $message }}</div>
    @enderror
</div>

3. 可选:前端JS即时验证

添加轻量级JS验证,减少后端请求:

<script>
document.addEventListener('DOMContentLoaded', function() {
    const form = document.getElementById('menu-form');
    form.addEventListener('submit', function(e) {
        let isValid = true;
        // 验证名称非空
        const nameInput = document.getElementById('name');
        if (!nameInput.value.trim()) {
            nameInput.classList.add('is-invalid');
            isValid = false;
        } else {
            nameInput.classList.remove('is-invalid');
        }
        // 验证价格为正数
        const priceInput = document.getElementById('price');
        if (priceInput.value <= 0 || isNaN(priceInput.value)) {
            priceInput.classList.add('is-invalid');
            isValid = false;
        } else {
            priceInput.classList.remove('is-invalid');
        }
        if (!isValid) e.preventDefault();
    });
});
</script>

三、CRUD实现的最佳实践优化

1. 控制器优化

搜索逻辑的安全性

当前orWhere会导致查询冲突,用闭包包裹避免与其他过滤条件冲突:

// 替换原搜索逻辑
if ($request->has('search')) {
    $searchTerm = $request->search;
    $query->where(function($q) use ($searchTerm) {
        $q->where('name', 'like', "%{$searchTerm}%")
          ->orWhere('description', 'like', "%{$searchTerm}%");
    });
}

权限控制细化

当前仅用auth中间件,未区分普通用户和管理员,添加角色验证:

  • User模型添加辅助方法:
// app/Models/User.php
public function isAdmin(): bool
{
    return $this->role === 'admin';
}
  • MenuController中添加权限校验:
public function __construct()
{
    $this->middleware('auth')->except(['index', 'show']);
    // 仅管理员可操作菜单
    $this->middleware(function ($request, $next) {
        if (!auth()->user()->isAdmin()) {
            abort(403, 'You are not authorized to perform this action.');
        }
        return $next($request);
    })->except(['index', 'show']);
}

资源清理的安全性

destroy方法中,先检查文件是否存在再删除:

public function destroy(Menu $menu)
{
    if ($menu->image && Storage::disk('public')->exists($menu->image)) {
        Storage::disk('public')->delete($menu->image);
    }
    $menu->delete();
    return redirect()->route('menus.index')->with('success', __('messages.menu_deleted'));
}

2. 模型优化

Menu模型添加访问器,简化图片URL获取:

// app/Models/Menu.php
protected $appends = ['image_url'];

public function getImageUrlAttribute(): ?string
{
    return $this->image ? asset('storage/' . $this->image) : null;
}

在Blade中直接使用$menu->image_url即可,无需手动拼接路径。

3. 路由优化

合并公共与受保护路由,提升可读性:

// 公共菜单路由(所有用户可访问)
Route::resource('menus', MenuController::class)->only(['index', 'show']);
// 管理员专属路由
Route::resource('menus', MenuController::class)->except(['index', 'show'])
    ->middleware(['auth', 'admin']); // 可自定义`admin`中间件

四、关键缺失功能(鲁棒性提升)

1. 本地化错误消息

lang/id/validation.php中添加对应语言的验证消息,让错误提示自动适配当前语言。

2. 分页文本本地化

lang/en/pagination.phplang/id/pagination.php中配置分页按钮文本:

// lang/id/pagination.php
return [
    'previous' => '&laquo; Sebelumnya',
    'next' => 'Selanjutnya &raquo;',
];

3. 自定义错误视图

bootstrap/app.php中配置自定义错误页面,提升用户体验:

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->render(function (Throwable $e, Request $request) {
        if ($e instanceof \Illuminate\Auth\Access\AuthorizationException) {
            return response()->view('errors.403', [], 403);
        }
        if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
            return response()->view('errors.404', [], 404);
        }
    });
})

4. 基础测试(考试加分项)

添加功能测试,覆盖核心场景:

// tests/Feature/MenuTest.php
<?php
namespace Tests\Feature;

use App\Models\Menu;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class MenuTest extends TestCase
{
    use RefreshDatabase;

    public function test_guest_cannot_access_create_menu()
    {
        $response = $this->get(route('menus.create'));
        $response->assertRedirect(route('login'));
    }

    public function test_admin_can_create_menu()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $response = $this->actingAs($admin)->post(route('menus.store'), [
            'name' => 'Test Menu',
            'price' => 10.99,
            'category' => 'food',
            'description' => 'Test Description',
        ]);
        $response->assertRedirect(route('menus.index'));
        $this->assertDatabaseHas('menus', ['name' => 'Test Menu']);
    }
}

五、现有实现的小问题修正

  1. 路由模型绑定规范MenuControllershow(menu $menu)中,参数名应大写为Menu $menu(符合PSR规范)。
  2. Seeder优化:用Laravel Factory替代手动循环,更符合最佳实践:
// database/seeders/MenuSeeder.php
public function run(): void
{
    \App\Models\Menu::factory(10)->create([
        'category' => 'food',
    ]);
}
  1. Flash消息本地化:将with('success', 'Menu created successfully!')替换为本地化键,如with('success', __('messages.menu_created'))

最终总结

你的基础实现已经覆盖了CRUD、Auth、Localization的核心需求,通过上述优化,应用将更符合Laravel最佳实践,鲁棒性和用户体验也会大幅提升。考试中优先确保核心功能的正确性,再逐步添加优化项,重点关注本地化的安全性权限控制代码可维护性,如果时间充足,添加基础测试会成为加分项!

火山引擎 最新活动