Coding Standards
Copyright Header
All PHP files must include the standard copyright header:
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)
*/Naming Conventions
Directory and File Naming
| Type | Rule | Example |
|---|---|---|
| Module directories | PascalCase | Common/, FrontEnd/ |
| Controller files | PascalCase + Controller | ProductController.php |
| Model files | PascalCase | Product.php |
| View directories | lowercase | front/, panel/ |
| Language files | lowercase + underscore | common.php, front.php |
| Route files | lowercase + underscore | front.php, panel-api.php |
| Config files | lowercase + underscore | config.json |
Namespaces
- Panel-related: use
Panelnamespace - Frontend-related: use
Frontnamespace - View directories:
panel/andfront/ - Route prefixes:
panel.andfront.
Class and Method Naming
php
// Controllers: PascalCase + Controller
ProductController.php
// Models: PascalCase
Product.php
// Method naming
public function index() // List page
public function show() // Detail page
public function store() // Save
public function update() // Update
public function destroy() // DeleteVariable Naming
php
// Model variables: camelCase
$product = Product::find(1);
$orderItem = OrderItem::first();
// Collection variables: plural form
$products = Product::all();
$orderItems = OrderItem::get();
// Boolean variables: is/has/should prefix
$isActive = true;
$hasStock = true;Route Usage
php
// Correct
front_route('product.index') // Frontend route
panel_route('product.index') // Panel route
// Wrong
route('product.index') // Do NOT use bare route()View Standards
Layout Inheritance
blade
{{-- Correct --}}
@extends('panel::layouts.app')
{{-- Wrong --}}
@extends('admin::layouts.app')Indentation
Blade files must use 2-space indentation. The project's .editorconfig configures [*.blade.php] with 2-space indent.
blade
{{-- Correct ✅ --}}
@section('content')
<div class="container">
<div class="row">
@if($condition)
<p>Content</p>
@endif
</div>
</div>
@endsection
{{-- Wrong ❌ — 4 spaces --}}
@section('content')
<div class="container">
<p>Content</p>
</div>
@endsectionEmpty Data Display
blade
<x-common-no-data />JavaScript Standards
Code Placement
Always use @push('footer') instead of @push('scripts').
blade
{{-- Correct --}}
@push('footer')
<script>
$(document).ready(function() {
console.log('jQuery works');
});
</script>
@endpush
{{-- Wrong — will not work --}}
@push('scripts')
<script>
// InnoShop renders JS via @stack('footer'), not @stack('scripts')
</script>
@endpushPanel Axios Notes
Panel axios responses are auto-unwrapped by the interceptor. In then callbacks, res is already the backend JSON object — no res.data needed.
javascript
// Wrong — extra .data layer
axios.post(url, params).then(function (res) {
if (res.data && res.data.success) { ... }
});
// Correct — res is already response.data
axios.post(url, params).then(function (res) {
if (res && res.success) { ... }
});catch errors are NOT unwrapped
The error in catch is still the raw axios error object. Use error.response.data to get error info.
JavaScript i18n
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 Development Standards
Controller
php
class ProductController extends Controller
{
public function store(ProductRequest $request): mixed
{
try {
$product = $this->productService->create($request->validated());
if ($request->ajax()) {
return json_success('Saved', $product);
}
return redirect()->panel_route('product.index')
->with('success', 'Saved');
} catch (Throwable $e) {
// Error handling...
}
}
}Model
php
class Product extends Model
{
protected $fillable = ['name', 'price'];
protected $casts = [
'price' => 'decimal:2',
'active' => 'boolean',
];
}Repository
php
class ProductRepo extends BaseRepo
{
protected string $model = Product::class;
public function builder(array $filters = []): Builder
{
$builder = $this->modelQuery();
// Filter conditions...
return $builder;
}
}Error Handling
Controller Error Handling
- Wrap service instantiation in try-catch within action methods
- Use
session()->flash('error', $e->getMessage())to display errors - Always return a view, do not redirect to an error page
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 Layer Error Handling
- Service classes can throw exceptions directly
- Use specific exception types
- Provide meaningful error messages
Best Practices
- Use a Service layer for business logic
- Use the Repository pattern for data access
- Use FormRequest for request validation
- Keep Controllers lightweight — handle requests only
- Use the Hook system for extensibility
- Follow Laravel best practices
- Keep code clean and maintainable