Skip to content

Coding Standards

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

TypeRuleExample
Module directoriesPascalCaseCommon/, FrontEnd/
Controller filesPascalCase + ControllerProductController.php
Model filesPascalCaseProduct.php
View directorieslowercasefront/, panel/
Language fileslowercase + underscorecommon.php, front.php
Route fileslowercase + underscorefront.php, panel-api.php
Config fileslowercase + underscoreconfig.json

Namespaces

  • Panel-related: use Panel namespace
  • Frontend-related: use Front namespace
  • View directories: panel/ and front/
  • Route prefixes: panel. and front.

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()    // Delete

Variable 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>
@endsection

Empty 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>
@endpush

Panel 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

  1. Use a Service layer for business logic
  2. Use the Repository pattern for data access
  3. Use FormRequest for request validation
  4. Keep Controllers lightweight — handle requests only
  5. Use the Hook system for extensibility
  6. Follow Laravel best practices
  7. Keep code clean and maintainable