Open nilshoerrmann opened 5 years ago
I think such a feature would be very useful :+1:
then:
anyFieldProperty: value
Unfortunately itβs not possible to have two or multiple different when
s in the same YAML structure as the when
keys would override each other. But this syntax could work:
when:
- test:
type: interview
then:
show: false
- test:
type: project
then:
show: true
placeholder: hello
required: true
While we are at it, we could also add testOr
and testNot
tests. Only one of them could be used at a time though.
Regarding negations, it would also be possible to think of it like this:
when:
- test:
type: interview
then:
show: false
otherwise:
placeholder: type here
The then
entry could be optional for full negations.
It would also be possible to incorporate AND
/OR
comparisons directly:
when:
- test:
- type: interview
- category: article
comparison: or
then:
show: false
comparison: and
could be the default (or vice versa) so that you don't have to type it everytime.
@nilshoerrmann that solves the and/or problem quite nicely!
What the suggested syntax probably wouldn't cover yet is a (A or B) and C
condition. Maybe we could make it
when:
tests:
-
conditions:
- type: interview
- category: article
comparison: or
then:
show: false
comparison: and
then:
required: true
So possibly having comparison
and then
on both levels? Or too complicated?
As the issue gets longer, the queries get longer and more complicated π
How do we solve complex conditions with query and keep when
simple? https://github.com/getkirby/ideas/issues/231
The difficulty of queries is, that they can't be evaluated in the Panel (in JS/Vue) themselves. But we would need constant API requests to update the value of the query.
But yes, I see your point... this is getting very complicated and long.
As the issue gets longer, the queries get longer and more complicated π
That's true. Looking at this as user, I'd prefer to have different syntaxes for different tasks. So I only have to use one of these complex syntax constructs when it's actually needed. So keeping the current simple syntax as a base.
What if we support only a simple data structure for now (like Nils proposed above) that is still much more powerful than the current implementation and add query or query-like when
option later when we have a good solution for that?
As queries are strings, it would be very simple to determine whether the test
option is a query (that could be evaluated in the frontend if we write a parser) or if it's a list of fields that will be evaluated with an operator
(which is what I would call the and
/or
option in Nils' example).
I'd like to propose a slightly different option for the expression portion of the when
property that I think would simplify both the implementation and visual syntax.
I suggest starting with a full expression AST and optimizing from there. That way we know that the system is capable of expressing just about anything from the start, and can make some special syntax for the common cases.
test:
kind: and
terms:
- kind: field
field: type
value: interview
- kind: field
field: category
value: article
- kind: or
terms:
- kind: field
field: shippingMethod
value: UPS
- kind: field
field: shippingMethod
value: FedEx
This expression can be evaluated with a few lines of JavaScript:
let evalExpression = page => expression =>
expression.kind === "and"
? expression.terms.every(evalExpression(page))
: expression.kind === "or"
? expression.terms.some(evalExpression(page))
: expression.kind === "not"
? !evalExpression(expression.term)
: expression.kind === "field"
? page[expression.field] === expression.value
: null;
This system is capable of expressing arbitrary boolean expressions of any complexity. But it is kind of hard to read. A few syntax transforms can make it easier:
kind
field are evaluated like this:
a. Having an and
field denotes an and
condition
b. Having an or
field denotes an or
condition
c. Having a not
field denots a not
condition
d. Otherwise it is a field conditionWhich lets us write the condition like this:
test:
and:
- type: interview
- category: article
- or:
- shippingMethod: UPS
- shippingMethod: FedEx
Which lets us write the condition like this:
test:
and:
type: interview
category: article
or:
- shippingMethod: UPS
- shippingMethod: FedEx
Note that the or
condition still needs to be a list since it has two terms for the same field. Otherwise it could be written the same as the and
condition, without the -
s.
The expansions can be performed in a few lines of JavaScript:
// Expansion #2
const expandTerms = list =>
Array.isArray(list)
? list
: Object.entries(list).map(x => Object.fromEntries([x]));
// Expansion #1
const expandCondition = test =>
test.kind // Base, verbose case
? { kind: test.kind, terms: expandTerms(test.terms).map(expandCondition) }
: test.and // Case a
? { kind: "and", terms: expandTerms(test.and).map(expandCondition) }
: test.or // Case b
? { kind: "or", terms: expandTerms(test.or).map(expandCondition) }
: test.not // Case c
? { kind: "not", term: expandCondition(test.not) }
// Case d
: Object.entries(test).map(([field, value]) => ({ kind: "field", field, value }))[0];
A few tests:
// Converted directly from the YAML above
let test = {
"and": {
"type": "interview",
"category": "article",
"or": [
{ "shippingMethod": "UPS" },
{ "shippingMethod": "FedEx" }
]
}
};
let page = {"type": "interview", "category": "article", "shippingMethod": "UPS"};
evalExpression(page)(expandCondition(test));
// => true
let page = {"type": "report", "category": "article", "shippingMethod": "UPS"};
evalExpression(page)(expandCondition(test));
// => false
let page = {"type": "interview", "category": "article", "shippingMethod": "FedEx"};
evalExpression(page)(expandCondition(test));
// => true
let page = {"type": "interview", "category": "article", "shippingMethod": "DHL"};
evalExpression(page)(expandCondition(test));
// => false
Pro: Simple, yet powerful enough to express arbitrary expressions
Con: Fields called "and", "or", "not" in the blueprint cannot be referenced. Using them requires falling back to the verbose syntax. Seems rather unlikely someone would be using fields with those names though.
Pro: You can fall back to the verbose syntax if necessary
Pro: The expression evaluation is small and simple, so can trivially be implemented in both PHP and JS. (Necessary if when
ever supports indicating whether a field is required.)
Pro: Easily expanded to support conditions other than equality by specifying kind
s like 'greaterThan' or 'regex'. Could also expand it to support reusable fragments by using kind: 'reference', reference: 'predeclaredCondition'
.
Pro: Easily expanded to include other boolean operations like implies
. (I've found implication to be very useful when deciding whether a field is required. (type === 'interview' || type === 'meeting') implies (category === 'report' || category === 'book')
means category can be anything if type isn't interview or meeting. The equivalent expression using and/or/not is more complex.
Con: This requires a bit of logic around the handling of the when
property so as to avoid breaking existing blueprints. (I don't think it would be too much though.)
Pro: This is independent of how the "then/otherwise" bit ends up working. It deals only with the conditional part of the when.
Here's what it would look like in a blueprint:
type:
label: Type
type: select
options:
- interview
- report
category:
label: Category
type: select
options:
- article
- editorial
shippingMethod:
label: Shipping Method
type: select
options:
- UPS
- USPS
- FedEx
- DHL
conditionalField:
label: Conditional Field
type: text
when:
and: # Could default to "and", but this would be handled outside of the expression evaluation I wrote above.
type: interview
category: article
or:
- shippingMethod: UPS
- shippingMethod: FedEx
# Still works if when/then/otherwise is implemented
anotherConditionalField:
label: Another Conditional Field
type: text
when:
test:
and: # Again, could default to "and", but that would not be part of the expression evaluation
type: interview
category: article
or:
- shippingMethod: UPS
- shippingMethod: FedEx
then:
required: true
placeholder: "Placeholder Here"
otherwise:
show: false
This should be moved to https://github.com/getkirby/kirby in my eyes.
In https://github.com/getkirby/kirby we really would like to try limiting it to bugs - I know sometimes we put enhancements or features in there as well but especially since Nolt will also serve for creating the roadmap, this should go on Nolt still (in my view).
Okay π I would leave summarizing the topic to one of you others.
Since Kirby 3.1.0, we have the option to conditionally show and hide fields using
when
. Would it be possible to also have athen
option in the blueprints that takes field settings that are applied as soon as thewhen
condition is met? Iβm thinking of different placeholder texts based on conditions or of different textarea sizes depending on the layout needed for a condition.Pseudo blueprint: