Values and Variables
A template is useful when parts of it change from one generation to the next. This lesson shows how a value gets into a file, where the fields of the dialog come from, how to hold a list, and how a value can be computed from other values.
A value in a file
Write the name of a variable between double braces. CodeBaker replaces it with the value the user typed:
Hello, {{ name }}!
{{ name }} is the syntax of Pebble, the template language CodeBaker uses. It works in every template with no setup, and it is the same for a text file, a Markdown file or an HTML page. The same braces can also hold a filter such as {{ name | tableize }}.
Where the fields of the dialog come from
The first step of the dialog is a form. Each field of the form is one entry of vars in codebaker.config.json5, the main configuration file of every template. The name of the entry is the name of the variable, and its value is what the field shows by default.
This example has everything this lesson talks about. Click codebaker.config.json5 in the template to see the vars and json sections, and compare them with the dialog below it:
https://codebaker.dev/examples/fields-and-json.zipNeeds 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.
{
// The folder name when the template is installed under .CodeBaker/
id: "fields-and-json",
name: "Entity Schema",
// Each entry becomes a text field in the dialog, with the value as its default.
vars: {
entity: "BlogPost",
// A value can be computed from the ones above it (never from the ones below it).
table: "{{ entity | tableize }}",
},
// Lists and objects go here. The dialog shows this as an editable area, and each top-level key becomes a variable.
json: {
fields: [
{ name: "id", type: "int" },
{ name: "title", type: "string" },
// The same {{ }} works here, and is computed from the fields above.
{ name: "{{ table }}_total", type: "int" },
],
},
}
# {{ entity }}
Table: `{{ table }}`
| Column | Type |
|---|---|
{% for field in fields %}
| {{ field.name }} | {{ field.type }} |
{% endfor %}
- entity
- BlogPost
- table
- {{ entity | tableize }}
{
"fields": [
{
"name": "id",
"type": "int"
},
{
"name": "title",
"type": "string"
},
{
"name": "{{ table }}_total",
"type": "int"
}
]
}
# BlogPost
Table: `blog_posts`
| Column | Type |
|---|---|
| id | int |
| title | string |
| blog_posts_total | int |
To define a field, add an entry:
{
name: "Entity Schema",
vars: {
entity: "BlogPost", // a field named entity, with BlogPost in it
table: "{{ entity | tableize }}", // a field whose value is computed (see below)
},
}
- One entry, one field. The user can change the text of any field before generating.
- A name uses letters, digits and
_, so that it can be written as a placeholder too. - Write values as strings. A boolean or a number (
hasTimestamps: true,perPage: 20) is accepted too and shown as text. Every field is a plain text field. - Typed
true,falseand numbers become real ones. The texttruein a field is a boolean once resolved, so{% if hasTimestamps %}works: see Conditions and Loops. - Some values need no field.
DATE,USER,PROJECT_NAMEand the others are always there: see System Properties.
JSON in a minute
A field holds one piece of text. A list of columns, or of menu items, does not fit in one field. For that the config has a place for JSON, a plain-text format for data. It has only a few pieces:
{ "name": "title", "type": "string" } an object: names, each with a value
[ "id", "title", "price" ] a list: values in a row
"text" 42 3.5 true false a string, a number, a boolean
An object can hold lists, and a list can hold objects, as far as you like. The config files of a template are JSON5: JSON that also allows comments, trailing commas, keys without quotes and single quotes. That is why the examples here can have comments, and why you can leave a comma after the last item. Nothing more about JSON is needed to start.
Lists and objects: the json section
Lists and objects go in the json section of the config:
json: {
fields: [
{ name: "id", type: "int" },
{ name: "title", type: "string" },
],
},
- The dialog shows it as an editable area under the fields, so the user can add, change or remove items before generating. If what is written there is not valid JSON, a red
Invalid JSON: ...message appears under it and Next stays disabled until it is fixed. - Each top-level key becomes a variable. Here
fieldsis a list, and a loop writes something for each item: see Conditions and Loops. Inside the loop,field.nameandfield.typeare the parts of one item. - Lists and objects do not work in
vars. An array or an object written there is dropped without a message. Put it injson.
Dynamic values: how they are computed
A value does not have to be written out. Any entry of vars, and any text in the JSON area, can contain {{ ... }}, and CodeBaker resolves it: it computes the value with Pebble, from the values it already knows. In the example above, table is {{ entity | tableize }}, which becomes blog_posts, and a column of the JSON area is {{ table }}_total, which becomes blog_posts_total.
The order matters, and it is always the same:
- The
vars, from the top down. Each entry can use the entries above it and nothing below it.tablecan useentitybecauseentitycomes first. A value that uses an entry written below it gets empty text, because that one is not known yet. - Then the JSON area, with all the resolved fields. Its
{{ }}can use any field of the form, and the system properties. - Then the types: after resolving, text that is exactly
true,falseor a plain number becomes a boolean or a number.
You can see it happen. Step 2 of the dialog writes a line for every field whose value changed (table: {{ entity | tableize }} → blog_posts), and it shows the JSON area as resolved when it differs from what was written. See Step 2. The Names and Filters lesson lists the filters that are most used here.
A value in a file name
A file or folder name takes the same placeholders as the contents:
{{ title }}-card.html → Welcome-card.html
models/{{ name }}.php → models/User.php
{{ module }}/Service.php → Auth/Service.php
A file or folder name takes a variable, or a dotted path into an object ({{ name }}, {{ user.name }}). A filter, an expression, an if or a loop written in a name is not evaluated and stays as written, and the operating system limits which characters a name may have (Windows does not allow | or :). If a name needs a filter, either:
- compute it in
vars, for exampletable: "{{ name | tableize }}", and write{{ table }}in the name, or - keep a plain file name in the template and give the output name in the mapping file, where the
targetis rendered by Pebble:"model.php": "{{ name | tableize }}/{{ name }}.php".
The names in open, expand and replace of the config follow the same rule.
A value inside an object
To reach into an object or a list, use a dot: {{ user.name }}, {{ user.profile.email }}, {{ items.0.title }}. This is how you use what a loop gives you, or a value from the json section. A path that does not exist is left as written in a name, and prints nothing in a file.
Choosing a placeholder style
Pebble's {{ name }} is valid text in many files, but not in all of them. In a PHP class name or a variable name the braces are a syntax error, and your IDE underlines the template while you edit it:
class {{ className }} extends Controller{}For those files, declare another way to write a value. The placeholders list in the config takes one or more styles. Each style contains the token VAR, which stands for the variable name:
{
placeholders: ["___VAR___"],
}
With that, ___name___ means the variable name, and it is a valid identifier in most languages, so the IDE has nothing to complain about:
https://codebaker.dev/examples/declared-placeholders.zipNeeds 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.
<?php
class ___name___Controller
{
protected string $table = '___table___';
}
{
// The folder name when the template is installed under .CodeBaker/
id: "declared-placeholders",
name: "Controller",
// Declares the style ___name___. VAR stands for the variable name, so ___name___ is the variable `name`.
placeholders: ["___VAR___"],
vars: {
name: "Order",
// computed from `name`; a value can only use the variables above it
table: "{{ name | tableize }}",
},
}
- name
- Order
- table
- {{ name | tableize }}
<?php
class OrderController
{
protected string $table = 'orders';
}
Look at what the example shows:
- the class name, the file name and the string all use
___name___and___table___, and the PHP file is valid PHP; tablewas not typed by anyone: its default is an expression that CodeBaker computed fromname;{{ name }}would still work in the same file, because Pebble is always on.
A few more things about declared styles:
- Nothing but
{{ }}is built in. A style works only when it is listed underplaceholders. - You can declare several styles and use them in the same template:
["___VAR___", "%VAR%", "[[VAR]]"]. A style works in file contents, in file and folder names, and inopen,expandandreplace. _dot_stands for a dot in a declared style:___user_dot_name___meansuser.name, and%user_dot_name%too.- A hyphen is not part of a variable name, so
___var-name___does not work. A name uses letters, digits and underscores. - Choose a style that is valid where you use it. The IDE checks your template, not CodeBaker. Some places, such as a CSS value, accept no identifier at all: see Replacing Real Values.
| Language | A style that usually keeps the IDE quiet |
|---|---|
| PHP, JavaScript, TypeScript, Java, Kotlin, Python | ___name___ (declare ___VAR___) |
| HTML, XML | ___name___, or Pebble's own {{ name }}, which HTML accepts as text |
| CSS, SCSS | no placeholder fits a value: use Replacing Real Values |
Next
- Conditions and Loops: generate a part of a file only when something is true, or once for each item of a list.
- Names and Filters: compute class, table and route names from one value.
- Configuration Reference: every detail of
vars.