Skip to main content

Inflection & Case Filters

CodeBaker adds a set of Pebble filters for turning one variable into every naming style a feature needs: class names, table names, route segments, constants and labels.

Only entity is typed in this example. Every other name is computed from it, from the top down:

One name, every spelling
Template.CodeBaker/naming/
Download the template (.zip)https://codebaker.dev/examples/naming.zip
Install in IntelliJ IDEA

Needs the CodeBaker plugin and an open project. Another IDE, or the button does nothing? Copy the link, then in the IDE right-click any folder → New → Generate from Template → Install Template from URL… and paste it. You always get a confirmation dialog before anything is installed.

codebaker.config.json5
{
// The folder name when the template is installed under .CodeBaker/
id: "naming",

name: "Naming",

// Only `entity` is typed. The others are computed from it, from the top down.
vars: {
entity: "BlogPost",
table: "{{ entity | tableize }}",
route: "{{ entity | pluralize | dasherize }}",
foreign: "{{ entity | foreignKey }}",
constant: "{{ entity | constantCase }}",
property: "{{ entity | variable }}",
},
}
names.md
# {{ entity }}

| Where it is used | Name |
|---|---|
| Class | `{{ entity }}` |
| Table | `{{ table }}` |
| URL | `/{{ route }}` |
| Foreign key | `{{ foreign }}` |
| Constant | `{{ constant }}` |
| Property | `${{ property }}` |
Typed in the dialogstep 1, Custom Properties
entity
BlogPost
table
{{ entity | tableize }}
route
{{ entity | pluralize | dasherize }}
foreign
{{ entity | foreignKey }}
constant
{{ entity | constantCase }}
property
{{ entity | variable }}
Generated resultwhat lands in your project
names.md
# BlogPost

| Where it is used | Name |
|---|---|
| Class | `BlogPost` |
| Table | `blog_posts` |
| URL | `/blog-posts` |
| Foreign key | `blog_post_id` |
| Constant | `BLOG_POST` |
| Property | `$blogPost` |

The filters in that example, and a few more:

{{ entity | pluralize }} {# BlogPost -> BlogPosts #}
{{ entity | tableize }} {# BlogPost -> blog_posts #}
{{ entity | variable }} {# BlogPost -> blogPost #}
{{ entity | dasherize }} {# BlogPost -> blog-post #}

Filters are used inside a Pebble expression, in two main places:

  • In template files and in computed vars of codebaker.config.json5.
  • In codebaker.advanced.json5, whose target and data values are rendered by Pebble too, for example target: "{{ entity | tableize }}.php".
{
"vars": {
"entity": "BlogPost",
"table": "{{ entity | tableize }}", // blog_posts
"routePrefix": "{{ entity | pluralize | dasherize }}", // blog-posts
"fk": "{{ entity | foreignKey }}", // blog_post_id
}
}

Variables are resolved from top to bottom, so entity must be defined before table, routePrefix and fk, which use it. The Values and Variables lesson explains how.

File and folder names

Names of files and folders only get plain text substitution of simple placeholders such as ___var___ or {{ var }}. They are not run through Pebble, so a filter such as {{ entity | tableize }} written directly in a file name is not evaluated and stays as written. For the full Pebble syntax in an output name, use one of two ways: compute the value in a vars entry (like table above) and use it as ___table___ in the name, or keep a plain file name in the template and set the output name with a target in codebaker.advanced.json5, which is rendered by Pebble first (see Advanced Templates).


Any Input Style Works​

Case filters split the input into words first, so these inputs all produce the same result:

Inputcamelizeunderscore
user_profileUserProfileuser_profile
user-profileUserProfileuser_profile
user profileUserProfileuser_profile
userProfileUserProfileuser_profile
UserProfileUserProfileuser_profile
USER_PROFILEUserProfileuser_profile

Acronyms become ordinary words: HTMLParser → HtmlParser / html_parser. Digits stay attached to the word before them: address1Line → address1_line.


Inflection​

FilterExampleResult
pluralizePersonPeople
pluralize(count)Item with count = 1Item (singular when the count is the number 1 or -1)
singularizecategoriescategory
tableizeUserCategoryuser_categories
classifyuser_categoriesUserCategory
foreignKeyUserCategoryuser_category_id
foreignKey('_uuid')Usersuser_uuid

pluralize and singularize change only the last word and keep its casing, so compound names stay intact:

Inputpluralize
UserPersonUserPeople
order_itemorder_items
ORDER_ITEMORDER_ITEMS
App\Models\CategoryApp\Models\Categories

Both filters leave a word alone if it is already in the target form (users | pluralize → users). Irregular and uncountable words are handled, including common code words: child/children, criterion/criteria, status/statuses, database/databases, index/indices, sheep, news, series, software, metadata, feedback. Words ending in data (such as metadata) and the word media are left unchanged in both directions, so data | singularize stays data. Two irregular words are the exception when pluralizing: datum gives data and medium gives media.


Case Conversion​

Each filter and its aliases do the same thing, so use whichever name your team knows.

FilterAlias(es)blog_post_id →
camelizepascalCase, studlyCaseBlogPostId
variablecamelCaseblogPostId
underscoresnakeCaseblog_post_id
dasherizekebabCaseblog-post-id
humanizetitleCaseBlog Post Id
constantCasescreamingSnakeCaseBLOG_POST_ID
dotCaseblog.post.id
pathCaseblog/post/id
spaceCaseblog post id
trainCaseBlog-Post-Id
sentenceCaseBlog post id
flatCaseblogpostid
upperFlatCaseBLOGPOSTID
delimit('::')blog::post::id

String Helpers​

FilterExampleResult
slugCrème Brûlée à la carte!creme-brulee-a-la-carte
slug('_', false)My Blog Post!My_Blog_Post
ucfirstuserNameUserName
lcfirstUserNameuserName

slug(separator = '-', lowercase = true) converts accented letters to plain ASCII and replaces everything else with the separator.


Chaining and Lists​

Filters chain left to right:

{{ 'UserCategory' | pluralize | constantCase }} {# USER_CATEGORIES #}

A list is converted item by item, so it combines with Pebble's join:

SELECT {{ fields | underscore | join(', ') }} FROM {{ entity | tableize }};
{# fields = ["firstName", "LastName"], entity = "BlogPost"
-> SELECT first_name, last_name FROM blog_posts; #}

A missing or null variable renders as an empty string, the same as without a filter.


Things to Watch For​

  • slug, ucfirst and lcfirst do not split words: BlogPost | slug gives blogpost, not blog-post. Use dasherize for that.
  • pluralize(count) treats only a numeric 1 or -1 as singular. The text '1' is not a number and gets pluralized.
  • humanize and titleCase do not strip a trailing _id: user_id | humanize gives User Id.