# Coding Conventions This document outlines the coding standards and conventions used in RSpade and RSX. Following these conventions ensures consistency across the codebase and compatibility with the framework. ## PHP Conventions ### Naming Conventions **CRITICAL**: This project uses **underscore_case** for all variables and functions, NOT camelCase. ```php // CORRECT - Use underscore_case $user_name = 'John'; $is_active = true; function get_user_profile() { } function calculate_total_price() { } // WRONG - Do not use camelCase $userName = 'John'; // ❌ $isActive = true; // ❌ function getUserProfile() { } // ❌ function calculateTotalPrice() { } // ❌ ``` ### Class and File Naming - **Classes**: Use PascalCase - **Files**: Match class name exactly with `.php` extension - **Constants**: Use UPPERCASE_WITH_UNDERSCORES ```php // Class naming class UserController { } // ✓ PascalCase class BlogPostManager { } // ✓ PascalCase class Rsx_Controller { } // ✓ Legacy RSX base classes use underscore // Constants const MAX_ATTEMPTS = 5; // ✓ const DEFAULT_CACHE_TTL = 3600; // ✓ ``` ### Database Conventions - **Tables**: Use plural snake_case (e.g., `users`, `blog_posts`) - **Columns**: Use snake_case (e.g., `created_at`, `user_id`) - **Foreign Keys**: Use `{singular_table}_id` (e.g., `user_id`, `post_id`) ```php // Model with database fields class BlogPost extends Model { protected $table = 'blog_posts'; // plural snake_case protected $fillable = [ 'title', 'content', 'user_id', // foreign key 'published_at', // snake_case 'view_count' // snake_case ]; } ``` ### Code Style RSpade follows [PSR-12](https://www.php-fig.org/psr/psr-12/) with these specific requirements: ```php namespace App\Models; use App\RSpade\Core\Database\Models\Rsx_Model_Abstract; /** * Blog post model * * @property int $id * @property string $title * @property string $content * @property int $user_id * @property Carbon $created_at */ class BlogPost extends Rsx_Model_Abstract { protected $fillable = [ 'title', 'content', 'user_id', 'status' ]; /** * Get the author of the post * * @return BelongsTo */ public function author() { return $this->belongsTo(User::class, 'user_id'); } /** * Mark post as published * * @return bool */ public function mark_as_published() // underscore_case method { $this->status = 'published'; $this->published_at = now(); return $this->save(); } /** * Get formatted title * * @return string */ public function get_formatted_title() // underscore_case method { $max_length = 50; $title_text = $this->title; if (strlen($title_text) > $max_length) { return substr($title_text, 0, $max_length) . '...'; } return $title_text; } } ``` ## RSX Attribute Conventions ### Attribute Usage Use PHP 8 attributes for routing, caching, and other decorators: ```php use App\RSpade\Core\Attributes\{Route, Cache, RateLimit, Middleware}; class UserController extends Rsx_Controller { #[Route('/users', methods: ['GET'])] #[Cache(ttl: 300, tags: ['users'])] #[RateLimit(max_attempts: 60)] public function list_users($params) { $page_number = $params['page'] ?? 1; $items_per_page = 20; return $this->paginate_results($page_number, $items_per_page); } #[Route('/users/:id', methods: ['GET'])] #[Middleware(['auth'])] public function get_user($params) { $user_id = $params['id']; $include_posts = $params['include_posts'] ?? false; return $this->load_user_data($user_id, $include_posts); } } ``` ### Attribute Placement - **Class-level**: Applies to all methods in the class - **Method-level**: Applies to specific method only - **Multiple attributes**: Stack vertically for readability ```php #[Cors(allowed_origins: ['https://app.example.com'])] // Class-level #[ApiVersion('v2')] // Class-level class ApiController extends Rsx_Api { #[Route('/api/data', methods: ['GET'])] // Method-level #[Cache(ttl: 600)] // Method-level #[RateLimit(max_attempts: 100)] // Method-level public function get_data($params) { // Implementation } } ``` ## JavaScript Conventions ### ES6+ Standards Use modern JavaScript with ES6+ features: ```javascript // Use const/let, arrow functions, destructuring const process_user_data = (user_data) => { const { first_name, last_name, email } = user_data; const full_name = `${first_name} ${last_name}`; return { full_name, email, display_name: full_name.toLowerCase() }; }; // Class with static methods for namespacing class UserManager { static async load_user(user_id) { const response = await fetch(`/api/users/${user_id}`); const user_data = await response.json(); return user_data; } static format_user_name(first_name, last_name) { return `${first_name} ${last_name}`.trim(); } } ``` ### jQuery Usage Use jQuery for DOM manipulation where appropriate: ```javascript $(document).ready(() => { // Use underscore_case for functions and variables const init_user_form = () => { const $form = $('#user_form'); const $submit_button = $form.find('.submit_button'); $submit_button.on('click', async (e) => { e.preventDefault(); const form_data = get_form_data($form); await submit_form_data(form_data); }); }; const get_form_data = ($form) => { const form_values = {}; $form.find('input, select, textarea').each(function() { const field_name = $(this).attr('name'); const field_value = $(this).val(); form_values[field_name] = field_value; }); return form_values; }; init_user_form(); }); ``` ## SCSS/CSS Conventions ### File Organization ```scss // resources/sass/app.scss @import 'variables'; // Custom variables @import 'bootstrap'; // Bootstrap framework @import 'layout'; // Layout styles @import 'components'; // Component styles @import 'pages'; // Page-specific styles ``` ### Naming Conventions Use BEM-style naming with underscores: ```scss // Component naming .user_card { padding: 1rem; border: 1px solid $border_color; &__header { font-size: 1.2rem; margin_bottom: 0.5rem; } &__content { color: $text_color; } &--featured { border_color: $primary_color; background: $featured_background; } } // Page-specific styles .page_is_user_profile { .profile_section { margin_bottom: 2rem; } .profile_stats { display: flex; gap: 1rem; } } ``` ## Database Migration Conventions ### Naming Use descriptive names with timestamps: ```php // Good migration names 2024_01_15_000001_create_users_table.php 2024_01_15_000002_add_avatar_to_users_table.php 2024_01_15_000003_create_blog_posts_table.php ``` ### Structure ```php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateBlogPostsTable extends Migration { public function up() { Schema::create('blog_posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->unsignedBigInteger('user_id'); $table->enum('status', ['draft', 'published', 'archived']); $table->datetime('published_at')->nullable(); // Standard tracking columns $table->unsignedBigInteger('created_by')->nullable(); $table->unsignedBigInteger('updated_by')->nullable(); $table->unsignedBigInteger('deleted_by')->nullable(); $table->timestamps(); $table->softDeletes(); // Indexes $table->index('user_id'); $table->index('status'); $table->index('published_at'); // Foreign keys $table->foreign('user_id')->references('id')->on('users'); }); } public function down() { Schema::dropIfExists('blog_posts'); } } ``` ## Testing Conventions ### Test Naming ```php namespace Tests\Unit; use Tests\TestCase; class UserServiceTest extends TestCase { // Use descriptive test names with underscores public function test_user_can_be_created() { $user_data = [ 'name' => 'John Doe', 'email' => 'john@example.com' ]; $user = $this->create_test_user($user_data); $this->assertNotNull($user->id); $this->assertEquals('John Doe', $user->name); } public function test_user_email_must_be_unique() { $existing_user = User::factory()->create([ 'email' => 'test@example.com' ]); $this->expectException(ValidationException::class); $this->create_user_with_email('test@example.com'); } } ``` ## Documentation Conventions ### PHPDoc Comments ```php /** * Process user registration * * Handles the complete user registration flow including validation, * user creation, email verification, and initial setup. * * @param array $user_data User registration data * @param bool $send_email Whether to send welcome email * @return User The created user instance * @throws ValidationException If validation fails * @throws RegistrationException If registration cannot be completed */ public function process_registration(array $user_data, bool $send_email = true): User { // Implementation } ``` ### Inline Comments Use inline comments sparingly for complex logic: ```php public function calculate_discount($order_total, $user) { $discount_amount = 0; // Apply loyalty discount for users with 10+ orders if ($user->order_count >= 10) { $discount_amount += $order_total * 0.1; } // Apply seasonal discount during December if (date('n') == 12) { $discount_amount += $order_total * 0.05; } // Cap discount at 25% of total $max_discount = $order_total * 0.25; return min($discount_amount, $max_discount); } ``` ## Git Commit Conventions ### Commit Message Format ``` :