Dynamic XML Settings
I am working on a project and in this project there are many variables I want to store in an xml file. I could just stick them all in there as key/value pairs but I enjoy some type of structure in my application settings (as well as life). Typically I break my settings into logical groups such as the example settings.xml file below.
settings.xml<settings>
<application>
<name>mysite</name>
<version>v 0.1 {alpha}</version>
<url>http://www.mysite.com</url>
<secure>https://mysite.com</secure>
</application>
<database>
<dsn>mydsn</dsn>
<dbtype>mssql</dbtype>
</database>
<email>
<username>me</username>
<password>pass</password>
<server>mail.mysite.com</server>
</email>
</settings>
I am sure this is nothing to everyone but I came across something this problem today and could not find a quick solution. This example above would be very easy to parse int o some type of structure but what if I wanted to add a new section such as cart. I do not want to make changes to my Settings component so everything needs to be dynamic. For every grouping I want to create a separate structure. I whipped up this quick Setting component that will allow you read the XML file above with no regards of what it contains. I know I am re inventing the wheel here but hopefully this will help someone, on to the code.
Settings.cfc<cfset variables.settings = "">
<cfset variables.configPath = "">
<cffunction name="init" access="public" output="false" returntype="settings">
<cfargument name="path" type="string" required="true" hint="Absolute Path to config file.">
<cfset variables.settings = structNew()>
<cfset variables.configpath = arguments.path>
<cfreturn this>
</cffunction>
<cffunction name="read" access="public" output="true" returntype="struct"
hint="I will return a structure containing all application seetings.">
<cfset var xml = "">
<cfset var xmldoc = "">
<cftry>
<cffile action="read" file="#variables.configPath#" variable="xml"/>
<cfcatch type="any">
<cfthrow message="Invalid path to settings file.">
</cfcatch>
</cftry>
<cfset xmldoc = xmlParse(xml)>
<cfloop collection="#xmldoc.settings#" item="parent">
<!--- for each parent add a new item to our structure --->
<cfset structInsert(variables.settings,parent,structNew())>
<cfloop collection="#xmldoc.settings[parent]#" item="child">
<cfset structInsert(variables.settings[parent],child,xmldoc.settings[parent][child].xmlText)>
</cfloop>
</cfloop>
<cfreturn variables.settings>
</cffunction>
<cffunction name="set" access="public" output="false" returntype="void"
hint="I will set a key/pair value ">
<cfargument name="parent" type="string" required="true">
<cfargument name="key" type="string" required="true">
</cffunction>
<cffunction name="get" access="public" output="false" returntype="void">
<cfargument name="parent" type="string" required="true">
<cfargument name="key" type="string" required="true">
</cffunction>
</cfcomponent>
In a nutshell the read method is where the magic happens and here is what it does.
- Read in your xml settings file.
- Parse it
- Loop the settings node
- For each child of settings (grouping such as application & database) add a new key to our settings structure and initialize a new structure as its value
- Loop that key and insert a new key/value pair into your new structure that was just created.
<cfset settingsObj = createObject("component","settings").init(path)>
<cfset settings = settingsObj.read()>
<cfdump var="#settings#">
One more quick note about xml settings files. I tend to keep this out of the web root for security reasons. In my new application I am using the following structure.
- /admin - admin area (admin.mysite.com)
- /cfc - all components
- /config - coldspring and settings xml
- /www - public folder
- Application.cfc
