In a single working day I can easily write code in three naming styles. Laravel models are PascalCase, database columns are snake_case, the JavaScript on the same page is camelCase, and the URLs are kebab-case. Switch to a WordPress plugin and the rules change again.
None of this is random. Each style belongs to a convention, and following the convention is what makes frameworks like Laravel “just work”. This is the reference I wish I'd had when I started, with the conventions I follow on Laravel and WordPress projects.
The naming styles in one table
| Style | Example | Typical use |
|---|---|---|
| camelCase | ledgerEntry | JavaScript variables and functions, PHP methods |
| PascalCase (StudlyCase) | LedgerEntry | PHP and JavaScript classes, Laravel models, React components |
| snake_case | ledger_entry | Database tables and columns, WordPress functions |
| kebab-case | ledger-entry | URLs, CSS classes, file names |
| CONSTANT_CASE | LEDGER_ENTRY | Constants and environment variables |
Laravel's documentation calls PascalCase “StudlyCase”, which is where its Str::studly() helper gets its name.
Laravel's conventions
Laravel relies on naming conventions so you don't have to configure everything by hand. Imagine an accounting app with invoices, customers, suppliers and ledger entries:
| Thing | Convention | Example |
|---|---|---|
| Model | Singular, PascalCase | LedgerEntry |
| Table | Plural, snake_case | ledger_entries |
| Columns | snake_case | opening_balance, created_at |
| Foreign key | Singular model name + _id | customer_id |
| Pivot table | Both singular model names, alphabetical, snake_case | product_supplier |
| Controller | Singular, PascalCase + Controller | LedgerEntryController |
| Methods and relationships | camelCase | ledgerEntries() |
| Route URI | Plural, kebab-case | /ledger-entries |
| Config keys | snake_case | config('app.timezone') |
| Environment variables | CONSTANT_CASE | DB_DATABASE |
Follow these and Eloquent works out the table name, foreign keys and pivot tables on its own. Break them and you'll be passing table and key names into every relationship by hand.
One detail that surprises people
Relationship methods are camelCase, but when a model is turned into JSON or an array, Laravel converts loaded relationships to snake_case keys. A ledgerEntries() relationship appears as "ledger_entries" in the JSON. This behaviour comes from the $snakeAttributes property on Eloquent models, which is true by default.
Accessors follow the same pattern. In Laravel 9 and later you define an accessor as a camelCase method, such as protected function fullName(): Attribute, but you read it as $customer->full_name. If you want it in JSON, add 'full_name' to the model's $appends array.
WordPress and WooCommerce conventions
The WordPress PHP coding standards are older than PSR and look different from Laravel:
- Functions and variables: lowercase snake_case, with a unique prefix to avoid clashes with other plugins, for example
kbl_get_order_total(). - Classes: capitalised words separated by underscores, such as
WC_ProductorWP_Query. - Hooks: snake_case, such as
woocommerce_before_cart. - Files: lowercase with hyphens. Class files are usually named after the class with a
class-prefix, for exampleclass-wc-product.php. - Constants: CONSTANT_CASE, such as
WP_DEBUG.
When I move between a Laravel codebase and a WooCommerce plugin, the hardest part isn't the logic; it's remembering which conventions belong to which project. Mixing them in one codebase is what makes code look messy to the next developer.
JavaScript and CSS conventions
- Variables and functions: camelCase, such as
orderTotalorcalculateTax(). - Classes and React or Vue components: PascalCase, such as
InvoiceTable. - Constants: CONSTANT_CASE for true constants, such as
MAX_UPLOAD_MB. - CSS classes: kebab-case, such as
.invoice-table, which also matches what Bootstrap and most CSS frameworks use.
Where the styles collide: JSON between Laravel and JavaScript
The real friction is at the boundary. Laravel sends JSON with snake_case keys straight from the database columns, while JavaScript code naturally wants camelCase. You'll see the same split in third-party APIs. Stripe's REST API returns snake_case fields such as client_secret and amount_received, while Stripe.js on the front end takes options in camelCase, such as clientSecret.
There are three sensible ways to deal with it:
- Keep snake_case end to end. Use the API's keys as they are in your JavaScript:
invoice.created_at. It looks slightly odd in JavaScript, but there's nothing to convert and nothing to get out of sync. For small projects it's often the most practical option. - Convert in an API Resource. Laravel's API Resources let you choose the exact keys you send, for example
'createdAt' => $this->created_at. It's explicit, and it also stops you accidentally exposing columns you didn't mean to. - Convert once on the front end. A small helper that converts keys when the response arrives keeps your JavaScript consistently camelCase. The important part is doing it in one place, not ad hoc in every component.
customerId and the other half sends customer_id.When an API response doesn't match what you expect, paste it into the JSON Formatter. Seeing the structure laid out with proper indentation makes a misnamed key obvious in seconds.
A trap on Windows: table name case in MySQL
Many developers in Pakistan, me included, learned on XAMPP or WAMP on Windows. Here's a trap that catches people when they first deploy to a Linux server. MySQL's lower_case_table_names setting defaults to 1 on Windows, where table names are treated case-insensitively, but to 0 on Linux, where they're case-sensitive. A query for Invoices that worked on your laptop can fail in production because the table is actually named invoices.
The simple fix is to follow the convention: always name tables and columns in lowercase snake_case, and the problem never appears.
Converting between styles quickly
Inside a Laravel app, the Str helper handles conversions:
| Helper | Input | Output |
|---|---|---|
Str::camel() | ledger_entry | ledgerEntry |
Str::studly() | ledger_entry | LedgerEntry |
Str::snake() | LedgerEntry | ledger_entry |
Str::kebab() | LedgerEntry | ledger-entry |
Outside your code, when you're renaming a list of fields from a client's spreadsheet into column names or turning headings into URL slugs, paste the list into the Case Converter. Each line is converted separately, so “Opening Balance” and “Customer ID” become opening_balance and customer_id in one click. It supports camelCase, PascalCase, snake_case, kebab-case and CONSTANT_CASE, and everything runs in your browser.