The framework also offers you the capability to bind incoming FORM/URL/REMOTE/XML/JSON/Structure data into your model objects by convention. This is done via WireBox's object population capabilities. The easiest approach is to use our populateModel()
function which will populate the object from many incoming sources:
request collection RC
Structure
json
xml
query
This will try to match incoming variable names to setters or properties in your domain objects and then populate them for you. It can even do ORM entities with ALL of their respective relationships. Here is a snapshot of the method:
Let's do a quick example:
Person.cfc
editor.cfm
Event Handler -> person.cfc
In the dump you will see that the name
and email
properties have been bound.
We have a complete section dedicated to the Model Layer, but we wanted to review a little here since event handlers need to talk to the model layer all the time. By default, you can interact with your models from your event handlers in two ways:
Dependency Injection (Aggregation)
Request, use and discard model objects (Association)
ColdBox offers its own dependency injection framework, WireBox, which allows you, by convention, to talk to your model objects. However, ColdBox also allows you to connect to third-party dependency injection frameworks via the IOC module: http://forgebox.io/view/cbioc
Aggregation differs from ordinary composition in that it does not imply ownership. In composition, when the owning object is destroyed, so are the contained objects. - wikipedia
Your event handlers can be autowired with dependencies from WireBox by convention. By autowiring dependencies into event handlers, they will become part of the life span of the event handlers (singletons), since their references will be injected into the handler's variables
scope. This is a huge performance benefit since event handlers are wired with all necessary dependencies upon creation instead of requesting dependencies (usage) at runtime. We encourage you to use injection whenever possible.
Warning As a rule of thumb, inject only singletons into singletons. If not you can create unnecessary scope-widening injection issues and memory leaks.
You will achieve this in your handlers via property
injection, which is the concept of defining properties in the component with a special annotation called inject
, which tells WireBox what reference to retrieve via the WireBox Injection DSL. Let's say we have a users handler that needs to talk to a model called UserService. Here is the directory layout so we can see the conventions
Here is the event handler code to leverage the injection:
Notice that we define a cfproperty
with a name and inject
attribute. The name
becomes the name of the variable in the variables
scope and the inject
annotation tells WireBox what to retrieve. By default it retrieves model objects by name and path.
Tip: The injection DSL is vast and elegant. Please refer to it. Also note that you can create object aliases and references in your config binder: config/WireBox.cfc
The other approach to integrating with model objects is to request and use them as associations via the framework super type method: getInstance()
, which in turn delegates to WireBox's getInstance()
method. We would recommend requesting objects if they are transient (have state) objects or stored in some other volatile storage scope (session, request, application, cache, etc). Retrieving of objects is okay, but if you will be dealing with mostly singleton objects or objects that are created only once, you will gain much more performance by using injection.
Association defines a relationship between classes of objects that allows one object instance to cause another to perform an action on its behalf. - 'wikipedia'
In this practical example we will see how to integrate with our model layer via WireBox, injections, and also requesting the objects. Let's say that we have a service object we have built called FunkyService.cfc
and by convention we will place it in our applications models
folder.
FunkyService.cfc
Our funky service is not that funky after all, but it is simple. How do we interact with it? Let's build a Funky event handler and work with it.
By convention, I can create a property and annotate it with an inject
attribute. ColdBox will look for that model object by the given name in the models
folder, create it, persist it, wire it, and return it. If you execute it, you will get something like this:
Great! Just like that we can interact with our model layer without worrying about creating the objects, persisting them, and even wiring them. Those are all the benefits that dependency injection and model integration bring to the table.
You can use the value of the inject
annotation in several ways. Below is our recommendation.
Let's look at the requesting approach. We can either use the following approaches:
Via Facade Method
Directly via WireBox:
Both approaches do exactly the same thing. In reality getInstance()
does a wirebox.getInstance()
callback (Uncle Bob), but it is a facade method that is easier to remember. If you run this, you will also see that it works and everything is fine and dandy. However, the biggest difference between injection and usage can be seen with some practical math:
As you can see, the best performance is due to injection as the handler object was wired and ready to roll, while the requested approach needed the dependency to be requested. Again, there are cases where you need to request objects such as transient or volatile stored objects.