I have an application with some complex interactions between fields, such as always requiring that property x
be set to the same as y
when z
is true. There are also rules that require that x >= y
. I’ve handled this to date by doing pre-processing in beforeRules
: if ($entity->z) { $entity->x = $entity->y; }
This works well, and the related rule will always pass.
Now, I’ve got a new requirement to be able to accept data from third-party sources. The data from them may not pass our validation rules, yet it must be saved anyway; the next time someone works on the record (which must happen in order for it to progress through the system’s workflow), the rules will be applied and they’ll need to resolve the discrepancy. This is also easily handled, by just passing 'checkRules' => false
to the save
call when receiving the third-party data.
The problem is, when I skip the rules, that also skips the beforeRules
callback. But I still need for that code to run and set x
when z
is true. I’m looking for a way to make sure that this happens.
Thought #1: Move the business logic to beforeSave
instead of beforeRules
. But now if the entity is edited such that y > x
but also z = true
, the rule will fail and prevent the save from proceeding, so that’s a non-starter.
Thought #2: Add a buildRules
callback on the affected tables, which can skip adding rules based on options passed to save
. This seems like a good option, as it could skip only some rules and leave others in place. However, neither the buildRules
callback nor the rulesChecker
function have the options array passed to them, so implementing this would seem to require overloading both the checkRules
and rulesChecker
functions from the RulesAwareTrait
to get the options to a useful place.
Thought #3: Change the implementation of every relevant rule (of which there are many) so that it can be skipped based on an option passed to the save function. This would probably work, but feels extremely invasive.
Thought #4: Always manually call beforeRules
first when saving with 'checkRules' => false
. This works, but relies on all developers knowing (and remembering) that this is required.
Any options that I haven’t considered here? For now, I think I’m going with #4, but maybe make a PR for Cake to pass the options array into the rule generator functions to support this sort of functionality in the future.