Use Dependency Injection on a base class?

Hi,

I want to use dependency injection on a base class, but the reference
to the injected bean is not recognized there. Here is what I do:

<cfcomponent name="myComponent" output="false">
  <cffunction name="myFunction" access="public"
returntype="myComponent">
    <cfset myVar=CreateObject("component",myObject).init()>
  </cffunction>
</cfcomponent>

<cfcomponent name="myObject" extends="base">
  <!--- no init method here, see base class --->
</cfcomponent>

<cfcomponent name="base" autowire="true">
  <cfproperty name="dsn" type="coldbox:datasource:myDSN"
scope="VARIABLES">

  <cffunction name="myFunction" returntype="base">
    <cfdump var="#VARIABLES.dsn#">
    <cfabort>
  </cffunction>
</cfcomponent>

Coldbox returns an error "Element DSN is undefined in VARIABLES."

If I do this:

<cfcomponent name="myComponent" autowire="true">
  <cfproperty name="dsn" type="coldbox:datasource:myDSN"
scope="VARIABLES">
  <cffunction name="myFunction" access="public"
returntype="myComponent">
    <cfset myVar=CreateObject("component",myObject).init(dsn=dsn)>
  </cffunction>
</cfcomponent>

<cfcomponent name="myObject" extends="base">
  <!--- no init method here --->
</cfcomponent>

<cfcomponent name="base">
  <cffunction name="myFunction">
    <cfargument name="dsn" type="any" required="true">
    <cfdump var="#arguments.dsn#">
    <cfabort>
  </cffunction>
</cfcomponent>

Coldbox dumps the dsn component coldbox.system.beans.datasourceBean...

So instead of injecting the basecomponent with the datasourcebean I
have to inject the calling component and pass the bean along as an
argument.

Is it not possible to use DI in the way I originally intended? That
way my beans get injected where I need them.

Thanks,

Marc

I actually created the code here an tested it, and here’s what I have.

You only need to use autowire=“true” in event handlers. I’ve created the following CFC’s in my coldbox application’s model folder:

Then what I did was autowire myComponent to an event hander:

Then in my view I was able to do:

-Dutch

Hi Dutch,

Not quite sure that is what I mean... I have several objects extending
a base object.I need a reference to a datasource bean in all of them.
Instead of using <cfproperty name="dsn"
type="coldbox:datasource:myDsn" scope="Variables"> in _every_ object I
figured I could add
this line in the base object (the one that all objects are extending).
This should make the dsn bean accessible in all my objects.

So instead of

<cfcomponent name="comp1" extends="base">
<cfproperty name="dsn" type="coldbox:datasource:myDsn"
scope="Variables">
</cfcomponent>

in 3 objects I use:

<cfcomponent name="base" >
<cfproperty name="dsn" type="coldbox:datasource:myDsn"
scope="Variables">
</cfcomponent>

and in all _extending_ objects I can refer to Variables.dsn

I don't need to refer to variables.dsn in views, only in my model.

Marc

Hi Marc

You have to put in your base component.

THEN

variables.dsn is available in this component.

Also I would suggest that you read about scopes in CFC (this, variables, var)

ColdBox init your object first then inject the objects/string etc .
So every thing will be available in your methods

If above is not the answer to your question then!
( May be still we don’t understand what exactly you want to achieve. )

Thanks
Sana

You are doing it ok but you are not using “GETMODEL()” method. That is how coldbox puts everything together. You can put 1000 cfproperties with their specific annotations but you will not get any dependency injection if you do not go through the factory to request the object. You cannot do createObject() and expect ColdBox to autowire it for you. That would mean that the DI framework is doing byte weaving at the JVM level, and we are not doing that.

Please refer to the modelintegration guide, and try to retrieve the objects, via GetModel(). That method in turn calls the beanFactory plugin.

Luis

Thanks Luis for the answer. I didn't realize the GetModel() function
was required in order to get the CB functionality.

I replaced the CreateObject("component",myObject).init() for GetModel
("#myObject#").init() (#myObject# resolves to a model name set via
addModelMapping()).
I get a ColdBox error
"Error Messages: Variable GETMODEL is undefined.".
I figure this makes sense since this object does not extend
coldbox.system.eventHandler.

Let me be clear and summarize what I want to do:

<!--- initialize the main component --->
<cfcomponent name="main" extends="coldbox.system.eventhandler"
output="false" autowire="true">
  <cffunction name="onApplicationStart" access="public"
returntype="void" output="false">
    <!--- I want to use the CB Object Caching Model hence the call to
getColdboxOCM--->
    <!--- old code, see my first post and Luis' comment
    <cfset myVar=CreateObject("component",myObject).init()>
    --->
    <cfset getColdboxOCM().set("oCms",getModel('cms').initcomponent())>
    ...more code here...
  </cffunction>
</cfcomponent>

<!--- have the main component initializer child components --->
<cfcomponent displayname="cms" output="false">
  <cffunction name="initcomponent" access="public">
    <cfset VARIABLES.contentTypes= {}>
    <cfset types="graphic,text,video">
    <cfloop list="#types#" index="contentType">
      <!--- #contentType# refers to an object and is set in config/
modelmappings.cfm --->
      <cfset getColdboxOCM().set("#contentType#",GetModel
("#contentType#").init())>
<!--- In component #contentType# I use the parent component's
(base.cfc) init method where I want to use the cfproperty tag - see my
start post --->
    </cfloop>
    <cfreturn this>
  </cffunction>
</cfcomponent>

getColdboxOCM() gives me the CB error "Variable GETCOLDBOXOCM is
undefined." because I don't extend component cms from
coldbox.system.eventHandler. So I still cannot use GetModel() here.
I read the Model Integration Guide but can not find an example where
component B is instantiated using GetModel() in component A that _does
not extend the coldbox.system.eventhandler class_.

Does that mean I have to extend this model "cms" with the
coldbox.system.eventhandler class??

Thanks,

Marc

No,

don’t do inheritance just to bring in external functionality that does not belong there.

If you want to be able to “create” and use OTHER model objects within model objects, you need to inject the beanfactory plugin itself. In order to do this. Now, what I would do is setup a “baseModel” object with certain features and methods to provide me all the functionality I need to talk to coldbox model objects. Example:

Does this make it clear?

So instead of injecting the called model with a datasourcebean I
should inject the calling event handler with a ds bean and pass it
along?

Like this:

<cfcomponent name="main" extends="coldbox.system.eventhandler"
output="false" >

<cfproperty name="dsn" type="coldbox:datasource:irama" />

<cffunction name="initComponents" access="private" returntype="void"
output="false">
  <cfset VARIABLES.contentTypes= {}>
  <cfset types="graphic,text,video">
  <cfloop list="#types#" index="contentType">
    <!--- the reference to the object is set in config/modelmappings.cfm
--->
    <cfset getColdboxOCM().set("#contentType#",GetModel
("#contentType#").init(dsn=dsn))>
  </cfloop>
</cffunction>

<cffunction name="onApplicationStart" access="public"
returntype="void" output="false">
  <cfset initComponents()>
</cffunction>

</cfcomponent>

<cfcomponent name="text" extends="base">
</cfcomponent>

<cfcomponent name="base">
<cffunction name="init" access="public" returntype="void"
output="false">
<cfargument name="dsn" required="true" type="any">
</cffunction>

</cfcomponent>

The dsn bean is known in the initComponents function (I can dump it)
but when passed as an argument to init() I get a CB error
"Error Messages: The DSN parameter to the init function is required
but was not passed in." on line 187 of D:\www\wwwroot\coldbox\system
\plugins\beanFactory.cfc

Marc

Hi Marc,

cfproperty are metadata, coldbox first initialise the object then inject the objects/strings etc according to cfproperty metadata.

Secondly if you want to wire anything on arguments, then do like this

<cfargument name="orm" type="transfer.com.Transfer" _wireme="ocm:Transfer" />

I would suggest that just relax, have a coffee and read model guide, its really powerful concepts but require some attention before firing off any code.

Hopefully once you read the guide then you will understand the magic of coldbox model stuff.

Trust me no body can explain better than this guide
http://ortus.svnrepository.com/coldbox/trac.cgi/wiki/cbModelGuide

Now about your ERROR
missing “scope = variables”

<cfset getColdboxOCM().set("#contentType#",GetModel ("#contentType#").init(dsn=variables.dsn))>

NO SCOPE THEN
If you don’t specify any scope then ColdBox default scope is “instance”

<cfset getColdboxOCM().set("#contentType#",GetModel("#contentType#").init(dsn=instance.dsn))>

Thanks
Sana

NO SCOPE THEN
If you don't specify any scope then ColdBox default scope is "instance"

Hi Sana, Thanks for you answer and patience! I read this in the MIG:

CFPROPERTY

    * name : The name of the property to be injected
    * type : The type of property to inject (see chart)
    * scope : Into which scope to inject the object/setting to.
Defaults to variables scoppe

(http://ortus.svnrepository.com/coldbox/trac.cgi/wiki/cbModelGuide)

I will try later today what you suggested

Marc

NO SCOPE THEN
If you don't specify any scope then ColdBox default scope is "instance"

Hi Sana, Thanks for you answer and patience! I read this in the MIG:

CFPROPERTY

    * name : The name of the property to be injected
    * type : The type of property to inject (see chart)
    * scope : Into which scope to inject the object/setting to.
Defaults to variables scoppe

(http://ortus.svnrepository.com/coldbox/trac.cgi/wiki/cbModelGuide)

I will try later today what you suggested

Marc