开发者

doctrine deny updates on related records from the main record

I have 开发者_如何转开发this situation in doctrine: Invoice with its related InvoiceItems. I'd like to deny changes on InvoiceItems if the related Invoice is closed, maybe in the preSave method of the Invoice. How should I do it?


There are several possibilities, how would you achieve such thing, it really depends on your situation, application business logic etc. (Also, you should specify Doctrine branch, 1.x or 2.x)

  1. It's integrity constraint, do it at database level: Say you have a preUpdate trigger on your table, which would either raise exception (only some DBs can do that, think Postgres, Oracle) or do nothing, just stop the update operation.

  2. As @jensgram suggested, you could override the validator method, and either return invalid state, or you might throw an exception, because this is not a validation problem as it is integrity and logic constraint, IMHO

  3. Use preSave method, either skip the save (but user won't notice a thing) or again, throw Exception from it.

  4. In your application, you should not allow the user to get into this situation at all. Your GUI should be explicit and clearly display that invoice is closed and cannot be modified further.

Personally, I would use this scenario: First, solution No. 4, don't allow the user to do it. To be on the safe side, implement a trigger directly in the database, to prevent a bug in my application from changing closed invoice, but do it silently, no errors raised, just skip the save operation (solution No. 1). This approach has one more advantage. If you find yourself in the situation, when you need to connect to the database from any other application, that integrity constraint will be preserved.


In Doctrine 1.x I've overridden the isValid() method on the concrete Doctrine_Record (here, LineItem):

public function isValid($deep = false, $hooks = true) {
    $q = Doctrine_Query::create()
         ->select('co.uid')
         ->from('Company co')
         ->leftJoin('co.workers w')
         ->leftJoin('co.customers cust')
         ->leftJoin('cust.workCases wc')
         ->where('w.uid = ?', $this->_workerUid) // This LineItem's Worker must _always_ have the same Company as this LineItem (through LineItem -> WorkCase -> Customer -> Company)
         ->andWhere('wc.state = ?', WorkCase::STATE_OPEN) // This LineItem's WorkCase must be open
         ->andWhere('wc.uid = ?', $this->_caseUid);
    $company = $q->fetchOne();

    return $company !== false && parent::isValid($deep, $hooks);
}

There are a few things to note here:

  1. I'm trying to fetch the Company based on some criteria (amongst others, the state must be "open"). The end result depends on whether I find a company that satisfies these criteria ($company !== false)
  2. isValid() is overriding a deeper implementation. Always make the end result dependent on parent::isValid($deep, $hooks) (we must be certain that the original implementation is also happy).
  3. You could probably make the "fetch company record" simpler in your case.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜