# Where to put beforeRules logic, when rules aren't being checked

**URL:** <https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073>\
**Category:** Need Help\
**Created:** [January 7, 2020, 5:34pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073 "2020-01-07T17:34:18Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Zuluru](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/zuluru/32/1230_2.png) [@Zuluru](https://discourse.cakephp.org/u/Zuluru)\
**Post date:** [January 7, 2020, 5:34pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/1 "2020-01-07T17:34:18Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dreamingmind](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dreamingmind/32/1857_2.png) [@dreamingmind](https://discourse.cakephp.org/u/dreamingmind)\
**Post date:** [January 7, 2020, 8:23pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/2 "2020-01-07T20:23:54Z")

</div>

Drawing from vague memory:

Isn’t there a way to control the order of rules (or the order of event handlers), and a way to stop propagation of events?

If so, there would be the possibility of rearranging things. I guess your 3 party rules would have to be could be the first rule-handler/event?

Don Drake

---

<div class="post-metadata">

**Author:** ![dreamingmind](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dreamingmind/32/1857_2.png) [@dreamingmind](https://discourse.cakephp.org/u/dreamingmind)\
**Post date:** [January 7, 2020, 8:46pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/3 "2020-01-07T20:46:47Z")

</div>

Here are the two event system features that I was thinking about.

[https://book.cakephp.org/4/en/core-libraries/events.html#establishing-priorities](https://book.cakephp.org/4/en/core-libraries/events.html#establishing-priorities)

[https://book.cakephp.org/4/en/core-libraries/events.html#stopping-events](https://book.cakephp.org/4/en/core-libraries/events.html#stopping-events)

---

<div class="post-metadata">

**Author:** ![Zuluru](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/zuluru/32/1230_2.png) [@Zuluru](https://discourse.cakephp.org/u/Zuluru)\
**Post date:** [January 7, 2020, 9:11pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/4 "2020-01-07T21:11:04Z")

</div>

Rules don’t get passed the event, so no way for one rule to cause the rest to get skipped. Any one returning false would cause the save to fail.

It’s possible to change the order of handlers for a particular event, but no way to change the order of the events themselves.

So, unless I’m missing something in your suggestions, this won’t get me there.

---

<div class="post-metadata">

**Author:** ![dreamingmind](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dreamingmind/32/1857_2.png) [@dreamingmind](https://discourse.cakephp.org/u/dreamingmind)\
**Post date:** [January 7, 2020, 10:18pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/5 "2020-01-07T22:18:52Z")

</div>

No. Not missing anything.

How about:

I’m guessing the 3rd party save is on a particular Table class that is only used in this case? If so, can you override the Table::save() call there to:

- manually trigger the beforeRules event
- manually set checkRules = false
- call the parent::save()

?

---

<div class="post-metadata">

**Author:** ![Zuluru](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/zuluru/32/1230_2.png) [@Zuluru](https://discourse.cakephp.org/u/Zuluru)\
**Post date:** [January 7, 2020, 10:47pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/6 "2020-01-07T22:47:24Z")

</div>

It’s tables that are used regularly by other functions too; the third-party data import transmogrifies their structure into ours. But I could add a “saveRegardless” type function that does what you suggest for these situations, that’s not a bad idea. (It’s not a _great_ solution, IMO, but sometimes “not bad” is the best we can hope for. 🙂)

---

<div class="post-metadata">

**Author:** ![dreamingmind](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dreamingmind/32/1857_2.png) [@dreamingmind](https://discourse.cakephp.org/u/dreamingmind)\
**Post date:** [January 7, 2020, 11:25pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/7 "2020-01-07T23:25:10Z")

</div>

I’m with you on ‘not bad’.

can you:

- Write a sub-class of ThirdParty table
- Let all your old code use oldThirdParty table as normal
- Make your new 3rd party problem calls use the newThirdParty table (with the save override)

The advantage I imagine is

- everyone uses the familiar save() call
- The controller dedicated to accepting the foreign calls could install the new sub-class table
- All other code/controllers keep using the old table class

The success of this strategy really depends on how focused the intake of the problem 3rd party data is on a single controller.

This has been a fun problem to consider. Especially since I’m not on the hook for a working solution.

---

<div class="post-metadata">

**Author:** ![dreamingmind](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dreamingmind/32/1857_2.png) [@dreamingmind](https://discourse.cakephp.org/u/dreamingmind)\
**Post date:** [January 7, 2020, 11:35pm UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/8 "2020-01-07T23:35:32Z")

</div>

What I’m trying to develop is an OO solution to your option #4, which was proposed as a bit of procedure in existing code.

Finding a single place to compose in a Class that does the right thing while sticking to the familiar interface `Table->save($entity)` would represent a ‘best’ solution.

Having a new `saveRegardless` call is ok. It would wrap up all current and future implementation details in one method. But you’d still have to know and remember to use it. That was the legitimate concern with option #4

---

<div class="post-metadata">

**Author:** ![dmuenstermann](https://avatars.discourse-cdn.com/v4/letter/d/b9bd4f/32.png) [@dmuenstermann](https://discourse.cakephp.org/u/dmuenstermann)\
**Post date:** [January 14, 2020, 10:48am UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/9 "2020-01-14T10:48:07Z")

</div>

Maybe I’m getting your problem wrong, but did you think about using different validation-sets?

So you can e.g. simply accept the 3rd-party records at first and when they are edited/updated later, you let a more hardened validationset take effect…?

---

<div class="post-metadata">

**Author:** ![Zuluru](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/zuluru/32/1230_2.png) [@Zuluru](https://discourse.cakephp.org/u/Zuluru)\
**Post date:** [January 16, 2020, 12:47am UTC](https://discourse.cakephp.org/t/where-to-put-beforerules-logic-when-rules-arent-being-checked/7073/10 "2020-01-16T00:47:33Z")

</div>

That only applies to validation, not rules. I think the closest approximation to what you’re talking about that works with rules is #3 from my original post.
