I have a very cohesive relation between Order
and Item
models.
Order hasMany Item
Item belongsTo Order
Item hasMany ChildItem
ChildItem is alias for Item (it's a recursive model)
The Order Model has a special Order::prepare()
function. It fires a Order.prepare
event on all attached Behaviors, which control, validate and modify Item data, such as shipping, item/order weights, quantity, discounts, ie. validating and staging the data for a save operation. It also sets the Order.total, Order.weight, Order.status, ...
fields.
Any behavior can also stop the preparation based on its constraints (stock limits, weight limits, vat, anything).
When Item
is added to an existing Order
:
- The Order and all its existing Items are retrieved from the database
- The new Item data is added to the Items array
- The Order is PREPARED (all behaviors run,
Item.subtotal
s,Order.total
, weights and such are recalculated ...) - If prepare is successful, all the new (and modified) Order and Item data is saved with Order.saveAssociated.
After carefully considering multiple approaches I chose the third, but I got stuck:
beforeSave callbacks
This callback would ensure that these callbacks and calculations run on every item, but it makes it harder to retrieve related order data and items and run callbacks. It is also harder to know for sure if the data has already been prepared or not and recursion is also a problem. Harder to return prepared data instead of saving it.
Extending the Model::save
methods
Recursion is also a problem, no way of returning the prepared data without saving and I generally avoid extending save().
Decoupling the preparation and save processes Creating a special workflow for order data manipulation through use of custom Model methods and callbacks (eg. Order::prepare and Order::commit). Behaviors run on non-standard events (beforePrepare, beforeCommit, etc..) and Model::save() stays untouched.
I would gladly provide additional details, but it is really a massive model and it would be a long question, I've summarized as much as possible. Any ideas or examples regarding the correct approach will be much appreciated.
I am not sure how complex your system is - but why dont you create an OrderPrepare class ( i typically make this class extend Object not AppModel or Order, and put it in the Model folder or I put it in the Lib folder), pass in the Order object and then you can perform whatever logic you want in that class?