ORDA and Triggers

I have tried to find some information on the matter of how smart (or probably stupid) it would be to use ORDA in Triggers. I have been seriously using ORDA for some weeks now and i am just loving it. Maybe too much.

I know that ORDA have no concept for “current record” except the “This” statement that according to the documentation is designed mostly for the Listbox. So probably ORDA is not really there yet for trigger purposes.

Is there any thoughts of how to use ORDA in triggers? Does anyone have any serious experience in this matter?

: Johan BRAUN

So probably ORDA is not really there yet for trigger purposes.

it is more the other way around, Triggers are not there yet for ORDA.

Triggers was developed and designed fully based on “current record concept”.
If you use ORDA not only as a syntax but as a concept, Trigger, as they work today, makes only very limited sense, or in other words, they limit the possibilities drastically.

We will show at 4D Summit 2020 more about how to use ORDA for database/business logic design…

Yes that was what i was thinking too.It was the reason behind my question. But in the same time triggers are a great way to ensure data that integrity and business rules are enforced in a complex system. And yes i understand that those things could be enforced in other ways. But they will always require more discipline from the developers side. Given that most of the applications i have been working on for the last 30 years are still alive and in use. I have found that the “hard” structural definition of the database structure is the very foundation that makes the success and maintainability of all those applications. The old idea of the Trigger have been foundation for that.

I can imagine how “triggers” could be implemented in an ORDA environment. System defined function “fields” with some kind of event that tells when they should fire. I read what you are saying as 4D have a plan for some kind of trigger functions and the future looks bright.

John,

I did some testing/play with ORDA and triggers the other day.
I was impressed.

I tested having a table , in it’s trigger - perform an ORDA operation on the same table and attempt a modification to another record in the SAME TABLE. It actually produced a nested trigger action: 4D successfully saved the 2nd record, then the first.

I’ve heard no word (that I can remember) about if this is officially supported: but it worked.

oh, to avoid misunderstandings, yes, you can use ORDA inside a Trigger.

Triggers are based on current record. It is not allowed to change the current record, but as ORDA are not doing that, it is fine to use it.

What I thought is the reason of the question: as Triggers are based on the current record and ORDA is not, how to use ORDA syntax or ORDA concept to work with the reason the trigger was started. You need to classic field syntax to access the record starting the trigger.

Just: attempt a modification to another record in the SAME TABLE.
As you wrote, this will start another trigger. This can easily run into a deadlock, the server appears to “freeze”.

I’m going to nudge this topic again. I’m working in v17.4.

Changes to a record made with ORDA fire the trigger as expected. From the previous discussion, plus just working with the code, it’s apparent you really need to be careful not to do anything that would cause a second record in the table to change. And any code to update fields in this table has to be classic 4D.

It appears I can call methods that are only ORDA to modify records in OTHER tables.

I’m wondering if I can safely use an ORDA method or query on the same table being modified provided nothing gets changed? For example, getting sums or counts of records meeting some criteria.

For anyone else working with triggers like this it appears to me the changes to the record in the ORDA space are written to the record fields but you can’t get an ORDA instance of the record until after the trigger runs. Sort of a “well duh” moment, yes. But it matters because I have some methods that update other things based on the values of the table. I’d been writing those methods to receive either a record PK or an entity. But a strategy where before I accept a save on Table A I must successfully update Table B contingent on the new values in Table A really has to be written in classic 4D to work.

The question is mostely about cascading triggers in classic mode.

The concept is described here:
https://doc.4d.com/4Dv18/4D/18/Triggers.300-4504565.en.html#35932

(scroll to cascading triggers).
That concept did not changed since 4D version 3.
The only change his was the behaviour inside transaction. Starting with v11 nested transaction can increase the complexity, and starting with v16 paused/resumed transaction can help to ease complexity.

ORDA allows to read other records of the same table (as it has no impact on the current selection), but you need still to follow the rules for cascading triggers or transactions.

Note: as shown at LR’s session


(around 2nd quarter), extended data classes will in the future support “events”, which could be seen similar as a trigger, while way more powerful.

The JavaScript framework that comes with NTK Plugin uses events as a form of triggers. It has a function named:

Table.on( event, callback )

where event can be one of: create, validate, save or delete.

The benefit of this approach is that you can dynamically at runtime assign event handlers to a table. You can even add multiple event handlers for the same event, creating a chain of “triggers”. Or apply the same event handle to multiple tables. All this at runtime.

For example:

db.table('Project').on( TableEvent.CREATE, function( record ) {
  // Register the entry date and time
  record.EntryDate = new Date();
  record.EntryTime = new Time();
} );

db.table('Project').on( TableEvent.SAVE, function( record ) {
  // Register the modification date and time
  record.ModifyDate = new Date();
  record.ModifyTime = new Time();
} );

Or to apply the same event handler to all tables in the database:

db.tables.map( function( table ) {
  table.on( TableEvent.CREATE, function( record ) {
    // Register the entry date and time
    record.EntryDate = new Date();
    record.EntryTime = new Time();
  } )
} );

I hope 4D implements it in a similar way, since it is a really flexible system.

2 Likes

That seems to be very similar to the datastore class Laurent was talking about. Not sure if he was talking about a possibility to subclass the datastore and what behaviours it could control. But it seems that the future of triggers could be handled by the class mechanism.

I had a wish for triggers, something like “on before save or create”: is that the meaning of the “validate” one?

If the trigger method does not return 0 the record does not save. So in the on saving new record event you can do some stuff and if you decide not to save send a non-zero error code. Wouldn’t that be like on before create?

I had a wish for triggers, something like “on before save or create”: is that the meaning of the “validate” one?

The “validate” event is called when you call the Record.validate() function. The idea was to have one single place for data validation. Validation errors are handled by throwing an exception.

What you are referring to is the “create” event. It allows you to set default values for a blank/new record. Contrary to the 4D trigger (which is called at the moment the record gets saved), the “create” event is called upon creation of the record in memory. This way you can initialise the new record with default values like the current date, the default currency etc.

I basically implemented the following two concepts, which 4D is currently missing:

  • a central place to initialise a new record with default values
  • a central place for validating the values of a record and getting a clear error message back when validation fails.
1 Like

This is a great idea.
Usually, we have two types of “errors”, blocking and non blocking (a warning) and you can get more than one error.

Very interested in your approach :wink:

1 Like

I never uses triggers ; am I
☐ amateur
☐ improvident
☐ reckless
Perhaps several choices ?

I don’t say that you should or that it is needed. But it’s sure a great way to enforce a business logic in a larger system that has been under development for some 10 to 20 years. I am using it mostly for handing of progress status on different objects and to initiate post processing or batch processing. Never to execute any larger amount of code. But if you keep it compact and a distinct purpose then its the best thing there is.

Something I took advantage of with triggers over the years was that the code is executed on the server. Often times I wanted to keep some information ‘up-to-date’ on all clients—for example, lists of customers, etc that would appear in combo-boxes or lists or the like. So with a simple check of (old([file]field # [file]field) I could see if the server needed to inform all the clients to update the value. Of course, this part of the code was not done in the trigger; it merely got another process to inform the client machines. It was really neat, because even if, for instance, a person had a combo-box with a customer’s name showing in it, that value would get automatically updated even while it was still being shown.

I agree. I can hardly wait for something similar to be implemented in ORDA. But it would have to be processed on the server (as a trigger is) rather than a client machine for some of the benefits of the current TRIGGER mechanism — easier to message all clients with updated information from the record being saved, for example [of course, using a worker or other dedicated process rather than actually in the trigger code.]