Recipes
The recipe language describes how fields (or more generally: variables) are evaluated. In the context of Brezel, recipes bind a field’s value to the values of other fields that are referenced in the recipe. For example, it allows you to automatically calculate totals in invoices, define field values inside workflows, and even evaluate expressions inside bakery planner configuration files.
You can test recipes in the Brezel CLI by opening the interactive shell or by opening the DevTools in the Brezel UI and evaluating the recipe in the console.
php bakery shell> recipe("'Hello world!'")= "Hello world!"> recipe("'Hello world!'")"Hello world!"Syntax
The syntax of the recipe language is based on the EcmaScript syntax with a few additions. Supported are mathematical, logical and bitshift operations, function calls, property, subscript and method access and of course common literals.
Math and logic
Say we want a field total to always be the sum of two other fields net and tax. Then we define the recipe of total to be:
net + taxIf we do not know the total tax but the tax rate, we write something like:
net * (1 + tax_rate)and if we know that the tax rate is constant, we can of course write:
net * 1.19These operations work the same way you are probably accustomed to from other languages or Excel. We can also test for truth values:
net > 10would return either true or false. We can also negate the result:
!(net > 10)Testing for equality is done using ==:
net == 10And its negation
!(net == 10)or
net != 10This would return true if and only if net is not equal to 10.
We can also test multiple conditions:
net < 10.25 && net > 5.75Would return true if net is below 10.25 and greater than 5.75.
Available operators: + (add), - (subtract), * (multiply), / (divide), > (greater than), >= (greater than or equal), < (less than), <= (less than or equal), == (equals), != (not equals), ! (not), && (and), || (or), & (bitwise and), | (bitwise or), ^ (bitwise xor), >> (right bitshift), << (left bitshift).
String operations
Recipes are not limited to numbers. If we have two fields first_name and last_name but want the field full_name to be automatically calculated, we define the following recipe:
first_name + " " + last_nameNote that we can also use single quotes instead:
first_name + " " + last_nameProperties
Fields can not only be numbers or text, but also objects that have properties. Fields of type select for example hold relations to other Brezel entities. Say we want to define a field total that depends on the price of the selected product product and another field quantity. Then we can write the following recipe:
product.price * quantitySubscript syntax works too in this case:
product["price"] * quantityFunctions
Often, we want to transform values in some way. There are pre-defined functions available for that, for example when we want to round numbers for a currency field:
round(product.price * quantity)Conditionals
Ternary operator
Sometimes we want the value to be something in one case, but be something different in another case. Then we can write:
condition ? x : yIf the condition is true, it will return x, otherwise it will return y. Example:
category == "Food" ? 0.07 : 0.19will return 0.07 if the category in question is Food, otherwise we get 0.19.
Null-coalescing operator
If you just want the resulting expression if it does not evaluate to null and another expression otherwise, you can use the null-coalescing operator:
specific_tax ?? default_taxThis will return the value of specific_tax if specific_tax is not null, otherwise it returns default_tax.
List fields
Fields of type list are expressed as arrays. Having a field positions consisting of list entries with child fields product, unit_price and quantity, we can express the sum position_total of one single list entry to be:
unit_price * quantityImagine then that we have the following positions, here illustrated in the form (product, position_total): (Donut, 4.50), (Pretzel, 2.80) and (Bread, 6.00). Applying the following recipe:
positions[0].position_totalreturns 4.50. But we can do more, because instead of specifying the index inside [], writing something like…
positions[*].position_totalwill give us the following result:
[4.5, 2.8, 6.0]This is called wildcard expression, because it maps each entry in the list to whatever is defined after .. Here, each position in the list is mapped to position.position_total.
If we now want to sum all position_total fields of the positions list, we can write
sum(positions[*].position_total)which gives us 13.30.
Arrow functions
Some functions can also accept functions as input, which are defined in the form of (a, b, ..., c) => y where a, b, ..., c) are parameters andy is the return value. This is especially useful when we want to filter a list by some criterium:
[1, 2, 3, 4, 5].filter((a) => a > 3)which yields [4, 5].
Math
sum
Signature
sum(items: list)
Description
Returns the sum of all items in the list.
Example
>>> sum([1, 2, 3, 5, 8])19avg
Signature
avg(items: list)
Description
Returns the average of all items in the list.
Example
>>> avg([1, 2, 3, 5, 8])3.8round
Signature
round(value: number, precision: number)
Description
Returns the value rounded to the specified precision.
Example
>>> round(3.1415, 2)3.14floor
Signature
floor(value: number, precision: number)
Description
Returns the value rounded down to the specified precision.
Example
>>> floor(3.1415, 2)3.14ceil
Signature
ceil(value: number, precision: number)
Description
Returns the value rounded up to the specified precision.
Example
>>> ceil(3.1415, 2)3.15max
Signature
max(items: list)
Description
Returns the maximum of all items in the list.
Example
>>> max([1, 2, 3, 5, 8])8min
Signature
min(items: list)
Description
Returns the minimum of all items in the list.
Example
>>> min([1, 2, 3, 5, 8])1sin
Signature
sin(value: number, precision: number)
Description
Returns the sine of the value as a number between -1 and 1.
Example
>>> sin(30, 5)0.500cos
Signature
cos(value: number, precision: number)
Example
>>> cos(30, 5)0.866exp
Signature
exp(value: number, precision: number)
Example
>>> exp(1, 5)2.718pow
Signature
pow(value: number, exponent: number)
Example
>>> pow(2, 3)8sqrt
Signature
sqrt(value: number, precision: number)
Example
>>> sqrt(9, 5)3.0000abs
Signature
abs(value: number)
Example
>>> abs(-3)3vector
Signature
vector(values: list)
Example
>>> vector([[1, 2, 3]])Vector { [[1, 2, 3]]}rand
Signature
rand(start: number, end: number)
Example
>>> rand(1, 10)6String
len
Signature
len(value: string)
Example
>>> len("hello")5substr
Signature
substr(value: string, start: number, end: number)
Example
>>> substr("hello", 1, 3)ellcontains
Signature
contains(value: string, search: string)
Example
>>> contains("hello", "ell")truetoUpperCase
Signature
toUpperCase(value: string)
Example
>>> toUpperCase("hello")HELLOrandom.password(minLength, maxLength)
Signature
random.password(minLength: number, maxLength: number)
Example
>>> random.password(6, 8)cR9mVzp6Object
len
Signature
len(value: object)
Example
>>> len({"hello": "world", "foo": "bar"})2contains
Signature
contains(value: object, key: string)
Example
>>> contains({"hello": "world", "foo": "bar"}, "hello")truelist
Signature
list(value: object)
Example
>>> list([{"hello": "world", "foo": "bar"}])ListWrapper([{"hello": "world", "foo": "bar"}])relation
Returns a new object with only the required keys for storing relations.
Signature
relation(value: object)
Example
>>> relation({"id": 4, "module_id": 12, "hello": "world", "foo": "bar"}){"id": 4, "module_id": 12}join
Signature
join(glue: string, pieces: list)
Example
>>> join(', ', ['a', 'b', 'c', 'd'])'a, b, c, d'Date
today
Signature
today()
Example
>>> today()2018-09-12now
Signature
now()
Example
>>> now()2018-09-12T13:22:02-05:00date
Signature
date(value: string, timezone: string)
Example
>>> date('2018-06-19')Date { date: 2018-09-12 00:00:00.0 UTC (+00:00)}duration
Signature
duration(value: string)
Example
>>> duration("1h30m")90mUser
user -> User
Returns the current user from local store. The user gets updated on every refresh.
Signature
user()
Example
>>> user()User { id: 4, module_id: 1, email: "steffen@brezel.io", name: "Steffen"}hasRole
Signature
hasRole(role: string)
Example
>>> hasRole("ROLE_ADMIN")trueJSON
json.decode
Signature
json.decode(value: string)
Example
>>> json.decode("[1, 2, 3]")[1, 2, 3]json.encode
Signature
json.encode(value: string)
Example
>>> json.encode("[1, 2, 3]")[1, 2, 3]Regex
regex.quote
Signature
regex.quote(value: string, delimiter: string)
Example
>>> regex.quote("-=hello world=-", "/")\\-=hello\\ world\\=-regex.matchAll
Signature
regex.matchAll(pattern: string, subject: string)
Example
>>> regex.matchAll("hello (\\w+)", "hello world")[ { "0": "hello world", "1": "world" }]Misc
env API
Signature
env(name: string)
Example
>>> env("SERVICE_URL")"https://api.my-service.com"- Math functions:
sum,avg,round,floor,ceil,max,min,sin,cos,exp,pow,sqrt,abs,vector,rand - String functions:
len,substr,contains,toUpperCase,random.password(minLength, maxLength) - Object functions:
len,contains,list,relation,join - Date functions:
today,now,date,duration - User functions:
user,hasRole - JSON functions:
json.decode,json.encode - Regex functions:
regex.quote(string, delimiter),regex.matchAll(pattern, subject) - Misc functions:
env