I'm very excited to be posting my first blog entry for IPS, and thrilled that this is what I get to post.
When we started planning the new development tools for v5, we looked at existing resources - from the marketplace, from some contributors, and from our own managed clients - and asked, what is it that developers find themselves doing the most often? Matt’s previous blog entry gave a brief summary of some of those functions. Today’s blog entry is going to focus on one of our powerful new tools, Listeners.
One common reason for a code hook was to execute custom code when a specific action occurs. For example, when a topic is created, or when a new reply is posted. Listeners allow you to execute your code at key points in the workflow, without the worry of finding exactly the right hook location.
Here is an excerpt from the BlogComment listener in our Cloud application:
Let’s start with the application’s Developer Center.
We’ve added a new tab for Listeners. This tab lists all your existing listeners, as well as the class on which it is listening.
You’ll notice that each listener can only observe a single class. While this may seem slightly tedious, the idea here was that as a developer, you should be conscious of what class you are working with. It also eliminates the need to check what type of object is being passed preventing accidents and unintended consequences when missing class checks.
When you add a listener, there are several listener types available. We will discuss each one in detail.
This listener is fired on an Item or Comment (or Review) class. When adding a Content Listener, you must specify the class you are observing. When you hit Save, there is a validation check in place to ensure that the class exists, and that it is a valid class for the selected listener type.
Content Listeners have the following methods available. All methods are included in the default listener file that is generated.
Triggered when an object is loaded (in Content::constructFromData)
Fired when a form is submitted, before it is saved. This is the equivalent of Item::processBeforeCreate and Item::processBeforeEdit. This method includes a parameter to indicate whether this is a create or edit.
Fired after a form is saved. This is the equivalent of Item::processAfterCreate and Item::processAfterEdit. This method includes a parameter to indicate whether this is a create or edit.
Fired after an object is deleted.
Fired when any moderation action occurs. For example, when an item is pinned/unpinned, locked/unlocked. The moderation action is passed as a parameter so that your code can take that into account.
Triggered AFTER an item is merged.
Triggered when an item is viewed and the view count is incremented.
An Invoice listener is fired only on the \IPS\nexus\Invoice class. This listener does not require you to specify a class to extend.
The Invoice listener includes a single method, onStatusChange. This is fired when the invoice is saved and the status changes (pending/paid/expired/canceled).
The Member listener will be familiar to many of you. We have taken the MemberSync extension and moved it in its entirety to a listener, as it was a better fit for this structure. All the previous methods that were available are still available here. We have also added the following new methods:
Fired when a member joins a club. If approval is required, this is fired after they are approved.
Fired when a member leaves or is removed from a club.
Fired when a member responds to an event. The response is included as a parameter to this method.
Commerce Package Listener
The Commerce Package listener is fired on any implementation of \IPS\nexus\Invoice\Item. You must specify the class that you are observing.
The methods are the same as those that are available in an item extension.
Some Technical Notes
- All listener files will be generated in a “listeners” directory within your application.
- Your application’s data directory will include a listeners.json file that defines all your listeners. The json file is automatically generated when you add/remove a listener.
- All listener methods are optional. The default class that is generated will include all available methods, but they do not have to be present in your file.
- All listener methods are return type void.
- If a listener method fails, an exception will be thrown when IN_DEV is enabled. In production, the exception will be logged with a reference to the listener class and the event on which the exception occurred.
Your code can fire any existing event using the Event::fire method.
Event::fire( 'onBeforeCreateOrEdit', $comment, array( $values ) );
The Event::fire method is called statically and accepts an event name, the object on which the event will be fired, and an array of additional parameters (varies according to the event that you are triggering).
This concludes our introduction to Listeners. We do intend to implement additional listener types and events based on your feedback and as the need arises.