开发规范
版权声明
所有 PHP 文件必须包含标准版权头:
php
<?php
/**
* Copyright (c) Since 2024 InnoShop - All Rights Reserved
*
* @link https://www.innoshop.com
* @author InnoShop <team@innoshop.com>
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/命名规范
目录与文件命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 模块目录 | 大驼峰 (PascalCase) | Common/, FrontEnd/ |
| 控制器文件 | 大驼峰 + Controller | ProductController.php |
| 模型文件 | 大驼峰 | Product.php |
| 视图目录 | 小写 | front/, panel/ |
| 语言文件 | 小写 + 下划线 | common.php, front.php |
| 路由文件 | 小写 + 下划线 | front.php, panel-api.php |
| 配置文件 | 小写 + 下划线 | config.json |
命名空间
- 后台相关:使用
Panel命名空间 - 前台相关:使用
Front命名空间 - 视图目录:
panel/和front/ - 路由前缀:
panel.和front.
类与方法命名
php
// 控制器:大驼峰 + Controller
ProductController.php
// 模型:大驼峰
Product.php
// 方法命名
public function index() // 列表页
public function show() // 详情页
public function store() // 保存
public function update() // 更新
public function destroy() // 删除变量命名
php
// 模型变量:小驼峰
$product = Product::find(1);
$orderItem = OrderItem::first();
// 集合变量:复数形式
$products = Product::all();
$orderItems = OrderItem::get();
// 布尔变量:is/has/should 前缀
$isActive = true;
$hasStock = true;路由使用
php
// 正确
front_route('product.index') // 前台路由
panel_route('product.index') // 后台路由
// 错误
route('product.index') // 禁止使用视图规范
布局继承
blade
{{-- 正确 --}}
@extends('panel::layouts.app')
{{-- 错误 --}}
@extends('admin::layouts.app')缩进规范
Blade 文件必须使用两个空格缩进。项目根目录的 .editorconfig 文件已配置 [*.blade.php] 使用两个空格缩进。
blade
{{-- 正确 ✅ --}}
@section('content')
<div class="container">
<div class="row">
@if($condition)
<p>内容</p>
@endif
</div>
</div>
@endsection
{{-- 错误 ❌ — 四个空格 --}}
@section('content')
<div class="container">
<p>内容</p>
</div>
@endsection空数据展示
blade
<x-common-no-data />JavaScript 规范
代码位置
必须使用 @push('footer') 而不是 @push('scripts')。
blade
{{-- 正确 --}}
@push('footer')
<script>
$(document).ready(function() {
console.log('jQuery 正常工作');
});
</script>
@endpush
{{-- 错误 — 这样不会生效 --}}
@push('scripts')
<script>
// InnoShop 使用 @stack('footer') 渲染 JS
</script>
@endpushPanel Axios 注意事项
后台(Panel)的 axios 经过拦截器处理,响应已被自动解包一层。then 回调中 res 直接就是后端返回的 JSON 对象,不需要 再通过 res.data 访问。
javascript
// 错误 — 多了一层 .data
axios.post(url, params).then(function (res) {
if (res.data && res.data.success) { ... }
});
// 正确 — res 已经是 response.data
axios.post(url, params).then(function (res) {
if (res && res.success) { ... }
});catch 中的 error 没有被解包
catch 中的 error 仍然是原始的 axios error 对象,需要通过 error.response.data 获取错误信息。
JavaScript 多语言
javascript
<script>
window.translations = {
system: {
submit: "{{ trans('common/button_submit') }}",
cancel: "{{ trans('common/button_cancel') }}"
},
plugin: {
save: "{{ trans('PluginName::common/message_save_success') }}"
}
};
</script>MVC 开发规范
控制器
php
class ProductController extends Controller
{
public function store(ProductRequest $request): mixed
{
try {
$product = $this->productService->create($request->validated());
if ($request->ajax()) {
return json_success('保存成功', $product);
}
return redirect()->panel_route('product.index')
->with('success', '保存成功');
} catch (Throwable $e) {
// 错误处理...
}
}
}模型
php
class Product extends Model
{
protected $fillable = ['name', 'price'];
protected $casts = [
'price' => 'decimal:2',
'active' => 'boolean',
];
}仓储层
php
class ProductRepo extends BaseRepo
{
protected string $model = Product::class;
public function builder(array $filters = []): Builder
{
$builder = $this->modelQuery();
// 查询条件...
return $builder;
}
}错误处理规范
控制器错误处理
- 在 action 方法中使用 try-catch 包装服务实例化
- 使用
session()->flash('error', $e->getMessage())显示错误 - 始终返回视图,不要重定向到错误页面
php
public function index()
{
try {
$service = new PluginService;
$data = $service->getData();
return view('PluginName::index', compact('data'));
} catch (Exception $e) {
session()->flash('error', $e->getMessage());
return view('PluginName::index', ['data' => collect()]);
}
}服务层错误处理
- 服务类可以直接抛出异常
- 使用具体的异常类型
- 提供有意义的错误消息
最佳实践
- 使用 Service 层处理业务逻辑
- 使用 Repository 模式处理数据访问
- 使用 FormRequest 进行请求验证
- 保持 Controller 轻量,只负责请求处理
- 合理使用 Hook 机制扩展功能
- 遵循 Laravel 最佳实践
- 保持代码简洁和可维护性