Search

I am experimenting with saving entry data from Symphony entries to a second database (with a different database scheme). By using the EntryPostCreate and EntryPostEdit delegates, I can receive the entry object and build my custom queries (as mentioned by @nickdunn in another thread) — that is fine.

But: How to handle errors? Is there any chance to "interrupt" the entry commit in case of errors with the second DB? As far as I see, the callback function will be executed "blind", but can not influence further execution of the commit/publishing logic, right?

Maybe I just don't see it?

By the time EntryPostEdit executes, the data has already been persisted to Symphony, so it's too late.

Could you swap the order? Subscribe to EntryPreEdit, which receives the old entry object but I think it should also receive the new values. If it doesn't currently receive the new values, modify the delegate signature so that it does and send a pull request — it would be useful in the future! If errors arise, then your delegate callback would need to cancel execution by redirecting to a new page.

The problem with events is that they do not tend to care about what happens in the delegate function.

So in order to respond to errors in the query execution you will need to adjust the class.mysql.php file. I don't know if you have a problem with core modifications?

BTW: I guess this another use case for Issue 739

If errors arise, then your delegate callback would need to cancel execution by redirecting to a new page.

That's just pure evil :)

That's just pure evil :)

It's that or exit() ;-)

Delegate callbacks have no return values, they just modify the variables passed to them by reference. So the only way to modify Symphony's application flow using a delegates is to force a redirect, which would prevent any logic further down the chain from executing.

Evil, but less evil than modifying the core and forgetting about it come the next Symphony update :-P

There must be an alternative way to deal with this, instead of redirecting?

The delegate execution in the code is like this:

Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', ...);
$entry[0]->setData(...);
$entry[0]->commit();
Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', ...);

One way would be for delegates to optionally return a boolean value. If false, then the application would need to handle this and stop application flow. But this fundamentally changes the way delegates work.

$continue = Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', ...);
if(!$continue) // do something to halt flow
$entry[0]->setData(...);
$entry[0]->commit();
$continue = Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', ...);
if(!$continue) // do something to halt flow

Or you could throw an exception and catch it with your own custom error handler. That would be the most programmatically clean method I suppose.

@remie: The MySQL stuff for the second DB is not (yet) my problem. Still try to figure out how to interrupt "native saving" in Symphony based on certain conditions...

When the entry can not be saved in the second DB, I want to stop saving in Symphony as well. Additionally I would like to display an error message.

@nickdunn:

your delegate callback would need to cancel execution by redirecting to a new page

Redirecting in a callback function of the EntryPreEdit delegate indeed interrupts further execution. I can even use the current URL:

redirect(Administration::instance()->getCurrentPageURL());

But I have no chance to append an error then, right?

Nick, you are too fast for me...

Regarding your new comment:

One way would be for delegates to optionally return a boolean value.

That's what I was looking for. :-)

Or you could throw an exception and catch it with your own custom error handler. That would be the most programmatically clean method I suppose.

Maybe I should finally learn how exceptions work... Thanks a lot!

@michael: Sorry, we are hijacking your topic for conceptual thinking :)

You can append the error message using GET parameters...

I don't think that my idea will work on the end. A redirect will definitely be bad for new entries (because all data will be lost).

So I will think in different directions — maybe build a cron script to push those Symphony entries to the second database? The problem then is the "delete" logic... Should I delete everything from the second DB, then re-fill it? That might be too slow...

(In fact all I try is to have an independent email server, but manage email domains/addresses from within Symphony. The email server should have its own database.)

@michael: take a look at the CDI extension, this might help you. It will also include other changes (structural, changes to extensions), but if that is not a problem, it should do the trick!

Well, that would mean to replicate using the identical database scheme. (Which might as well be achieved using native MySQL database replication.) I'd rather have a much simpler scheme in the email server's database.

right... good point. What is the reason that you want your second database to interfere with entry creation?

Perhaps you should look more into creating a push / pull kind of service (using WebHooks or REST API extensions). If there is an error on your second database, there is something wrong with your specific service, not with Symphony.

This sounds like a situation where an asynchronous approach is actually valid.

I don't think that my idea will work on the end. A redirect will definitely be bad for new entries (because all data will be lost).

What would you need to do in this situation? Create the entry but flag to the user that they have to re-save to try and push the data again? Sounds like you might need it to be an asynchronous process, because at the moment you're relying on user interaction to achieve both tasks. If one fails on entry creation what should the correct behaviour be?

What might prevent the saving in the second database? The server being down? How would you want that to be handled in your application — a silent fail and the application has a history so it knows to reattempt the communication at a later date, or report the failure to the user and request that they try again? Perhaps what you need is a few field type that you add to an entry. On entry saving, this field is responsible for sending the new details to the slave database, and tracks the status. If the communication failed, flag the entry requesting the user saves again.

You could use something like Tracker. This keeps a log of all UI delegates that have fired, including deletes. A script could parse Tracker's own log, and repeat those operations on the slave database.

Or even simpler, a single cron script which purges the slave database table and copies across all required data on a schedule...

Damn, too slow!

:) Your answer was more elaborate, so don't be to hard on yourself.

Or even simpler, a single cron script which purges the slave database table and copies across all required data on a schedule...

That would probably be the simplest solution, but as I wrote earlier:

Should I delete everything from the second DB, then re-fill it? That might be too slow...

I am not sure how long this would take for some thousand addresses. I probably would have to teach the mailserver to wait for the process to be finished instead of denying email delivery.

Thanks for all your suggestions, guys! For the moment I will put this "on hold", because it is not a prio 1 task. (I thought it to be simpler, to be honest.)

I tested the following: Lock the tables and insert 5000 entries. It took no more than 0,3 s (over an SSL-encrypted connection) — so there would probably be no need to anesthetize the mail server. :-)

So I may go this route:

  1. Manage data in Symphony.
  2. Write a cron (PHP) script to get the Symphony entries (maybe using pagination?) and build custom SQL queries.
  3. Connect to the email database and execute the queries.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details