A couple of days ago I blogged about how Doctrine’s SoftDelete behavior can keep other listeners’ preDelete() hooks from firing; after a bit of coding this morning, I believe I have a solution.
In my initial setup, I had applied the SoftDelete behavior in the model itself via the following YAML config:
1 2 3 | |
Then, in my Zend_Application bootstrap, I was assigning my ACL listener like so:
1 2 3 | |
Since behaviors are assigned very early on in the process (during the setTableDefinition() method), this chain of events resulted in the following order of listeners:
- SoftDelete
- Acl
Unfortunately, since SoftDelete changes the nature of the event from DELETE to UPDATE, this meant that by the time the Acl listener fired, the event looked like an update, and the update hooks got fired instead of the delete hooks.
The solution? Change the order in which the listeners are registered by implementing my ACL listener as part of an assignable behavior. That way I could assign both listeners in the actAs part of the YAML schema, like so:
1 2 3 4 | |
As long as SoftDelete is the last behavior attached, the ACL listeners will fire correctly.
This requires a couple of modifications to the original code. For starters, we need to modify the constructor of our listener to accept an array of options instead of just a Zend_Acl object:
1 2 3 4 5 6 7 8 9 10 11 | |
Since we’re no longer forcing constructor injection of the ACL object, we also need to allow the listener to go and find one from the application bootstrap if necessary. As a result, we modify getAcl() and setAcl() accordingly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
This still allows us to inject the ACL as necessary for tests and such, while allowing for graceful failover to the bootstrap object if one has not been provided (caveat: you have to register your bootstrap with your front controller instance for this to work).
In order to use this modified listener as a behavior, we also need to define a (very) simple Doctrine_Template implementation. All it’ll need to do is register the listener during setTableDefinition():
1 2 3 4 5 6 7 8 9 10 11 12 | |
And that’s it! From here, just add the new Aclable behavior to your schema file, regenerate your model classes, and everything should happen in the right order from then on. Problem solved.