CakePHP:高度依赖的模型,回调问题和数据工作流


CakePHP: Highly codependent models, callback issues and data workflow

我在OrderItem模型之间有一种非常有凝聚力的关系。

Order hasMany Item
Item belongsTo Order
Item hasMany ChildItem
ChildItem is alias for Item (it's a recursive model)

订单模型具有特殊的Order::prepare()功能。它在所有附加的Behavior上触发Order.prepare事件,这些Behavior控制、验证和修改Item数据,如发货、项目/订单重量、数量、折扣,即验证和暂存保存操作的数据。它还设置Order.total, Order.weight, Order.status, ...字段。

任何行为也可以根据其限制条件(库存限制、重量限制、增值税、任何)停止准备。

Item添加到现有Order:时

  1. 从数据库中检索订单及其所有现有项目
  2. 新的Item数据将添加到Items数组中
  3. 订单为PREPARED(运行的所有行为、Item.subtotal s、Order.total、权重等都将重新计算…)
  4. 如果准备成功,所有新的(和修改的)订单和项目数据都将与Order.saveAssociated一起保存

在仔细考虑了多种方法后,我选择了第三种,但我陷入了困境:

beforeSave回调

此回调将确保这些回调和计算在每个项目上运行,但它会使检索相关订单数据和项目以及运行回调变得更加困难。也很难确定数据是否已经准备好,递归也是一个问题。更难返回准备好的数据,而不是保存

扩展Model::save方法

递归也是一个问题,在不保存的情况下无法返回准备好的数据,我通常避免扩展save()。

取消准备和保存过程的耦合通过使用自定义Model方法和回调(例如order::prepare和order::commit)为订单数据操作创建一个特殊的工作流。行为在非标准事件(beforePrepare、beforeCommit等)上运行,Model::save()保持不变。

我很乐意提供更多的细节,但这确实是一个庞大的模型,这将是一个很长的问题,我已经尽可能多地总结了。任何关于正确方法的想法或例子都将不胜感激。

我不确定你的系统有多复杂,但你为什么不创建一个OrderPrepare类(我通常让这个类扩展Object而不是AppModel或Order,并把它放在Model文件夹或Lib文件夹中),传入Order对象,然后你可以在这个类中执行你想要的任何逻辑呢?

我已经成功地完成了这项工作,我正在发布结果和我当前的解决方案。我不认为这是完全传统的工作流程,但它做得很好。

我已经创建了BaseOrderBaseItem类,它们扩展了AppModel,并打算用您自己的类进行扩展。

我创建了BaseOrder::fetch()BaseOrder::stage()BaseOrder::commit()方法,它们是工作的核心操作。所有这些都触发之前的之后的callback,它们是自定义事件名称,与自定义OrderEvent(扩展CakeEvent)对象一起发布。

BaseOrder::fetch()

这用于从数据库中获取订单的当前状态。

  • 从其相关的BaseItem类和行为中收集所有关联需求。这允许每个行为动态创建自定义关联(例如,Couponed行为附加Order hasMany Coupon),修改query=>contain数据,以确保Order::fetch()为工作流检索正确的数据
  • 运行生成的查询
  • 运行AfterFetch回调,允许任何Behavior或OrderItem实例附加或调整所需的其他数据

BaseOrder::stage()

接收任何类型的与订单相关的数据作为参数,执行Order::fetch(),应用和修改数据,并运行所有行为和OrderItem的beforeStagestageafterStage回调。

该方法预计将返回订单的真实状态,并解决所有细节,如产品数据、财务细节、折扣、行为附加数据等。

BaseOrder::commit()

采用暂存订单数组,验证财务状况,并将数据提交到数据库。

此外,BaseItemBaseOrder都有一个afterSave回调,这会触发相关订单记录财务的重新计算,以防任何数据被手动保存到数据库中。

这样,我可以轻松地为所有的铃声和口哨声提交订单,或者我可以直接更新订单/项目数据,并且仍然具有财务数据的完整性。

我正在考虑将计算端转移到MySQL存储过程中,该存储过程将在每次保存后自动运行,但它会取消应用程序的微调控制(如舍入错误、默认税率等)

我期待着有人去不,不,你做错了,并用一个更好的主意来启发我,因为这一切都让我觉得很肮脏。

我遇到的最大"问题"是获取所有相关的BaseItem扩展模型,并遍历数据数组以"捕获"它们进行计算。这允许我拥有不同的模型(如ProductItem、ShippingItem)以及它们之间的不同关系,但会带来一些开销。