As Jeffrey said, it's better to implement the iApplicationObjectExtension when possible as it is meant to allow several extension to do some processing at this time of the object lifecycle.
The OnUpdate/OnDelete methods of the DBObject class on the other hand are subject to DM changes, meaning that is several extensions try to overload it, after setup (compilation) only 1 extension processing while remain. Usually the last extension to redefine it if your are lucky, or a big crash if extensions are not compatible. ๐
You can find some examples of the iApplicationObjectExtension usage on GitHub. Just keep in mind, that if several extensions implement them, they will be called in the same order as their dependancies.
Guillaume
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, if the use of iApplicationObjectExtension permit more than one add to the standard change, it seems to be the way I should follow.
But I have to admit, I have no idea how to do that. And I didn't find any implementation directly in iTop. Research on GitHub gave me some hints, but as a non OO programmer), I'm lost. Does that means that, adding a clas of this king (like ''class RequestPhysicalConnection implements iApplicationObjectExtension'' with that kind of function ''public function OnDBInsert($oObject, $oChange = null)'' ) means that, ervery time a dbInsert is made on iTop, that function will be triggered ? So I have to go quiclky out if I'm not concerned ?
(I'm probably totally 'out of bounds' :))
Thanks,
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes exactly it will be triggered for every object creation no matter the type. That's why the first thing you need to do in the function, is to check if the current object ($oObject) is concerned by your processing, if not you should exit the function. For example:
thank you for that explanation. And a great thank you for the reminder, I didn't think of this point.
OK, definitively some tests to do here !
.....
Going further :
OnDBInsert is called after the insert, like 'AfterInsert', and that's OK for me.
OnDBDelete is called just before the DB Delete, like OnDelete, so I can still use the GetOriginal funct, thats' also OK For me .
OnDBUpdate is called after the update, like 'AfterUpdate' so I guess I cannot use GetOriginal for the object ? I mostly use OnUpdate to have access of the previous value, any hints for that ?
Thanks,
Pascal
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, unfortunately no, from the OnDBUpdate you don't have a direct access to the old value anymore (not that I know of!).
There is a way to find it through the CMDBChange and CMDBChangeOp object, but not sure it is worth it. You can take a look at those two classes and the following OQL for example (Change the id with the id of the $oChange object)
OK, As I'm not (for now) a alpha tester, I'll will wait a little bit :)
But just one (more) question :
With the Methods, we have 6 stages : On/After Insert, On/AfterUpdate, On/AfterDelete.
In the trigerred methods, ther are only 3 'equivalent' triggers', two of them launched after the Insert/Update (and, with your changes in 2.7.0, with an acces to the 'before' values, witch is OK for me). But for the OnDBDelete, according to the documentation, the trigger is launched before the Delete.
Wouln't be better, if you give an access to the preceding values, to trigger the OnDBDelete action also after the actual Delete ?
Thanks,
Pascal
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
There is no extension point after deletion, only iApplicationObjectExtension::OnDBDelete and iApplicationObjectExtension::OnCheckToDelete which are launched before.
You can still override an object method (but only once as said before) like DBObject::AfterDelete to do some process after the object deletion.
If this doesn't suits your need, you may ask for a change, but try to explain your use case as clearly as possible.
Modifying the extension interfaces is not easy to do at it is a breaking change for all existing implementations... So it won't be done in 2.7.0 which aims for stability now, and maybe it would be too disturbing to be done in the first place.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here's the situation I use 'AfterDelete' :
I am nearly finishing an extension for network connections, working this way : You set the real connections (say Eth1 from server0 depends on port 4 of switch01) Then, after an insert/update/delete of the interface, I run a process who find the 'CI level dependencies' (in that case, it create automatically a dependency with server0 dependent of switch01).
In that case, the new calculation has to be done after all changes on interface level (including a delete).
But, in my case, I found another way, because a deletion of an interface launches also an update of the CI, so i'll launch my calculation more on the update of the CI, and no more on the creation/update/Delete of the interface (seems also to reduce the number of launch, I have to check that all). In short, this it's no more a trouble for me.
However, as I ran into this trouble, I can imagine that some others could ran into also.
OTOH, modifying an already published interface is risky, I totally agree !
Thanks for all your time,
Pascal
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello Pascal,
Sorry for the long time it took me to answer, but I'd like to thank you for this clear explanation : it's really useful for us to hear about real life use cases !
Glad you find a solution :)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Okay, I did my homework, and the triggered mode is really interresting (and not too hard to understand, even for a noo OO programmer as me). In fact, it solves one case I had troubles to solve trough methods. So that's really cool for the cases my update don't need the older values. For the other cases ... I'll follow the lead from vdumas an Pierre :)
But I have another 'silly' question : As the use of 5 of the functions seems obvious to me : 3 3 actions (OnDBInster, OnDBUpdate, OnDBDelete), two safety checks (I didn"t need them, but I can imagine that if some security rules are to set before an actual DBUpdate/DBRmoeve, they must be there).
But I don't see an use for the sixth function, OnIsModified. In witch case(s) could I have to do semething in this method ? In case of an foreign update ?
Thanks,
Pascal
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
OnIsModified. In witch case(s) could I have to do semething in this method ?
Didn't have to implement it myself, but searching for any existing real implementation (instead of the default return false), I found only 1 non public module.
Sometimes you need things that are usefull only once but you would be blocked it it wasn't there ;)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
This is, I think "Another Silly Question From Pascal" (TM) :)
I use and abuse of modification in the 'OnUpdate' 'AfterIsert' and 'OnDelete' methods.
But I don't exactly understand the way things works if more than one extension try to change the same method.
For instance, if I want to alter the OnUpdate of one class in extension A, I mostly write something like :
With ConnectCable and DisconnectCable being the functions I added to do the 'real' work.
But supposing that another extension does the same thing, does iTop 'links' the changes ? If yes, is that done by the magic of 'parent::OnUpdate();' ?
If not, what is the correct way to do that ?
Thanks
Pascal
"But I don't exactly understand the way things works if more than one extension try to change the same method."
=> that's why interfaces are for: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Aextensions_api#interfaces_reference_documentation -> iApplicationObjectExtension . There are some interesting undocumented interfaces as well; but obviously they're not considered "stable"/"supported" :)
While the "feature"/method might still generate conflicts, it's less likely to be the case or cause conflicts.
Last edit: Jeffrey Bostoen 2019-11-08
Hello Pascal,
As Jeffrey said, it's better to implement the iApplicationObjectExtension when possible as it is meant to allow several extension to do some processing at this time of the object lifecycle.
The OnUpdate/OnDelete methods of the DBObject class on the other hand are subject to DM changes, meaning that is several extensions try to overload it, after setup (compilation) only 1 extension processing while remain. Usually the last extension to redefine it if your are lucky, or a big crash if extensions are not compatible. ๐
You can find some examples of the iApplicationObjectExtension usage on GitHub. Just keep in mind, that if several extensions implement them, they will be called in the same order as their dependancies.
Guillaume
Hi Jeffrey, Guillaume,
Yes, if the use of iApplicationObjectExtension permit more than one add to the standard change, it seems to be the way I should follow.
But I have to admit, I have no idea how to do that. And I didn't find any implementation directly in iTop. Research on GitHub gave me some hints, but as a non OO programmer), I'm lost. Does that means that, adding a clas of this king (like ''class RequestPhysicalConnection implements iApplicationObjectExtension'' with that kind of function ''public function OnDBInsert($oObject, $oChange = null)'' ) means that, ervery time a dbInsert is made on iTop, that function will be triggered ? So I have to go quiclky out if I'm not concerned ?
(I'm probably totally 'out of bounds' :))
Thanks,
Hello Pascal,
Yes exactly it will be triggered for every object creation no matter the type. That's why the first thing you need to do in the function, is to check if the current object ($oObject) is concerned by your processing, if not you should exit the function. For example:
Mind that as this implements an interface, you MUST implement ALL its methods, even those you don't need...
Hope this helps,
Guillaume
Hi Guillaume,
thank you for that explanation. And a great thank you for the reminder, I didn't think of this point.
OK, definitively some tests to do here !
.....
Going further :
Thanks,
Pascal
Well, unfortunately no, from the OnDBUpdate you don't have a direct access to the old value anymore (not that I know of!).
There is a way to find it through the CMDBChange and CMDBChangeOp object, but not sure it is worth it. You can take a look at those two classes and the following OQL for example (Change the id with the id of the $oChange object)
Hi,
Yes, indeed, this could maybe do the work. I'll see. Thanks for all your help :)
Pascal
You can hope for the 2.7 version, in which we plan to provide a mean to get the old values within OnDBUpdate. Stay tuned.
Hello,
This is already commited in the repo, see all commits related to Nยฐ2293, for example 5af33ffe
w00t w00t!
Vaouh vDumas, Pierre, I'll go this way, then :)
Thanks for the information,
Pascal
OK, As I'm not (for now) a alpha tester, I'll will wait a little bit :)
But just one (more) question :
With the Methods, we have 6 stages : On/After Insert, On/AfterUpdate, On/AfterDelete.
In the trigerred methods, ther are only 3 'equivalent' triggers', two of them launched after the Insert/Update (and, with your changes in 2.7.0, with an acces to the 'before' values, witch is OK for me). But for the OnDBDelete, according to the documentation, the trigger is launched before the Delete.
Wouln't be better, if you give an access to the preceding values, to trigger the OnDBDelete action also after the actual Delete ?
Thanks,
Pascal
There is no extension point after deletion, only iApplicationObjectExtension::OnDBDelete and iApplicationObjectExtension::OnCheckToDelete which are launched before.
You can still override an object method (but only once as said before) like DBObject::AfterDelete to do some process after the object deletion.
If this doesn't suits your need, you may ask for a change, but try to explain your use case as clearly as possible.
Modifying the extension interfaces is not easy to do at it is a breaking change for all existing implementations... So it won't be done in 2.7.0 which aims for stability now, and maybe it would be too disturbing to be done in the first place.
Hi Pierre,
Here's the situation I use 'AfterDelete' :
I am nearly finishing an extension for network connections, working this way :
You set the real connections (say Eth1 from server0 depends on port 4 of switch01)
Then, after an insert/update/delete of the interface, I run a process who find the 'CI level dependencies' (in that case, it create automatically a dependency with server0 dependent of switch01).
In that case, the new calculation has to be done after all changes on interface level (including a delete).
But, in my case, I found another way, because a deletion of an interface launches also an update of the CI, so i'll launch my calculation more on the update of the CI, and no more on the creation/update/Delete of the interface (seems also to reduce the number of launch, I have to check that all). In short, this it's no more a trouble for me.
However, as I ran into this trouble, I can imagine that some others could ran into also.
OTOH, modifying an already published interface is risky, I totally agree !
Thanks for all your time,
Pascal
Hello Pascal,
Sorry for the long time it took me to answer, but I'd like to thank you for this clear explanation : it's really useful for us to hear about real life use cases !
Glad you find a solution :)
Okay, I did my homework, and the triggered mode is really interresting (and not too hard to understand, even for a noo OO programmer as me). In fact, it solves one case I had troubles to solve trough methods. So that's really cool for the cases my update don't need the older values. For the other cases ... I'll follow the lead from vdumas an Pierre :)
But I have another 'silly' question : As the use of 5 of the functions seems obvious to me : 3 3 actions (OnDBInster, OnDBUpdate, OnDBDelete), two safety checks (I didn"t need them, but I can imagine that if some security rules are to set before an actual DBUpdate/DBRmoeve, they must be there).
But I don't see an use for the sixth function, OnIsModified. In witch case(s) could I have to do semething in this method ? In case of an foreign update ?
Thanks,
Pascal
Didn't have to implement it myself, but searching for any existing real implementation (instead of the default return false), I found only 1 non public module.
Sometimes you need things that are usefull only once but you would be blocked it it wasn't there ;)
Thanks Pรฏerre, I think I'll stick with 'return false;' :)