Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
March 2, 2025
COLDBOX-1293 DBAppender with SQL Server Not Compatible with Adobe 2023
COLDBOX-1270 Abililty to restart schedulers with a restart()
method
COLDBOX-1268 WireBox Singleton auto reload now only affects app singletons and not core singletons
COLDBOX-1269 Do not add double headers if `event.setHTTPHeader()
` is called more than once
COLDBOX-1273 Removal of deprecated CFML functions in core
COLDBOX-1275 Improved engine detection by the CFMLEngine feature class
COLDBOX-1278 Remove unsafe evaluate
function usage
COLDBOX-1266 Logger for MS SQL using date
not datetime
.
COLDBOX-1267 Lucee only isEmpty
function call
COLDBOX-1279 Render encapsulator bleed of this scope by engines
ColdBox is a conventions-based HMVC web development framework for ColdFusion (CFML).
ColdBox Hierarchical MVC is the de-facto enterprise-level HMVC framework for ColdFusion (CFML) developers. It's professionally backed, conventions-based, modular, highly extensible, and productive. Getting started with ColdBox is quick and painless. ColdBox takes the pain out of development by giving you a standardized methodology for modern ColdFusion (CFML) development with features such as:
Much More
It provides a set of reusable code and tools that can be used to increase your development productivity, as well as a development standard for working in team environments.
The ColdBox Platform is open source and licensed under the Apache 2 License.
Copyright by Ortus Solutions, Corp
ColdBox, CacheBox, WireBox, and LogBox are registered trademarks of Ortus Solutions, Corp.
The Ortus Community is how to get help for our entire platform and modules: https://community.ortussolutions.com.
You can also join our Slack Box Team at: https://boxteam.ortussolutions.com
ColdBox is a professional open source software backed by Ortus Solutions, Corp offering services like:
Custom Development
Professional Support & Mentoring
Training
Server Tuning
Security Hardening
Code Reviews
Official Site: https://www.coldbox.org
CFCasts Video Training: http://www.cfcasts.com
Facebook: https://www.facebook.com/coldboxplatform
Source Code: https://github.com/coldbox/coldbox-platform
Bug Tracker: https://ortussolutions.atlassian.net/browse/COLDBOX
Twitter: @coldbox @ortussolutions
Vimeo Channel: https://vimeo.com/channels/coldbox
Because of His grace, this project exists. If you don't like this, don't read it, it's not for you.
"Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5
We all make mistakes from time to time :) So why not let us know about it and help us out? We also love pull requests, so please star us and fork us at: https://github.com/coldbox/coldbox-platform
The best way to contribute to ColdBox
Hola amigo! I'm excited that you are interested in contributing to ColdBox, CacheBox, LogBox, and/or WireBox. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
This project is open source, and as such, the maintainers give their free time to build and maintain the source code held within. They make the code freely available in the hope that it will be of use to other developers and/or businesses. Please be considerate towards maintainers when raising issues or presenting pull requests. We all follow the Golden Rule: Do to others as you want them to do to you.
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
Participants will be tolerant of opposing views.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions not aligned with this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
When interpreting the words and actions of others, participants should always assume good intentions. Emotions cannot be derived from textual representations.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
Each of the main standalone frameworks in ColdBox has separate locations for submitting bug reports. Please also ensure that if you submit a pull request, you link it to the appropriate issue.
ColdBox Core: https://ortussolutions.atlassian.net/browse/COLDBOX
If you file a bug report, your issue should contain a title, a clear description of the issue, a way to replicate the issue, and any support files we might need to replicate your issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix for it. All issues that do not contain a way to replicate will not be addressed.
If you have questions about usage, professional support, or just ideas to bounce off the maintainers, please do not create an issue. Leverage our support channels first.
Ortus Community Discourse: https://community.ortussolutions.com/c/communities/coldbox/13
Box Slack Team: http://boxteam.ortussolutions.com/
Professional Support: https://www.ortussolutions.com/services/support
The master
branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. Do not submit PRs against the master branch. They will be closed.
All pull requests should be sent against the development
branch or the LTS version branch releases/v{version}
It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
Make sure all local tests pass before submitting the merge.
Please make sure all your pull requests have companion tests.
Please link the Jira issue in your PR title when sending the final PR
If you discover a security vulnerability, please email the development team at security@ortussolutions.com and make sure you report it to the #security
channel in our Box Team Slack Channel. All security vulnerabilities will be promptly addressed.
We have added all the necessary information to develop on ColdBox in our readme collaboration area and our tests readme so you can set up the database to test against.
Please make sure your code runs on the following Supported CFML Engines:
Lucee 5+
Adobe ColdFusion 2018+
We are big on coding styles and have included a .cfformat.json
in the root of the project so that you can run the formatting tools and CommandBox scripts:
We recommend that anytime you hack on the core, you start the format watcher (box run-script format:watch
). This will monitor your changes and auto-format your code for you.
You can also see the Ortus Coding Standards you must follow here: https://github.com/Ortus-Solutions/coding-standards.
All CFCs are self-documenting, and we leverage DocBox to document the entire software. All functions must be properly documented using the DocBox syntax: https://docbox.ortusbooks.com/getting-started/annotating-your-code
You can support ColdBox and all of our Open Source initiatives at Ortus Solutions by becoming a Patreon. You can also get lots of goodies and services depending on the level of contributions.
Thank you to all the people who have already contributed to ColdBox! We: heart: : heart: : heart: love you!
Made with contributors-img
August 3, 2023
This is a minor release with tons of updates and bug fixes.
You can now add a debug
argument to your task definitions, and your console will add tons of debugging for your tasks:
You can now use this matcher to test relocations in a nice fluent expectation:
Thanks to our very own Gavin Pickin you can now create exception handlers in your REST Handlers that follow the on{type}Exception()
convention and you can listen to specific error type exceptions:
The full release notes per library can be found below. Just click on the library tab and explore their release notes:
Bug
New Feature
Improvements
Tasks
Bug
Improvement
Task
All the major information about ColdBox Releases
And constructed with the following guidelines:
Breaking backward compatibility bumps the major (and resets the minor and patch)
New additions without breaking backward compatibility bump the minor (and resets the patch)
Bug fixes and misc changes bump the patch
For all ColdBox releases, updates are provided for 12 months, and security fixes are provided for 2 years after the next major release.
In this section, you will find the release notes and links for each version's documentation. If you are looking for the release notes of previous major versions, use the version switcher at the top left of this documentation book.
Version 3.0 - March 2011
Version 2.0 - April 2007
Version 1.0 - June 2006
Exception bean can't cast `"i"` to a number value
Added debug argument to ScheduleExecutor and Scheduler when creating tasks for consistency
Reorganized ScheduledTasks functions within the CFC into code groups and comments
New StringUtil.prettySQL
method for usage in debugging and whoops reports
New testing matcher: toRedirectTo
for easier testing against relocations
New REST convention for custom error handlers: `on{errorType}Exception()`
Logging category in ColdBox scheduler is generic
Improve RestHandler Exception handler with on#ExceptionType#Exception() convention
Account for null or empty incoming json to prettyjson output
Incorporate appName into the appHash to give more uniqueness to locks internally
Removal of Lucee RC tests - no longer needed
Several AOP and Internal WireBox methods not excluded from delegations
Wirebox standalone is missing delegates
Injections are null, sometimes
getEnv
errors in Binder context
populateFromQuery delegate defaulting composeRelationships to true
Improve debug logging to not send the full memento on several debug operations
`toWebservice()` is now deprecated
ColdBox is maintained under the guidelines as much as possible. Releases will be numbered in the following format:
6.x
2022
2023
2025
7.x
2023
2024
2026
8.x
2024
2025
2027
9.x
2025
2026
2028
Release Notes for ColdBox 7.0.0
The full release notes per library can be found below. Just click on the library tab and explore their release notes:
COLDBOX-1079 Router resources doesn't seem to respect group() closures
COLDBOX-1100 Event Caching Does Not Preserve HTTP Response Codes
COLDBOX-1133 `getFullURL` encodes the query string when it should not.
COLDBOX-1136 Scoping lookup bug in Lucee affects route()
COLDBOX-1138 Event Cache Response Has Status Code of 0 (or Null)
COLDBOX-1139 make event caching cache keys lower cased to avoid case issues when clearing keys
COLDBOX-1143 render inline PDF (CB 6.8.1) throws a 500 error
COLDBOX-1145 RestHandler OnError() Exception not checking for empty `exception` blocks which would cause another exception on development ONLY
COLDBOX-1146 BiConsumer proxy was making both arguments required, when they can be null so execution fails
COLDBOX-1149 Woops and Adobe CF needs a double check if session/client is defined even if sessionManagement/clientManagement is defined
COLDBOX-1150 virtual app controller scoping is missing on ocassion due to this.load|unload flags
COLDBOX-1151 Integration Tests do not support NoRender()
COLDBOX-1153 RestHandler.cfc missing exception information on InvalidCredentials & TokenInvalidException
COLDBOX-1154 Invalid DateFormat Mask in Whoops.cfm
COLDBOX-1173 Update the Router.cfc to look at not only the cgi host but the forwarded hosts
COLDBOX-1175 calling function "view" from within function which has an argument named "view" causes error.
COLDBOX-1176 viewLocations struct does not exist in function renderer.layout() on line 684 if nolayout = true
COLDBOX-1191 Attempts to use `getHTMLBaseURL()` inside of Async Task Fail on ACF
COLDBOX-1193 Missing java casting on arrayRange() method ont the asyncmanager
COLDBOX-1194 Ensure modules are applied to routing action structs when necessary #243
COLDBOX-1196 Render collections items and counter variables are not thread safe since we migrated to a singleton renderer
COLDBOX-1202 urlMatches in the request context does not account for the path to be larger than the requested uri
COLDBOX-1204 Overridden events in preProcess interceptions assume event cache configuration of original request
COLDBOX-1211 Base Model and Interceptor Tests where overriding application.wirebox in integration tests
COLDBOX-1213 Var scoping issue on `includeUDF` in the super type
COLDBOX-1029 ModuleAwareness : Wirebox Injector Lookup Should Check Current Module First
COLDBOX-1155 Implement abort logic onAuthenticationFailure on RESTHandler
COLDBOX-1157 Reuse existing controller in getMockRequestContext()
COLDBOX-1159 JSON Serialization in `forAttribute` Does Not Support ACF Prefixing
COLDBOX-1171 Do not allow injection of the same applicatio helper on the same target
COLDBOX-1177 Please add more debugging info to REST handler
COLDBOX-1184 When whoops error template defaults to public for non-dev, the messaging is very confusing
COLDBOX-1185 ColdBox DebugMode with inDebugMode() helper
COLDBOX-1190 Reworking of several rest handler exception methods so they log the issues instead of announcing `onException` events and announce their appropriate events , onAuthenticationFailure, onAuthorizationFailure, onValidationException, onEntityNotFoundException
COLDBOX-1195 ColdBox Proxy should ignore the routing service captures to avoid redirects or returns
COLDBOX-1210 encapsulate route finding by name to the actual router itself
COLDBOX-1214 Compatibility layer for env methods in the Util object: getSystemSetting(), getSystemProperty(), getEnv()
COLDBOX-1022 Allow for Flash RAM to use a third party provided tracking variable via the new setting identifierProvider
COLDBOX-1039 Allow unregistering closure listeners
COLDBOX-1077 Provide ability for handlers/interceptors/etc. to have inherent self-knowledge of the module they live in for modulesettings/moduleConfig injections
COLDBOX-1137 Allow passing interception point first in interceptor listen() method
COLDBOX-1140 Whoops updates galore! SQL Syntax highlighting, json formatting and highlighting, and more
COLDBOX-1141 New Flow delegate helpers for functional usage everywhere in ColdBox land
COLDBOX-1142 New convention for module setting overrides: config/{moduleName}.cfc
COLDBOX-1147 PostLayoutRender and PostViewRender now pass which view/layout path was used to render
COLDBOX-1148 postEvent now get's new interceptData: ehBean, handler and data results
COLDBOX-1152 this.unloadColdBox is false now as the default thanks to the virtual test app
COLDBOX-1158 New `back()` function in super type that you can use to redirect back to your referer or a fallback
COLDBOX-1161 new toJson() helper in the Util class which is delegated in many locations around the framework to add struct based query serialization and no dubm security prefixes
COLDBOX-1162 Add in functionality to exclude patterns via router's findRoute()
COLDBOX-1164 New convention for coldfusion tags: includes/tags. Every ColdBox app will register that location as a repository for custom tags for your application
COLDBOX-1166 Lazy loading and persistence of engine helper to assist in continued performance and initial load speed
COLDBOX-1167 New core delegates for smaller building blocks, which leverages the `@coreDelegates` namespace
COLDBOX-1168 New coldbox based delegates mapped with `@cbDelegates`
COLDBOX-1172 core json utils now include a prettyJson() and a toPrettyJson() utilities
COLDBOX-1174 New getEnv() method on the base test class to get access to the env delegate for inquiring for env and java properties
COLDBOX-1178 ChronoUnit becomes the official cb Date Time Helper to assist with date/time conversions and formatting
COLDBOX-1179 New super type methods: getDateTimeHelper() getIsoTime() to assist with recurrent iso time conversions in modern APIs and responses
COLDBOX-1186 Add three environment location helpers in the controller and supertype: isProduction(), isDevelopment(), isTesting()
COLDBOX-1188 Request Context setRequestTimeout() method encapsulation, so you can control the time limit of a request
COLDBOX-1189 setRequestTimeout() mock in testing Request Context so handlers cannot override testing timeouts
COLDBOX-1192 Module Inception Isolation - every module has it's own injector that matches the module hierarchy
COLDBOX-1197 All rendering methods now accept a `viewVariables` argument that allows you to add variables into the view's `variables` scope
COLDBOX-1199 New request context method: `routeIs( name ):boolean` that evaluates if the passed name is the same as the current route
COLDBOX-1200 request context `getFullUrl()` renamed to `getUrl()`
COLDBOX-1201 request context `getFullPath()` renamed to `getPath()`
COLDBOX-1205 request context `event.getUrl( withQuery:true )` new argument `withQuery` to allow adding the query string or not
COLDBOX-1206 request context `event.getPath( withQuery:true )` new argument `withQuery` to allow adding the query string or not
COLDBOX-1207 New request context methods: `getPathSegments():array, getPathSegment( index, defaultValue ):string` so you can segment the incoming url path
COLDBOX-1208 Add `persist` and `persistStruct` to the `back()` method in the supertype
COLDBOX-1209 Add route names to resourceful routes according to conventions
COLDBOX-1215 this.moduleInjector enables modular injector hiearchy. By default it is false until ColdBox 8
COLDBOX-1216 New super type method: `getRootWireBox()` to get an instance of the root wirebox in the application
COLDBOX-1160 COMPAT: jsonQueryFormat has been removed in preference to "struct".
COLDBOX-1169 routes.cfm Support Removal
COLDBOX-1170 populateModel deprecated - refactored to just populate() in the supertype methods
COLDBOX-1187 Removal of uniqueUrls boolean indicator for URL routing, since Pretty URLs are now the standard. This rerouting feature needs to be removed.
WIREBOX-133 BeanPopulator renamed to ObjectPopulator to be consistent with naming
WIREBOX-132 WireBox caches Singletons even if their autowired dependencies throw exceptions.
WIREBOX-89 Wirebox - add onInjectorMissingDependency event
WIREBOX-130 Ability to remove specific objects from wirebox injector singleton's and request scopes via a `clear( key )` method
WIREBOX-131 Object Delegators
WIREBOX-134 Object Populator is now created by the Injector and it is now a singleton
WIREBOX-135 Object populator now caches orm entity maps, so they are ONLy loaded once and population with orm objects accelerates tremendously
WIREBOX-136 object populator cache relational metadata for faster population of the same objects
WIREBOX-137 New `this.population` marker for controlling mas population of objects. It can include an `include` and and `exclude` list.
WIREBOX-138 Lazy Properties
WIREBOX-139 Property Observers
WIREBOX-140 Transient request cache for injections and delegations
WIREBOX-141 New config setting transientInjectionCache to enable or disable globally, default is true
WIREBOX-142 You can now instantiate an Injector with the `binder` argument being the config structure instead of creating a binder
WIREBOX-143 New injection DSL for ColdBox Root Injector `coldbox:rootWireBox`
WIREBOX-144 Injectors can now track the root injector by having a root reference via `getRoot(), hasRoot()` methods
WIREBOX-145 New DSL for wirebox only root injectors: `wirebox:root`
CACHEBOX-83 Intermittent Exception from MetadataIndexer
November 18, 2023
Welcome to ColdBox 7.2.0, which packs a big punch on stability and tons of new features.
A new helper has been born that assists you with dealing with Database Schema-related methods that are very common and core to ColdBox and supporting modules. This will grow as needed and be decoupled to its own module later.
hasTable()
hasColumn()
getDatabaseInfo()
getTextColumnType()
getDateTimeColumnType()
getQueryParamDateTimeType()
allApply()
error handlersThe allApply()
is great when dealing with async operations on arrays or collections of objects. However, if something blows up, it would blow up with no way for you to log in or know what happened. However, now you can pass an errorHandler
argument which is a UDF/closure that will be attached to the onException()
method of the future object. This way, you can react, log, or recover.
All scheduled tasks now have a group
property so you can group your tasks. This is now available when creating tasks or setting them manually.
You can then get the group using the getGroup()
method or it will be added to all task metadata and stats.
everySecond()
periodA new period method shortcut: everySecond()
. Very useful so you can fill up your logs with data.
All task results, if any, are now stored in a ColdBox Optional. Which is a class that can deal with nulls gracefully and it's very fluent:
A container object which may or may not contain a non-null value. If a value is present, isPresent()
will return true
and get()
will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as orElse()
(return a default value if value not present) and ifPresent()
(execute a block of code if the value is present). See https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
New method to get the last result, if any, from a task via the getLastResult()
method.
Lot's of great new methods and goodies so you can deal with date and times and timezones Oh My!
now( [timezone] )
getSystemTimezoneAsString()
getLastBusinessDayOfTheMonth()
getFirstBusinessDayOfTheMonth()
dateTimeAdd()
timeUnitToSeconds()
validateTime()
getIsoTime()
toInstant()
toLocalDateTime()
parse()
toLocalDate()
getTimezone()
getSystemTimezone()
toJavaDate()
duration()
period()
If in your binder you declare aspects or AOP bindings. Then WireBox will automatically detect it and load the AOP Mixer listener for you. You no longer have to declare it manually.
When you declare categories in LogBox you usually choose the appenders to send messages to, but you could never exclude certain ones. Now you can use the exclude
property:
You now have two new event listeners that all LogBox appenders can listen to:
preProcessQueue( queue, context )
: Fires before a log queue is processed element by element.
postProcessQueue( queue, context )
: After the log queue has been processed and after the listener has slept.
processQueueElement
receives the queueThe processQueueElement( data, context, queue )
now receives the entire queue as well as the queue
third argument.
If you use the RollingFileAppender
the default layout format of the archive package was static and you could not change it. The default is:
Now you have the power. You can set a property for the appender called archiveLayout
which maps to a closure/UDF that will build out the layout of the file name.
The full release notes per library can be found below. Just click on the library tab and explore their release notes:
You can now configure CacheBox by just passing a struct of config data:
You can now configure LogBox by just passing a struct of config data:
Scheduled tasks now get a `group` property so you can use it for grouping purposes
New `now()` method in the DateTmeHelper with optional TimeZone
New datetimehelper method: getSystemTimezoneAsString()
New ScheduledTask helper: getLastResult() to get the latest result
LastResult is now a cbproxies Optional to denote a value or not (COMPAT)
new scheduledTask method: isEnabled() to verify if the task is enabled
Complete rewrite of Scheduled Task setNextRuntime() calculations to account for start end running scenarios
new ScheduledTask period : everySecond()
New SchemaInfo helper to help interrogate databases for metadata
Add an errorHandler to the allApply method so you can attach your own error handler to each future computation
casting to long instead of int when using LocalDateTime and plus methods to avoid casting issues.
Do not expose restful handler exception data unless you are in debug mode
RestHandler.cfc should catch NotAuthorized exception
getFirstBusinessDayOfTheMonth(), getLastBusinessDayOfTheMonth() now refactored to the dateTimeHelper
validateTime() is now a helper method in the DateTimeHelper
Migration of old tasks to new task syntax of task()
Scheduled Task Stats "NextRun", "Created", "LastRun" Using Wrong Timezones
onSessionEnd Error when using Coldbox_App_Key
Scheduled task isConstrainted() on day of the month was calculating the days in month backwards
set next run time when using first or last business day was not accounting times
Make wirebox.system.aop.Mixer
listener load automatically if any aspects are defined/mapped
Support ad-hoc struct literal of CacheBox DSL to configure CacheBox
New listeners for all appenders: preProcessQueue() postProcessQueue()
Add the queue as an argument to the processQueueElement() method
new rolling appender property archiveLayout
which is a closure that returns the pattern of the archive layout
Unhandled race conditions in FileRotator lead to errors and potential log data loss
log rotator was not checking for file existence and 1000s of errors could be produced
Support ad-hoc struct literal of LogBox DSL to configure LogBox
Add `Exclude` key to Logbox Categories to Easily Exclude Appenders
shutdown the appenders first instead of the executors to avoid chicken and egg issues
Change fileMaxArchives default from 2 to 10
Removal of instance approach in preferences to accessors for the LogBoxConfig
Learn about the authors of ColdBox and how to support the project.
The source code for this book is hosted on GitHub: https://github.com/ortus-docs/coldbox-docs. You can freely contribute to it and submit pull requests. The contents of this book are copyrighted by Ortus Solutions, Corp and cannot be altered or reproduced without the author's consent. All content is provided "As-Is" and can be freely distributed.
The majority of code examples in this book are done in cfscript
.
The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL - http://www.ortussolutions.com/products/commandbox
Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.
ColdBox, CommandBox, FORGEBOX, TestBox, ContentBox, and Ortus Solutions are all trademarks and copyrights of Ortus Solutions, Corp.
The information in this book is distributed “as is” without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity concerning loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software, and resources described in it.
We highly encourage contributions to this book and our open-source software. The source code for this book can be found in our GitHub repository where you can submit pull requests.
10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - https://www.harvesting.org/. So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.
Shalom Children’s Home (https://www.harvesting.org/) is one of the ministries that are dear to our hearts located in El Salvador. During the 12-year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.
Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education, and life skills training in a Christian environment. The home is supported by a child sponsorship program.
We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador who either has no families or have been abandoned. This is a good earth to seed and plant.
Author biographies of the ColdBox Platform
Luis Majano is a Computer Engineer, and published author that has been creating software since the year 2000. He was born in San Salvador, El Salvador in the late 70s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he studied and completed his Bachelor of Science in Computer Engineering at Florida International University.
He is the founder and CEO of Ortus Solutions, a consulting firm specializing in web development, ColdFusion (CFML), Java development, and all open-source professional services under the ColdBox, CommandBox, and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, TestBox, LogBox, and anything “BOX”, and contributes to over 250 open-source projects. He has a passion for learning and mentoring developers so they can succeed with sustainable software practices and the usage and development of open-source software. You can read his blog at www.luismajano.com
Luis has a passion for Jesus, tennis, golf, volleyball, and anything electronic. Random Author Facts:
He played volleyball in the Salvadorean National Team at the tender age of 17
The Lord of the Rings and The Hobbit are his favorite books (Geek!)
His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)
He has a geek love for circuits, microcontrollers, and overall embedded systems.
He has of late (during old age) become a fan of organic gardening.
Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!
“Trust in the LORD with all your heart, and do not lean on your own understanding.” – Proverbs 3:5
Jorge is an Industrial and Systems Engineer born in El Salvador. After finishing his Bachelor studies at the Monterrey Institute of Technology and Higher Education ITESM, Mexico, he went back to his home country where he worked as the COO of Industrias Bendek S.A.. In 2012 he left El Salvador and moved to Switzerland in pursuit of the love of his life. He married her and today he resides in Basel with his lovely wife Marta and their daughter Sofía.
Jorge started working as project manager and business developer at Ortus Solutions, Corp. in 2013, . At Ortus he fell in love with software development and now enjoys taking part on software development projects and software documentation! He is a fellow Christian who loves to play the guitar, worship and rejoice in the Lord!
Therefore, if anyone is in Christ, the new creation has come: The old has gone, the new is here! 2 Corinthians 5:17
Brad grew up in southern Missouri where he systematically disassembled every toy he ever owned which occasionally led to unintentional shock therapy (TVs hold a charge long after they've been unplugged, you know) After high school he majored in Computer Science with a music minor at MidAmerica Nazarene University (Olathe, KS). Today he lives in Kansas City with his wife and three girls where he still disassembles most of his belongings (including automobiles) just with a slightly higher success rate of putting them back together again.) Brad enjoys church, all sorts of international food, and the great outdoors.
Brad has been programming CFML for 12+ years and has used every version of CF since 4.5. He first fell in love with ColdFusion as a way to easily connect a database to his website for dynamic pages. Brad blogs at (http://www.codersrevolution.com) and likes to work on solder-at-home digital and analog circuits with his daughter as well as building projects with Arduino-based microcontrollers.
Brad's CommandBox Snake high score is 141.
The official ColdBox 7 upgrade guide
ColdFusion 2016 support has been dropped. Adobe doesn't support them anymore, so neither do we.
addAsset()
ChangesThe addAsset()
now leverages the following settings for determining the convention locations for JavaScript and CSS assets:
So if you want to change the location of your JavaScript and CSS, you can create these settings in your ColdBox.cfc
In ColdBox 7, the this.unloadColdBox
setting is false by default. In ColdBox 6 this was true by default. So make sure you update this setting if you want ColdBox to be unloaded on each Test Bundle iteration.
All modules have their own injector now if you use the this.moduleInjector = true
setting. Meaning the concept of a global injector no longer exists. Therefore, there are some edge cases where certain types of code will not work in ColdBox 7.
If you are creating your own WireBox injector in your tests and using integration testing, you will have Injector collisions.
This actually affects EVERY version of ColdBox because the default behavior of instantiating an Injector like the code above is to put the Injector in application scope: application.wirebox.
This means that the REAL injector in an integration test lives in application.wirebox
will be overridden. To avoid this collision, disable scope registration:
For those of you with custom wirebox DSLs, you'll need to update your DSL to match the new process()
method signature:
This setting was in charge of converting NON-SES Urls into SES URLs. However, it was extremely error-prone and sometimes produced invalid URLs. This is now completely removed and if the user wants to do this feature, they can use CommandBox or Nginx, or Apache rewrites.
jsonQueryFormat
RemovedThe jsonQueryFormat
argument for rendering data is now removed. We default to an array of structs format for queries as it is the only format that makes sense.
There is a performance penalty to use any deprecated methods now, since they emit warnings to the logs.
These methods have been deprecated since version 6. Please use the shorthand versions
view()
layout()
externalView()
The core utility methods on env
and Java variables have been deprecated from the utility object and moved to the Env
delegate. Here are the methods deprecated:
getSystemSetting()
getSystemProperty()
getEnv()
So if you had code like this:
That will work, but it is deprecated now. You will have to move it to the delegate:
If you were using them in your integration or unit tests, then you can use our shorthand methods:
The object BeanPopulator
has been deprecated in favor of ObjectPopulator
.
Welcome to the world of ColdBox!
ColdBox has the following supported IDE Tools:
No Java Runtime (30mb)
Embedded Runtime (80mb)
Once you download and expand CommandBox, you will have the box.exe
or box
binary, which you can place in your Windows Path or *Unix /usr/bin
folder to have it available system-wide. Then open the binary, and CommandBox will unpack to your user's directory: {User}/.CommandBox
. This happens only once, and the next thing you know, you are in the CommandBox interactive shell!
We can execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.
All examples in this book are based on having an interactive shell open.
We can now use CommandBox to install the ColdBox CLI to assist us in all aspects of ColdBox Development:
You will now have a coldbox
command namespace. You can do coldbox help
and get all the help you need with the commands.
To get started, open the CommandBox binary or enter the shell by typing box
in your terminal or console. Then let's create a new folder and install ColdBox into a directory.
Tip : You can also install the latest bleeding-edge version by using the coldbox@be
slug instead, or any previous version.
To uninstall ColdBox from this application folder, just type uninstall coldbox
. Try it out!
To update ColdBox from a previous version, just type update coldbox
.
Model View Controller
Model - Business Logic, Data, Queries, Etc
View - Representation of your models, queries, data.
Controller - Orchestrator of client request to the appropriate models and views
Let's go a little deeper.
The Views are what the users see and interact with. They are the templates used to render your application out for the web browser. Typically this means cfm/HTML, but it can also be JSON, XML, data views, etc.
In modern times, your views can even be pure HTML with a combination of a JavaScript MVC framework. The major players in the MVC front-end world that we would recommend in order of personal preference:
Controllers are the traffic cops of your application. They direct flow control, and interface directly without incoming parameters from FORM and URL scopes. It is the controller’s job to communicate with the appropriate models for processing, and set up either a view to display results or return serialized data like JSON, XML, PDF, etc.
The model and the view layers have different concerns about their implementations. A view layer is concerned with how to render the data, the type of browser, or remote rendering, etc. While the model is more concerned with the business rules of the application, how to store data and even database operations. You use different development approaches to each layer.
Due to this separation, you can easily create multiple views for the same model data without affecting how the model works or is coded. The view layers can adapt to the model by coding their own implementations. This makes it really easy to create multiple GUI’s for applications.
Non-visual objects are easier to test than visual objects, in theory. With the introduction of Selenium, integration and visual UI testing has become rather simple. However, the key benefit here is that testing can be done separately. Frameworks like ColdBox even give you the ability to do UI and integration testing within its domain.
The most important benefit that we can arise out of the MVC pattern, is the direction of the dependencies. A view depends on its model data and controller, but the model itself does not depend on the view or controllers. This is how you want to build your business logic, encapsulated and providing a good API.
As you can see from the spaghetti hell diagram above, everything is linear and can become extremely convoluted. Tracking bugs are difficult, maintenance suffers and reusability is not efficient. Everything is in the same bowl of soup.
With the introduction of MVC we can hack away our spaghetti hell and at least have three distinct and separate layers of logic. Ahh much better. However, we can get even more complex.
MVC Plus shows us how you can further partition your model layer into more layers. We can identify now a layer of service CFCs and data access object CFCs. The main transportation of data between these layers by default is implied to be ColdFusion Query objects.
In this architecture approach, we have replaced (mostly) queries as our data structure of preference and converted to the usage of business objects. We are approaching a more object oriented architectural style. Remember that data is just data, objects are data plus behavior. We can encapsulate more features and abstract more behavior into actual objects now, which we could not do with queries.
In this architecture approach we have replaced business objects for ORM entities and replaced our data access layer to be controlled now by the ORM. This takes us very deep into object oriented land where the majority of our model is now modeled vi relational objects.
Stern Warning: ORMs are NOT silver bullets. They are an incredible tool that must be used for the right reasons and at the right time. Do not be confused in that you must ONLY use the ORM. No, you can still use DAOs and queries for certain things that matter. You do not need to retrieve entire object graph collections if NOT needed.
Out of the box, ColdBox gives you all the RESTFul capabilities you will need to create robust and scalable RESTFul services. Let's add some RESTFul capabilities to our contact listing we created in the previous section.
If you know beforehand what type of format you will be responding with, you can leverage ColdBox auto-marshaling in your handlers. By default, ColdBox detects any return value from handlers, and if they are complex, it will convert them to JSON automatically for you:
ColdBox detects the array and automatically serializes it to JSON. Easy Peasy!
The request context object has a special function called renderData()
that can take any type of data and marshall (convert) it for you to other formats like xml, json, wddx, pdf, text, html
or your own type.
So let's open the handlers/contacts.cfc
and lets create a new action called data
This tells ColdBox to render the contacts data in 4 formats: XML, JSON, pdf, and HTML. WOW! So how would you trigger each format? Via the URL, of course.
ColdBox has the ability to detect formats via URL extensions or an incoming Accepts
header. If no extension is sent, then ColdBox attempts to determine the format by inspecting the Accepts
header. If we still can't figure out what format to choose, the default of html
is selected for you.
Tip: You can also avoid the extension and pass a URL argument called format
with the correct format type: ?format=json
.
Let's add a new route to our system that is more RESTFul than /contacts/index.json
. You will do so by leveraging the application's router found at config/Router.cfc
. Find the configure()
method, and let's add a new route:
We have registered the API route and also defaulted the format to JSON
. Try it out.
Make sure you add routes above the default ColdBox route. If not, your route will never fire.
Tip: Every time you add new routes, make sure you reinit the application: http://localhost:{port}/?fwreinit
.
You can now visit the new URL pattern, and you have successfully built a RESTFul API for your contacts.
The major compatibility issues will be covered, as well as how to upgrade to this release from previous ColdBox versions smoothly. You can also check out the guide to give you a full overview of the changes.
The announceInterception()
method . You will need to refactor any uses of announceInterception()
to use announce()
instead.
The routes.cfm
approach is now removed in ColdBox 7. You will need to migrate to the Router.cfc
in your application and/or modules.
We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more. Please remember to us in Github.
Sublime -
VSCode -
The first step in our journey is to CommandBox. is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager, and Embedded Server. We will be using CommandBox for almost every exercise in this book, and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.
You can download CommandBox from the official site: and install on your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:
So make sure you choose your desired installation path and follow the instructions here:
CommandBox will resolve coldbox
from ForgeBox (); use the latest version available, and download and install it in this folder alongside a box.json
file that represents your application package.
CommandBox can now track this version of ColdBox for you in this directory. In the we will scaffold a ColdBox application using an application template.
You can find many scaffolding templates for ColdBox in our Github organization:
"A developer often wishes to separate data (model) and user interface (view) concerns, so that changes to the user interface will not affect data handling, and that the data can be reorganized without changing the user interface. The model-view-controller solves this problem by decoupling data access and business logic from data presentation and user interaction, by introducing an intermediate component: the controller."
MVC is a popular design pattern called which seeks to promote good maintainable software design by separating your code into 3 main tiers:
The Model is the heart of your application. Your business logic should mostly live here in the form of services, beans, entities and DAOs. A dependency injection framework becomes invaluable when dealing with object oriented model layers: (Dependency Injection Framework) is the framework of choice for dependency injection and aspect oriented programming.
VueJS -
Angular -
ReactJS -
EmberJS -
By implementing an MVC Framework to your applications you will gain several benefits that come inherent to the MVC design pattern. The most important benefit of MVC is that you will be separating the presentation from the model. This is a very important heuristic of software development as is applied and responsibilities are delegated upon the layers.
There are many types of MVC architectures and hopefully the following diagrams can help you in the progression from spaghetti hell to the most complex MVC architecture using an or Object Relational Mapper.
We have even build a companion package for ColdBox called that will help you build more pragmatic and enjoyable ORM applications.
Tip: You can find much more information about building ColdBox RESTFul services in our
Tip: You can find more information at the API Docs for renderData()
here )
You can find much more about routing in our
htmlhelper_js_path
/includes/js
htmlhelper_css_path
/includes/css
ColdBox provides you with a nice method for generating links between events by leveraging an object called event
that is accessible in all of your layouts/views and event handlers. This event
object is called behind the scenes the request context object, which models the incoming request and even contains all of your incoming FORM
and URL
variables in a structure called rc
.
Tip: You will use the event object to set views, set layouts, set HTTP headers, read HTTP headers, convert data to other types (json,xml,pdf), and much more.
You can easily build links with ColdBox by using two methods:
event.buildLink()
- Build links to events or URL routes
event.route()
- Build links to specifically named routes
Here are the signatures
Edit the views/virtual/hello.cfm
page and wrap the content in a cfoutput
and create a link to the main ColdBox event, which by convention, is main.index
. You can use main.index
or just main
(Remember that index
is the default action)
This code will generate a link to the main.index
event in a search engine-safe manner and in SSL detection mode. Go execute the event: http://localhost:{port}/virtual/hello
and click on the generated URL; you will now be navigating to the default event /main/index
. This technique will also apply to FORM submissions:
Tip You can visit our API Docs for further information about the event
object and the buildLink
method: http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/web/context/RequestContext.html.
For extra credit try to use more of the buildLink
arguments.
We have been using routing by convention, but let's do named routes now to control the URL. Let's create a /home
route that will execute the main.index
event and update our view to change the building of the URL via route()
. Let's open the config/Router.cfc
We use the route()
method to register URL patterns and then tell the router what to execute if matched. This can be an event, but it can also be a view, an inline action, a relocation, and much more. Since we registered new URLs you need to reinit the app (?fwreinit=1
). Now let's update the link in the hello
view:
Try it out now!
Tip: Check out the routing API Docs for further information.
Now let's create our first event handler controller, which in ColdBox is called Event Handler. Let's go to CommandBox again:
This will generate the following files:
A new handler called hello.cfc
inside of the handlers
folder
A view called index.cfm
in the views/hello
folder
An integration test at tests/specs/integration/helloTest.cfc
.
Now go to your browser and enter the following URL to execute the generated event:
You will now see a big hello.index
outputted to the screen. You have now created your first handler and view combination. However, how did this work? It works by convention.
Your application router is located at : config/Router.cfc
. It will include a few default routes for you and the following default URL route:
This route tells ColdBox to look for the names of handlers (including directory names) and names of the handler actions (functions). The ?
on the :action
portion denotes that the action might or might not exist in the URL. If it doesn't exist, then another convention is in play, the default action, which is index
.
So /main
will execute main.index
and /main/index
will execute main.index
Let's check out the handler code:
As you can see, a handler is a simple CFC with functions on them. Each function maps to an action that is executed via the URL. The default action in ColdBox is index()
which receives three arguments:
event
- An object that represents the request and can modify the response. We call this object the request context.
rc
- A struct that contains both URL/FORM
variables (unsafe data)
prc
- A secondary struct that is private only settable from within your application (safe data)
The event object is used for many things, in the case of this function we are calling a setView()
method which tells the framework what view to render to the user once execution of the action terminates.
Tip: The view is not rendered in line 7, but rendered after the execution of the action by the framework.
Did you detect a convention here?
The sections in the URL are the same as the name of the event handler CFC (hello.cfc
) and method that was generated index()
. By convention, this is how you execute events in ColdBox by leveraging the following URL pattern that matches the name of a handler and action function.
Tip : You can also nest handlers into folders and pass the name of the folder(s) as well.
If no action
is defined in the URL, then the default action of index
will be used.
All of this URL magic happens thanks to the URL mappings capabilities in ColdBox. By convention, you can write beautiful URLs that are RESTFul and by convention. You can also extend them and create more expressive URL Mappings by leveraging the config/Router.cfc
which is your application router.
Tip: Please see the event handlers guide for more in-depth information.
Now let's create a virtual event, just a view we want to execute with no event handler controller needed. This is a great way to incorporate non-mvc files into ColdBox. Migrating from a traditional application?
Open the view now (/views/virtual/hello.cfm
) and add the following:
Then go execute the virtual event:
You will get the Hello From ColdBox Land!
displayed! This is a great way to create tests or even bring in legacy/procedural templates into an MVC framework.
Tip: You can see our layouts and views section for more in-depth information.
ColdBox is a conventions-based MVC framework for ColdFusion (CFML). It is fast, scalable, and runs on CFML engines such as Adobe ColdFusion and the open source CFML engine Lucee, for a completely free and open source development stack. ColdBox itself is Professional Open Source Software, backed by Ortus Solutions which provides support, training, architecture, consulting and commercial additions.
ColdBox was the first ColdFusion (CFML) framework to embrace Conventions Over Configuration and comes with a modular architecture to enhance developer and team productivity. It integrates seamlessly with CommandBox CLI to provide you with REPL interfaces, package management, console development, testing, and automation.
Here’s a look at some of the core components of the ColdBox platform. Each of these come bundled with the framework, but are also available as separate stand-alone libraries that can be used in ANY ColdFusion application or framework:
Managing your domain objects has never been easier with this Dependency Injection and Inversion of Control (IOC) framework. WireBox supports all forms of injection as well as maintaining persistence for your domain objects. WireBox can also interface and provide Java classes, web services, and aspects of the ColdBox framework itself. WireBox also has built-in Aspect Oriented Programming (AOP) support; you’ll never need another Dependency Injection engine again.
This is a highly-configurable logging library which can be set up to relay messages of different types from any portion of your application to any number of predefined logging appenders.
A highly-versatile caching aggregator and enterprise cache that allows for multiple named caching stores as well as granular control over cache behaviors and eviction policies. CacheBox can interface out of the box with Ehcache, Adobe ColdFusion cache, and any Lucee cache.
A 60 minute guide to start working with ColdBox
This guide has been designed to get you started with ColdBox in fewer than 60 minutes. We will take you by the hand and help you build a RESTFul application in 60 minutes or less. After you complete this guide, we encourage you to move on to the Getting Started Guide and then to the other guides in this book.
You can find the source code of this quickstart here: https://github.com/coldbox-samples/60-minute-quickstart
Please make sure you download and install the latest CommandBox CLI. We will show you how in the Installing ColdBox section.
Grab a cup of coffee or tea
Get comfortable
The Ortus Community is the way to get any help for our entire platform and modules: https://community.ortussolutions.com
Discover the power of ColdBox 7.0.0
ColdBox 7.0.0 is a major release for the ColdBox HMVC platform. It has some dramatic new features as we keep pushing for more modern and sustainable approaches to web development and tons of bug fixes and improvements.
We break down the major areas of development below, and you can also find the full release notes per library at the end.
This release drops support for Adobe 2016 and adds support for Adobe 2023 and Lucee 6 (Beta). Please note that there are still issues with Adobe 2023 as it is still in Beta.
We now have an official CLI for ColdBox, which lives outside CommandBox. It will always be included with CommandBox, but it now has its own life cycles, and it will support each major version of ColdBox as well.
The new CLI has all the previous goodness but now also v7 support and many other great features like migration creation, API testing, and more. You can find the source for the CLI here: https://github.com/coldbox/coldbox-cli
We have updated our VSCode ColdBox extension now to support the latest version of snippets and skeletons for code generation.
All the application templates have been updated in our ColdBox Templates Org: https://github.com/coldbox-templates. They have been updated to support our LTS strategy, so now we can have templates based on each major iteration of ColdBox.
Here is a listing of the latest supported templates:
Default
default
The default ColdBox application template
Elixir
elixir
The default
template with ColdBox elixir support for asset pipelines
Modern (experimental)
modern
A fresh new approach to ColdBox applications that are non-root based. Still experimental
Rest
rest
A base REST API using ColdBox
Rest HMVC
rest-hmvc
An HMVC REST API using modules
Super Simple
supersimple
Barebones conventions baby!
WireBox has gotten tons of love in this release, with several additions, bug fixes, and improvements.
This feature is one of the most impactful for applications that leverage DI on transient objects, especially ORM-related applications. WireBox will now, by default, cache the signatures of the injections and delegations for you, so they are only done once per instance type. This addition has brought speed improvements of over 585% in Lucee and Adobe ColdFusion. You read that right, 585% performance increases. This is really a game changer for ORM-heavy applications that use DI and delegations. Go try it; you don't have to do a thing. Install and run it!
If this is not for you or there are issues in your system because of it, we have a setting for it to turn it off. Open the WireBox.cfc
binder and add it as a config item.
You can also disable the cache on a per-CFC basis by using the transientCache=false
annotation in your component declaration:
Even though the transient cache can help tremendously with performance, there is a price to pay. All the injections and delegations will be cached on a per-cfc definition basis. Thus, if you are injecting transients, those transients will become singletons. Therefore you have two options to alleviate this side effect:
Disable the cache entirely (The heavy-handed approach)
Disable the cache on that specific entity via the transientCache
annotation (The surgical approach)
Add the transient injection as a provider
injection (The ninja approach)
Add the property as a lazy property and add a builder that will construct it when called (The Jedi approach)
WireBox Delegators
WireBox supports the concept of object delegation in a simple, expressive DSL. You can now add a delegate
annotation to injections or use the delegates
annotations to components to inject and absorb the object's methods into yourself.
In object-oriented programming, an object delegator is a programming technique where an object delegates some of its responsibilities to another object. The delegating object passes the responsibility for handling a particular task to the delegate object. This allows the delegating object to focus on its core responsibilities while the delegate object handles the delegated task.
Basically, a way to inject/proxy calls from one object to the other and avoid the overuse of inheritance, and avoid runtime mixins. WireBox provides a set of rules for method lookup and dispatching that will allow you to provide delegation easily in your CFML applications. This feature is similar to traits in PHP or object delegators in Kotlin.
You can use it to encapsulate behavior on small, focused, and testable classes that can be brought in as traits into ANY component without abusing inheritance. In contrast, object delegation is a more flexible approach that allows objects to delegate tasks to any other object, regardless of its class hierarchy. Finally, object delegation can help to improve code performance by allowing objects to use specialized delegate objects for specific tasks.
Let's look at an example of how we would use delegation without this feature:
Now let's look at the computer
As you can see, in the traditional approach we must type and inject and know every detail of the delegated methods. Now let's delegalize it via WireBox:
Or use the shorthand notation via the delegates
annotation of components:
You can also do prefixes, suffixes, method includes, excludes, and even add as many delegates as you want:
Read more about delegates here: https://wirebox.ortusbooks.com/usage/wirebox-delegators
Core Delegates
Now that we have seen what delegators are, WireBox offers core delegators to your application via the @coreDelegates
namespace
Async - This delegate is useful to interact with the AsyncManager and the most used functionality for asynchronous programming
DateTime - Leverage the date time helper
Env - Talk to environment variables
Flow - Several fluent flow methods
JsonUtil - JSON utilities
StringUtil - String utilities
Population - Population utilities
So let's say you have a service that needs to populate objects and work with the system environment:
WireBox Property Observers
WireBox supports the concepts of component property observers. Meaning that you can define a function that will be called for you when the setter
for that property has been called and thus observe the property changes.
You will accomplish this by tagging a property with an annotation called observed
then by convention, it will look for a function called: {propertyName}Observer
by convention. This function will receive three arguments:
newValue
: The value is set into the property
oldValue
: The old value of the property, including null
property
: The name of the property
If you don’t like the convention and want to name the function as you see fit, then you can place the value of the observed annotation as the function's name to call.
property name="data" observed="myObserver"
Read more here: https://wirebox.ortusbooks.com/advanced-topics/property-observers
WireBox Lazy Properties
WireBox supports the concept of marking properties in your components as lazy
. This will allow the property to be constructed ONCE when requested ONLY (lazy loaded). This way, you can take advantage of the construction of the property being lazy-loaded.
Internally, we will generate a getter method for you that will make sure to construct your property via a builder function you will provide, lock the request (by default), store it in the variables
scope, and return it to you.
Note: With lazy properties, you must use the getter only to retrieve the property
Read more about Lazy Properties here: https://wirebox.ortusbooks.com/advanced-topics/lazy-properties
onInjectorMissingDependency
eventA new event called onInjectorMissingDependency
is now registered in Wirebox. It will be called whenever a dependency cannot be located. The data
sent into the event will contain:
name - The name of the requested dependency
initArguments - The init
arguments, if passed
targetObject - The target object that requested the dependency
injector - The injector in use building the dependency
If you return in the data
struct an element called, instance
we will return that as the dependency, else the normal exception will be thrown.
The object populator now caches ORM entity maps, so they are only loaded once, and the population with ORM objects accelerates tremendously.
The object populator caches relational metadata for a faster population of the same type of objects
this.population
This new convention allows for objects to encapsulate the way the mass population of data is treated. This way, you don’t have to scrub or pass include excludes lists via population arguments; it can all be nicely encapsulated in the targeted objects:
The populator will look for a this.population
struct with the following keys:
include
: an array of property names to allow population ONLY
exclude
: an array of property names to NEVER allow population
The population methods also get a new argument called: ignoreTargetLists
which defaults to false, meaning it inspects the objects for these population markers. If you pass it as true
then the markers will be ignored. This is great for the population of objects from queries or an array of structs or mementos that YOU have control of.
You can now instantiate an Injector with the binder
argument being the config structure instead of creating a binder. This will allow you to create injectors and pass the configuration structure as well:
Each injector can now have a human-friendly name via the name
property
You can also now use a clear( key )
method in ALL scopes so you can remove a-la-carte objects from any supported scope.
In a ColdBox or WireBox context, there will always be a root
injector in a hierarchy. This root injector can have children, and all of these children can have a direct link to its root via a hasRoot() and getRoot()
methods.
hasRoot()
getRoot()
setRoot()
There are also injection DSLs for retrieving the root injector:
Thanks to Giancarlo Gomez, scheduled tasks get a big revamp in ColdBox 7. Here are the updates:
annually
You can now task on an annual basis
delayTimeUnit
used to work with delays regardless of the setting in the chain
debug
used for debug output during task executions
firstBusinessDay
boolean to flag the task as on the first business day schedule
lastBusinessDay
boolean to flag the task as on the last business day schedule
taskTime
log of time of day for first business day and last business day tasks
startTime
limits tasks to run on or after a specific time of day
endTime
limits tasks to run on or before a specific time of day
meta
The user can use this struct to store any data for the task ( helpful for building UIs and not having to manage outside of the task )
stats.nextRun
ColdBox now determines the next run for the task in its scheduler interval
run( boolean force = false )
Added the force
argument, to allow running the task on demand, even if it is paused.
validateTime()
Sets minutes if missing from time entry and returns time value if successful - removes a lot of repetitive code
debug()
Used for setting debug setting ( can also be done in task init )
startOnTime()
used to set variables.startTime
endOnTime()
used to set variables.endTime
between()
used to set variables.startTime and variables.endTime in one call
setMeta()
used to set variables.meta
setMetaKey()
used to add / save a key to variables.meta
deleteMetaKey()
used to delete a key from variables.meta
getLastBusinessDayOfTheMonth()
getFirstBusinessDayOfTheMonth()
setInitialNextRunTime()
setInitialDelayPeriodAndTimeUnit()
setNextRunTime()
debugLog()
In ColdBox 7, you can now store the module configurations outside of the config/Coldbox.cfc
. Especially in an application with many modules and many configs, the modulesettings
would get really unruly and long. Now you can bring separation. This new convention will allow module override configurations to exist as their own configuration file within the application’s config folder.
The configuration CFC will have one configure()
method that is expected to return a struct of configuration settings as you did before in the moduleSettings
Just like a ModuleConfig
this configuration override also gets many injections:
This module configuration object will also inherit the ModuleConfig.cfc
behavior that if you create methods with the same name as the environment you are on, it will execute it for you as well.
Then you can change the original
struct as you see fit for that environment.
We have an experimental feature in ColdBox 7 to enable per-module injectors. This will create a hierarchy of injections and dependency lookups for modules. This is in preparation for future capabilities to allow for multi-named modules in an application. In order to activate this feature you need to use the this.moduleInjector = true
in your ModuleConfig.cfc
Once enabled, please note that your module injector will have a unique name, and a link to the parent and root injectors and will ONLY know about itself and its children. Everything will be encapsulated under itself. No more global dependencies, we are now in module-only dependencies. This means each module must declare its dependencies beforehand.
If the module injector is enabled, you will also get different injections in your ModuleConfig
binder
The root injector binder
rootWireBox
The root injector or global injector.
wirebox
This is now a reference to the module's injector.
The supertype also has methods to interact with the root and module injector getRootWireBox()
and getWireBox() for the module injector.
Every module also can inject/use its models
without the need to namespace them.
The injector will look into the models
of the module first and then it's children. Parent lookups are not guaranteed yet.
Please note that this is still experimental and there could be issues of not finding models or DSLs.
You can now use the {this}
placeholder in injections for module configurations-settings, and ColdBox will automatically replace it with the current module it's being injected in:
This is a great way to keep your injections clean without adhering to the module name.
listen()
OrderYou can now use listen( closure, point )
or listen( point, closure)
when registering closure interception points.
Since WireBox introduced delegates, we have taken advantage of this great reusable feature throughout ColdBox. We have also created several delegates for your convenience that can be used via its name and the @cbDelegates
namespace:
AppModes
Methods to let you know in which tier you are on and more
Interceptor
Announce interceptions
Locators
Locate files and/or directories
Population
Populate objects
Rendering
Render views and layouts
Settings
Interact with ColdBox/Module Settings
Let's say you have a security service that needs to get settings, announce events and render views:
Here you can find more information about the CBDelegates: https://s3.amazonaws.com/apidocs.ortussolutions.com/coldbox/7.0.0/coldbox/system/web/delegates/package-summary.html
In previous versions of ColdBox, it would auto-detect unique request identifiers for usage in Flash Ram, storages, etc., following this schema:
If we have session
enabled, use the jessionId
or session
URL Token
If we have cookies enabled, use the cfid/cftoken
If we have in the URL
the cfid/cftoken
Create a unique request-based tracking identifier: cbUserTrackingId
However, you can now decide what will be the unique identifier for requests, flash RAM, etc by providing it via a coldbox.identifierProvider
as a closure/lambda in your config/Coldbox.cfc
If this closure exists, ColdBox will use the return value as the unique identifier. A new method has also been added to the ColdBox Controller so you can retrieve this value:
The supertype as well so all handlers/layouts/views/interceptors can get the user identifier:
ColdBox 7 introduces opinionated helpers to the FrameworkSuperType
so you can determine if you are in three modes: production, development, and testing by looking at the environment
setting:
inProduction() == true
production
inTesting() == true
testing
inDevelopment() == true
development
or local
You can also find these methods in the controller
object.
These super-type methods delegate to the ColdBox Controller. So that means that if you needed to change their behavior, you could do so via a Controller Decorator.
You can also use them via our new AppModes@cbDelegates
delegate in any model:
When you register resourceful routes now, they will get assigned a name so you can use it for validation via routeIs()
or for route link creation via route()
GET
/photos
photos.index
photos
GET
/photos/new
photos.new
photos.new
POST
/photos
photos.create
photos
GET
/photos/:id
photos.show
photos.process
GET
/photos/:id/edit
photos.edit
photos.edit
PUT/PATCH
/photos/:id
photos.update
photos.process
DELETE
/photos/:id
photos.delete
photos.process
back()!
The framework super type now sports a back()
function so you can use it to redirect back to the referer in a request.
Here is the method signature:
RequestContext
Routing/Pathing EnhancementsThe RequestContext has a few new methods to assist you when working with routes, paths, and URLs.
routeIs( name ):boolean
Verify if the passed name
is the current route
getUrl( withQuery:boolean )
Returns the entire URL, including the protocol, host, mapping, path info, and query string.
getPath( withQuery:boolean )
Return the relative path of the current request.
getPathSegments():array
Get all of the URL path segments from the requested path.
getPathSegment( index, defaultValue )
Get a single path segment by position
The FrameworkSuperType
now has a getDateTimeHelper(), getIsoTime()
methods to get access to the ColdBox coldbox.system.async.time.DateTimeHelper
to assist you with all your date/time/timezone needs and generate an iso8601 formatted string from the incoming date/time.
You can also use the new date time helper as a delegate in your models:
Check out the API Docs for the latest methods in the helper
variables
Scope VariablesYou can now influence any view by injecting your own variables into a view's variables
scope using our new argument: viewVariables
. This is great for module developers that want native objects or data to exist in a view's varaibles
scope.
Then you can use the wire
and client
objects in your views natively:
Whoops got even more love:
SQL Syntax Highlighting
JSON Pretty Printing
JSON highlighting
Debug Mode to show source code
Production detection to avoid showing source code
Rendering performance improvements
The RESTFul handlers have been updated to present more useful debugging when exceptions are detected in any call to any resource. You will now get the following new items in the response:
environment
A snapshot of the current routes, URLs and event
exception
A stack frame, detail, and type
DebugMode
ColdBox now has a global debugMode
setting, which is used internally for presenting sources in Whoops and extra debugging parameters in our RESTFul handler. However, it can also be used by module developers or developers to dictate if your application is in debug mode or not. This is just a fancy way to say we trust the user doing the requests.
The default value is false
You also have a inDebugMode()
method in the main ColdBox Controller and the FrameworkSuperType that will let you know if you are in that mode or not.
You can also use the AppModes@cbDelegates
delegate to get access to the inDebugMode
and other methods in your models:
All integration tests now won't unload ColdBox after execution. Basically, the this.unloadColdBox = false
is now the default. This is to increase performance where each request run tries only to load the ColdBox virtual app once.
All test bundles now get a getEnv()
method to retrieve our environment delegate so you can get env settings and properties:
All logging messages in LogBox are now inspected for simplicity or complexity. If any extra argument is a complex variable, then LogBox will now serialize the output to JSON, and it will prettify it. This will allow for your log messages in your console to now look pretty!
If exception objects are being sent to any logger, LogBox will detect it and pretty print it back to the console instead of dumping a massive exception object.
In previous versions, you had to use the canxxxx
method to determine if you can log at a certain level:
In ColdBox 7, you can use a one-liner by leveraging a lambda function:
The RequestContext now has a setRequestTimeout()
function that can provide timeouts in your application, and when in testing mode, it will mock the request timeout. This is essential to encapsulate this setting as if you have requested timeout settings in your app; they will override the ones in testing.
You can find the release notes on the page below:
Let's complete our saga into MVC by developing the M, which stands for model. This layer is all your business logic, queries, external dependencies, etc. of your application, which represents the problem to solve or the domain to solve.
This layer is controlled by WireBox, the dependency injection framework within ColdBox, which will give you the flexibility of wiring your objects and persisting them for you.
Let's create a simple contact listing, so open up CommandBox and issue the following command:
This will create a models/ContactService.cfc
with a getAll()
method and a companion unit test at tests/specs/unit/ContactServiceTest.cfc
. Let's open the model object:
Notice the singleton
annotation on the component tag. This tells WireBox that this service should be cached for the entire application lifespan. If you remove the annotation, then the service will become a transient object, which means that it will be re-created every time it is requested.
Let's mock an array of contacts so we can display them later. We can move this to a SQL call later.
We also have created a project to mock any type of data: MockDataCFC. Just use CommandBox to install it: install mockdatacfc
You can then leverage it to mock your contacts or any simple/complex data requirement.
We have created our model, so let's tell our event handler about it. Let's create a new handler using CommandBox:
This will create the handler/contacts.cfc
handler with an index()
action, the views/contacts/index.cfm
view and the accompanying integration test tests/specs/integration/contactsTest.cfc
.
Let's open the handler and add a new ColdFusion property
that will have a reference to our model object.
Please note that inject
annotation on the property
definition. This tells WireBox what model to inject into the handler's variables
scope.
By convention, it looks in the models
folder for the value, which in our case, is ContactService
. Now let's call it and place some data in the private request collection prc
so our views can use it.
Now that we have put the array of contacts into the prc
struct as aContacts
, let's display it to the screen using ColdBox's HTML Helper.
The ColdBox HTML Helper is a companion class in all layouts and views that allows you to generate semantic HTML without the needed verbosity of nesting or binding to ORM/Business objects.
Please check out the API Docs to discover the HTML Helper: http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/modules/HTMLHelper/models/HTMLHelper.html
Open the contacts/index.cfm
and add the following to the view:
Note: If your models are singletons
, they will persist for the lifespan of your ColdFusion application. To see code changes for singletons, you have to reinit the framework by using the ?fwreinit={password}
Url action or via CommandBox using coldbox reinit
. Please check out the API Docs to discover CommandBox: [https://apidocs.ortussolutions.com/commandbox/current/index.html]
That's it! Execute the event: http://localhost:{port}/contacts/index
and view the nice table of contacts being presented to you.
Congratulations, you have made a complete MVC circle!
Tip You can find much more information about models and dependency injection in our full docs
Congratulations! Did you finish this guide in less than 60 minutes? If you did, please leave us some great feedback below. If you didn't, please tell us why; we would love to improve our guides.
You can now move on to the next level in becoming a ColdBox Guru! Choose your path below:
ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.
Get up and running with ColdBox easily.
Welcome to the world of ColdBox!
We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more.
Lucee 5+
Adobe 2018+
ColdBox has the following supported IDE Tools:
Note : However, you can use your own ColdFusion server setup as you see fit. We use CommandBox as everything is scriptable and fast!
No Java Runtime (80mb)
Embedded Runtime (120mb)
Once you download and expand CommandBox, you will have the box.exe
or box
binary, which you can place in your Windows Path or *Unix /usr/bin
folder to have it available system-wide. Then just open the binary and CommandBox will unpack itself your user's directory: {User}/.CommandBox
. This happens only once and the next thing you know, you are in the CommandBox interactive shell!
We can execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.
All examples in this book are based on having an interactive shell open.
The ColdBox CLI is your best friend when developing with ColdBox and it's based on CommadBox. Just fire up that terminal and install it
To update ColdBox from a previous version, just type update coldbox
. You can also run the
command outdated
periodically to verify the packages in your application.
Event handlers are the controller layer in ColdBox and is what you will be executing via the URL
or a FORM
post. All event handlers are singletons, which means they are cached for the duration of the application, so always remember to var scope your variables in your functions.
Tip: For development we highly encourage you to turn handler caching off or you will have to reinit the application in every request, which is annoying. Open the config/ColdBox.cfc
and look for the coldbox.handlerCaching
setting.
By default this is already done for you on the application templates.
Go open the handlers/main.cfc
and let's explore the code.
Let's recap: Every action in ColdBox receives three arguments:
rc
- A struct that contains both URL/FORM variables (unsafe data)
prc
- A secondary struct that is private only settable from within your application (safe data)
This line event.setView( "main/index" )
in the index
action told ColdBox to render a view back to the user found in views/main/index.cfm
.
ColdBox also has the concept of layouts, which are essentially reusable views that can wrap up other views or layouts. They allow you to reuse content to render views/layouts inside a specific location in the CFML content. By convention, ColdBox looks for a layout called layouts/Main.cfm.
This is yet another convention, the default layout. Your application can have many layouts or non-layouts at all.
Now, let's open the handler we created before called handlers/hello.cfc
and add some public and private variables so our views can render the variables.
Let's open the view now: views/hello/index.cfm
and change it to this:
If you execute the event now: http://localhost:{port}/hello/index
you will see a message of Hello nobody
.
Now change the incoming URL to this: http://localhost:{port}/hello/index?name=ColdBox
and you will see a message of Hello ColdBox
.
Now let's expect the name as part of the URL pattern; open the config/Router.cfc
and let's add another route:
We use the :
colon to denote incoming URL/FORM placeholder params that, if found are translated to the request collection (rc
). So let's try it out: http://localhost:{port}/hello/coldbox
You can use the route()
to generate the URL with params in a nice struct way:
The ColdBox HMVC Platform is the de-facto enterprise-level HMVC framework for CFML developers.
ColdBox has the following supported IDE Tools:
You should now be seeing a prompt that looks like this:
Now let's install the ColdBox CLI
Now you will have the coldbox
namespace of commands. Run coldbox help
and discover it.
Now we're cooking with gas! Let's create a new ColdBox application. CommandBox comes with built-in commands for scaffolding out new sites as well as installing ColdBox and other libraries. We'll start by changing into an empty directory where we want our new app to live. If necessary, you can create a new folder.
Now let's ask CommandBox to create a new ColdBox app for us.
You can also issue a coldbox create app help
command and get help for the creation command.
This command will place several new folders and files in your working directory. Let's run the ls
command to view them.
Here's a rundown of the important bits (Even though they might be more generated files/folders)
coldbox - This is the ColdBox framework managed by CommandBox
config/Coldbox.cfc - Your application configuration object
config/Router.cfc - Your application URL Router
handlers - Your controller layer, which in ColdBox they are called event handlers
layouts - Your HTML layouts
models - This holds your model CFCs
modules - This holds the CommandBox tracked modules
modules_app - This holds your app's modules
tests - Your test harness for unit and integration testing
views - Your HTML views will go here
Now that our shiny new MVC app is ready let's fire it up using the embedded server built into CommandBox. You don't need any other software installed on your PC for this to work. CommandBox has it all!
In a few seconds, a browser window will appear with your running application. This is a full server with access to the web administrator, where you can add data sources, and mappings, or adjust the server settings. Notice the handy icon added to your system tray as well. The --rewritesEnable
flag will turn on some basic URL rewriting so we have nice, pretty URLs.
ColdBox uses easy conventions to define the controllers and views in your app. Let's open up our main app controller in your default editor to have a looksie.
At the top, you'll see a function named index
. This represents the default action that runs for this controller, which in ColdBox land they are referred to as event handlers.
Now let's take a look in the main/index
view. It's located int he views
folder.
This line of code near the top of the view outputs the prc.welcomeMessage
variable we set in the controller.
Try changing the value being set in the handler and refresh your browser to see the change.
Let's define a new event handler now. Your controllers act as event handlers to respond to requests, REST API, or remote proxies.
Pull up CommandBox again and run this command.
That's it! You don't need to add any special configuration to declare your handler. Now we have a new handler called helloWorld
with actions index
, add
, edit
, and list
. The command also created a test case for our handler as well as stubbed-out views for each of the actions.
Let's hit this new controller we created with a URL like so. Your port number will probably be different.
127.0.0.1:43272/helloWorld
Normally the URL would have index.cfm
before the /helloWorld
bit, but our --rewritesEnable
flag when we started the server makes this nicer URL possible.
Here's some useful examples:
BCrypt -- Industry-standard password hashing
cbdebugger -- For debugging Coldbox apps
cbjavaloader - For interacting with Java classes and libraries
cbMarkdown - For writing in markdown
cbMessagebox -- Display nice error/success messages
cborm -- Awesome ORM Services
cb18n -- For multilingual sites
cbt - ColdBox templating language
cbValidation - Back-end validation framework
qb - Fluent query builder and schema builder
route-visualizer - For visualizing your application routes
Install cbmessagebox
from the CommandBox prompt like this:
We can see the full list of packages by using the list
command.
Right now we can see that our app depends on coldbox
and cbmessagebox
to run. We'll use our new cbmessagebox
module in a few minutes. But first, we'll create a simple Model CFC to round out our MVC
app.
Models encapsulate the business logic of your application. They can be services, entities, or DAOs. We'll use CommandBox to create a GreeterService
in our new app with a sayHello
method.
Tip: The --open
is a nice shortcut that opens our new model in our default editor after creating it.
Let's finish implementing the sayHello()
method by adding this return statement and save the file.
We can also add the word singleton
to the component declaration. This will tell WireBox to only create one instance of our service.
What is WireBox?
WireBox is a dependency injection framework that is included with ColdBox. It will manage all object creations, persistence and assembling. You don't have to worry about using new
or createobject()
for CFCs anymore.
Ok, let's open up that helloWorld
handler we created a while back. Remember, you can hit tab while typing to auto-complete your file names.
We'll inject our greeterService
and the cbmessagebox
service into the handler by adding these properties to the top of /handlers/helloWorld.cfc
.
What is this magical injection? Injection is a way to get references of other objects placed in the variables
scope of other objects. This makes your life easier as you don't have to manually create objects or even know where they exist.
This will put the instance of our services in the variables
scope where we can access it in our action methods.
And now, in our index
method, we'll set the output of our service into an info
message.
One final piece. Open up the default layout located in layouts/Main.cfm
and find the #view()#
. Add this line right before it to render out the message box that we set in our handler.
Here we leverage the cbMessagebox()
helper function which the MessageBox module collaborates to all layouts, views, handlers and interceptors.
Now hit your helloWorld
handler one final time with ?fwreinit=1
in the URL to see it all in action! (Again, your port number will most likely be different.
127.0.0.1:43272/helloWorld?fwreinit=1
Congratulations! In a matter of minutes, you have created a full MVC application. You installed a community module from ForgeBox, created a new handler/view, and tied in business logic from a service model.
As easy as that was, you're just scratching the surface of what ColdBox can do for you. Continue reading this book to learn more about:
Environment-specific configuration
Easy SES URL routing
Tons of 3rd party modules
Drop-in security system
Sweet REST web service support
ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.
Default: The default app template
Rest: A RESTFul services template
Rest-hmvc: A RESTFul service built with modules
SuperSimple : The bare-bones template
Vite: The default template using VITE for asset bundling
So let's create our first app using the default template skeleton:
Here are some of the major files and directory conventions you should know about:
Here are the major files you should know about:
Now let's start a server so we can see our application running:
This command will start a server with URL rewrites enabled, open a web browser for you, and execute the index.cfm
which in turn executes the default event by convention in a ColdBox application: main.index
. This is now our first runtime convention!
Tip: ColdBox Events map to handlers (cfc) and appropriate actions (functions)
Tip: The default event can also be changed in the configuration file: config/Coldbox.cfc
Hooray, we have scaffolded our first application, started a server, and executed the default event. Explore the application template generated, which contains useful information about your application.
Tip: Type coldbox create app help
to get help on all the options for creating ColdBox applications.
Let's open the handler and see the code, so open handlers/main.cfc
The action (function) we are interested in is the index()
function.
It sets a message in an incoming prc
argument and then calls a method in the incoming event
argument to set a view for rendering. We will discover these arguments in the next section. For now, we need to understand that handler actions are in place of traditional CFML pages. Depending on the incoming URL route, we execute the appropriate handler and action function.
There will be times when you make configuration or metadata/singleton code changes that are not reflected immediately in the application due to caching. You can tell the framework to reinit or restart the application for you via the URL by leveraging the special URL variable fwreinit
.
You can also use CommandBox to reinit the application:
The best way to get started with ColdBox
Here is a listing of the latest supported templates:
The coldbox create app
command has integration to our application templates via the skeleton
argument. This can be the name of each of the templates in our repositories or you can use the following alternatives:
A name of a ForgeBox entry: cbtemplate-advanced-script,cbtemplate-simple
A Github shortcut: github-username/repo
A folder containing a template: /opt/shared/templates/my-template
A zip file containing the template: /opt/shared/templates/my-template.zip
The ColdBox.cfc is the main applications' configuration object.
This CFC is instantiated by ColdBox and decorated at runtime so you can take advantage of some dependencies. Here is a table of the automatic injection this object has:
Once the application starts up, a reference to the instantiated configuration CFC will be stored in the configuration settings inside the ColdBox Main Controller (application.cbController
) with the key coldboxConfig
. You can then retrieve it later in your handlers, interceptors, modules, etc if you need to.
Note that the config CFC does not have the same variables mixed into it that a "normal" interceptor has. You can still access everything you need, but will need to get it from the controller
in the variables scope.
Learn how to configure ColdBox according to your needs beyond the conventions.
In this area we will learn how to configure ColdBox programmatically via the config/ColdBox.cfc
file. Most of the configurations in ColdBox are pre-set thanks to it's conventions over configuration approach. So the majority of settings are for fine-grained control, third-party modules and more.
ColdBox relies on conventions instead of configurations.
If you make changes to any of the main configuration files you will need to re-initialize your application for the settings to take effect.
You can also use CommandBox CLI to reinit your application if you are using its embedded server:
If you run into issues or have questions, please jump on our forum or our and ask away.
Please note that the supported CFML engines can change from major version to major version. Always verify them in the
Sublime -
VSCode -
The first step in our journey is to CommandBox. is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager, and Embedded Server. We will be using CommandBox for almost every exercise in this book, and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.
You can download CommandBox from the official site: and install in your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:
So make sure you choose your desired installation path and follow the instructions here:
Now you will have a coldbox
namespace of commands. Explore them coldbox help.
To install ColdBox, you can do so via install coldbox
or by scaffolding a starter application template. We would highly encourage you to visit our section to discover how to get started quickly.
Please remember to us in Github.
event
- An object that models and is used to work with the current request, called the .
Please note that we used the ColdFusion function encodeForHTML()
() on the public variable. Why? Because you can never trust the client and what they send, make sure you use the built-in ColdFusion encoding functions in order to avoid XSS hacks or worse on incoming public (rc
) variables.
Tip: Please see the section for in-depth information.
The ColdBox HMVC Platform is the de-facto enterprise-level HMVC framework for CFML developers. It's professionally backed, highly extensible, and productive. Getting started with ColdBox is quick and painless. The only thing you need to begin is , a command line tool for CFML developers. Please remember to us in Github.
This is a one-page introductory guide to ColdBox. If you are new to MVC or ColdBox, you can also leverage our as well.
The Ortus Community is the way to get any help for our entire platform and modules:
Sublime -
VSCode -
You can read through our one-page . Or simply grab the CommandBox executable from the and double click it to run.
Tip: You can find many scaffolding templates for ColdBox in our Github organization:
Tip: If you are creating an app to run on any server other than the CommandBox server, you will need to set up URL rewriting manually. More info here:
ColdBox's MVC is simple, but its true power comes from the wide selection of modules you can install into your app for additional functionality. You can checkout the full list of modules available on the FORGEBOX directory: .
If you run into issues or have questions, please jump on our and our and ask away.
The coldbox create app
command enables you to create application skeletons using one of our official skeletons or . Here are the names of the common ones you can find in our Github Organization:
Elixir : A based template to do asset compilation for you
You can find all our template skeletons here:
This will start up a 5 open-source CFML engine. If you would like an Adobe ColdFusion server then add to the command: cfengine=adobe@{version}
where {version}
can be: 2021,2018,2016.
Instead of executing pages like in a traditional application, we always execute the same page but distinguish the event we want via . When no mappings are present, we execute the default event by convention.
Tip: You can add a password to the reinit procedures for further security, please see the .
The best way to get started with ColdBox is with our application templates that you can find here: . We have a curated collection of starter templates to get you kicking the tires quickly. You will do so via the coldbox create app
command in the CLI:
An HTTP/S URL to a zip file containing a template:
The ColdBox configuration CFC is the heart of your ColdBox application. It contains the initialization variables for your application and extra information used by third-party modules and ultimately how your application boots up. In itself, it is also an event listener or , so it can listen to life-cycle events of your application.
Another cool concept for the Configuration CFC is that it is also registered as a once the application starts up automatically for you. You can create functions that will listen to application events by simply registering them by name:
Please note that anytime you make any configuration changes or there are things in memory you wish to clear out, you will be using a URL action that will tell the ColdBox to reinitialize the application. This special URL variable is called fwreinit
and can be any value or a specific password you setup in the .
.vscode
Mappings and build tasks for VSCode
build
Docker helpers
coldbox
The framework library
config
Configurations and module configurations
handlers
Event handler controllers
includes
i18n, JavaScript, helpers, CSS
interceptors
Event driven listeners go here
layouts
Application CFML layouts
lib
Java libraries or third party libraries
models
Model objects
modules
CommandBox driven dependencies
modules_app
Custom applicaiton modules
tests
Your application specs
views
Application CFML views
.cfconfig.json
Loads the CFML Engine settings
.cfformat.json
Formatting rules
.cflintrc
Linting rules
.env
Environment variables (Never commit)
.env.example
Example env file
Application.cfc
Your application bootstrap
box.json
Your CommandBox package descriptor
index.cfm
Front placeholder file
server.json
CommandBox server control
Default
default
The default ColdBox application template
Elixir
elixir
The default
template with ColdBox elixir support for asset pipelines
Modern (experimental)
modern
A fresh new approach to ColdBox applications that are non-root based. Still experimental
Rest
rest
A base REST API using ColdBox
Rest HMVC
rest-hmvc
An HMVC REST API using modules
Super Simple
supersimple
Barebones conventions baby!
Property
Description
appMapping
The ColdBox app mapping
coldboxVersion
The version of the framework
controller
The ColdBox running app controller
logBoxConfig
A reference to a LogBox configuration object
getJavaSystem()
Function to get access to the java system
getSystemSetting()
Retrieve a Java System property or env value by name. It looks at properties first then environment variables
getSystemProperty()
Retrieve a Java System property value by key
getEnv()
Retrieve a Java System environment value by name
webMapping
The application's web mapping
The CacheBox structure is based on the CacheBox declaration DSL, and it allows you to customize the caches in your application. Below are the main keys you can fill out, but we recommend you review the CacheBox documentation for further detail.
Info : We would recommend you create a
config/CacheBox.cfc
and put all your caching configuration there instead of in the main ColdBox configuration file. This will give you further portability and decoupling.
An absolute or relative path to the CacheBox configuration CFC or XML file to use instead of declaring the rest of the keys in this structure. So if you do not define a cacheBox structure, the framework will look for the default value: config/CacheBox.cfc
and it will load it if found. If not found, it will use the default CacheBox configuration found in /coldbox/system/web/config/CacheBox.cfc
A structure that enables scope registration of the CacheBox factory in either server, cluster, application or session scope.
The configuration of the default cache which will have an implicit name of default which is a reserved cache name. It also has a default provider of CacheBox which cannot be changed.
A structure where you can create more named caches for usage in your CacheBox factory.
Every time the framework renders a view, it will try to leverage the default layout located in layouts/Main.cfm
by convention. This is a reusable CFML template that gives format to your HTML output and contains the location of where the view you want should be rendered.
Tip : The request context can also be used to choose a different layout at runtime via the event.setLayout()
method or the layout
argument in the event.setView( layout: "login" )
method.
Tip : The request context can also be used to render a view with NO layout at all via the event.noLayout()
method or event.setView( noLayout: true )
The layout has everything you want to wrap views or other layouts with. You can use our rendering methods to do inline renderings or tell ColdBox where the set view should render:
view()
- Render the set view via event.setView()
view( name: "toolbar" )
- Render a named view
view( "partials/footer" )
- Render a explicit view
layout( name )
- Render another layout within this layout
The call to the view()
method with no arguments tells the framework to render the view that was set using event.setView()
. This is called a rendering region. You can use as many rendering regions within layouts or views.
Named Regions: The setView()
method even allows you to name these regions and then render them in any layout or other views using the name
argument.
Let's create a new simple layout with two rendering regions. Open up CommandBox and issue the following commands:
Open the layouts/Funky.cfm
layout and let's modify it a bit by adding the footer view as a rendering region.
Now let's do our footer:
As you can see from the footer, we introduced a new function called getSetting()
. All layouts, handlers, interceptors, and views inherit the Framework Super Type functionality. There are tons of methods inherited from this class that you can use in your application, from getting models to settings, relocating, async computations, and so much more.
Now, let's open the handler we created before, called handlers/hello.cfc
and add some code to use our new layout explicitly by adding a layout
argument to our setView()
call.
Go execute the event now: http://localhost:{port}/hello/index
and you will see the view rendered with the words funky layout
and footer view
at the bottom. Eureka, you have now created a layout.
You can also leverage the function event.setLayout( "Funky" )
to change layouts and even concatenate the calls:
event
.setView( "hello/index" )
.setLayout( "Funky" );
Discover the major conventions of the ColdBox framework
The core conventions delineate the contract between ColdBox and you for file/directory locations and more. Below is a table of the core conventions:
/config - Where configuration files are stored
/Coldbox.cfc - Your application configuration object (optional )
/CacheBox.cfc - Your application CacheBox configuration (optional )
/Router.cfc - Your application URL Router (optional )
/Scheduler.cfc - Your application global task scheduler (optional)
/WireBox.cfc - Your application WireBox Configuration (optional )
/handlers - This holds the app's event handlers (controller layer)
/includes - For public assets, helpers and i18n resources
/css - This can hold your CSS (optional)
/js - This can hold your JavaScript (optional)
/layouts - Your HTML layouts (view layer)
/models - This holds your app's CFCs (model layer)
/modules - This holds the CommandBox tracked modules
/modules_app - This holds your app's modules
/tests - Your test harness, including unit and integration testing
/specs - Where your test bundles go
/views - Your HTML views will go here (view layer)
ColdBox also has several execution conventions. This means that we have a convention or a default for the event, action, and layout to be used if you do not tell it what to use:
Convention
Default Value
Description
Default Event
main.index
The default event to execute when no event is specified
Default Action
index()
The default action to execute in an event handler controller if none is specified
Default Layout
layouts/Main.cfm
The default system layout to use
The configuration CFC has embedded environment control and detection built-in. Environments can be detected by:
regex matching against cgi.http_host
detection of an environmental variable called ENVIRONMENT ( Coldbox 5.2 and higher )
usage of a detectEnvironment()
function
The first option (regex matching) is the easiest to use, but not very reliable if you are using multiple hostnames or commandbox for re-initialization.
If you are using commandbox
please read ALL options below
To detect your environments you will setup a structure called environments
in your coldbox configuration with the named environments and their associated regular expressions for its cgi
host names to match for you automatically. If the framework matches the regex with the associated cgi.http_host
, it will set a setting called Environment
in your configuration settings and look for that environment setting name in your CFC as a method by convention. That's right, it will check if your CFC has a method with the same name as the environment and if it exists, it will call it for you. Here is where you basically override, remove, or add any settings according to your environment.
Warning : The environment detection occurs AFTER the
configure()
method is called. Therefore, whatever settings or configurations you have on theconfigure()
method will be stored first, treat those as Production settings.
The regex match will also create a global setting called "environment" which you can access and use like this:
In the above example, I declare a development key with a value list of regular expressions. If I am in a host that starts with cf2016, this will match and set the environment setting equal to development. It will then look for a development method in this CFC and execute it.
If you are using environmental variables for your different environments, you can specify an environmental variable called ENVIRONMENT and name it staging
, development
, testing
etcetera, depending on the required environment. As in the regex example, a function named after your environment (e.g. staging()
or development()
) will be called after your configure
method.
This method is more reliable than relying on cgi.http_host, since it will never change once configured correctly.
If you are NOT using environmental variables you can use your own detection algorithm instead of looking at the cgi.http_host
variable. You will NOT fill out an environments structure but actually create a method with the following signature:
This method will be executed for you at startup and it must return the name of the environment the application is on. You can check for any condition which distinguishes your environment from your other environments. As long as you return an environment name based on your own logic it will then store it and execute the method if it exists.
This element defines custom conventions for your application. By default, the framework has a default set of conventions that you need to adhere too. However, if you would like to implement your own conventions for a specific application, you can use this setting, otherwise do not declare it:
The ColdBox directive is where you configure the framework for operation.
Info : Please note that there are no mandatory settings as of ColdBox 4.2.0. If fact, you can remove the config file completely and your app will run. It will be impossible to reinit the app however without a reinit password set.
Protect the reinitialization of the framework URL actions. For security, if this setting is omitted, we will create a random password. Setting it to an empty string will allow you to reinitialize without a password. Always have a password set for public-facing sites.
The key used in FORM or URL to reinit the framework. The default is fwreinit
but you can change it to whatever you like.
Will scan the conventions directory for new handler CFCs on each request if activated. Use false for production, this is only a development true setting.
These settings map 1-1 from ColdBox events to the Application.cfc
life-cycle methods. The only one that is not is the defaultEvent
, which selects what event the framework will execute when no incoming event is detected via URL/FORM or REMOTE executions.
The ColdBox extension points are a great way to create federated applications that can reuse a centralized core instead of the local conventions. It is also a great way to extend some core classes with your own.
A list or array of absolute or relative paths to a UDF helper file. The framework will load all the methods found in this helper file globally. Meaning it will be injected in ALL handlers, layouts and views.
This is a location within your application or an absolute path to a cfm or bxm
template that will act as your global helper for all rendered views.
A list or array of locations of where ColdBox should look for modules to load into your application. The path can be a cf mapping or cfinclude
compatible location. Modules are searched and loaded in the order of the declared locations. The first location ColdBox will search for modules is the conventions folder modules
The CF include path of where to look for secondary views for your application. Secondary views look just like normal views except the framework looks in the conventions folder first and if not found then searches this location.
The CF include path of where to look for secondary layouts for your application. Secondary layouts look just like normal layouts except the framework looks in the conventions folder first and if not found then searches this location.
The CF dot notation path of where to look for secondary events for your application. Secondary events look just like normal events except the framework looks in the conventions folder first and if not found then searches this location.
The CF dot notation path of the CFC that will decorate the system Request Context object.
The CF dot notation path of the CFC that will decorate the system Controller
The event handler to call whenever ANY non-catched exception occurs anywhere in the request lifecycle execution. Before this event is fired, the framework will log the error and place the exception in the prc as prc.exception
.
The event handler to call whenever a route or event is accessed with an invalid HTTP method.
This is the event handler that will fire masking a non-existent event that gets requested. This is a great place to place 302 or 404 redirects whenever non-existent events are being requested.
The relative path from the application's root level of where the custom error template exists. This template receives a key in the private request collection called exception
that contains the exception. By default ColdBox does not show robust exceptions, you can turn on robust exceptions by choosing the following template:
ColdBox by convention can talk to, use and inject models from the models
folder by just using their name. On startup it will scan your entire models
folder and will register all the discovered models. This setting is true by default.
By default implicit views are case sensitive since ColdBox version 5.2.0, before this version the default was false.
This directive tells ColdBox that when events are executed they will be inspected for caching metadata. This does not mean that ALL events WILL be cached if this setting is turned on. It just activates the inspection mechanisms for whenever you annotate events for caching or using the runEvent()
caching methods.
This is useful to be set to false in development and true in production. This tells the framework to cache your event handler objects as singletons.
Allows you to use implicit views in your application and view dispatching. You can get a performance boost if you disable this setting.
This setting allows you to configure a lambda/closure that will return back the user's request identifier according to your own algorithms. This overrides the internal way ColdBox identifies requests incoming to the application which are used internally to track sessions, flash rams, etc.
The discovery algorithm we use is the following:
If we have an identifierProvider
closure/lambda/udf, then call it and use the return value
If we have sessions enabled, use the jessionId
or session URL Token
If we have cookies enabled, use the cfid/cftoken
If we have in the URL the cfid/cftoken
Create a request based tracking identifier: cbUserTrackingId
This is a boolean setting used when calling the ColdBox proxy's process()
method from a Flex or SOAP/REST call. If this setting is set to true, the proxy will return back to the remote call the entire request collection structure ALWAYS! If set to false, it will return, whatever the event handler returned back. Our best practice is to always have this false and return appropriate data back.
This directive tells ColdBox that when views are rendered, the cache=true
parameter will be obeyed. Turning on this setting will not cause any views to be cached unless you are also passing in the caching parameters to your view()
or event.setView()
calls.
The basic configuration object has 1 method for application configuration called configure()
where you will place all your configuration directives and settings:
Inside of this configuration method you will place several core and third-party configuration structures that can alter your application settings and behavior. Below are the core directives you can define:
Directive
Type
Description
struct
An optional structure used to configure CacheBox. If not setup the framework will use its default configuration found in /coldbox/system/web/config/CacheBox.cfc
struct
The main coldbox directives structure that holds all the coldbox settings.
struct
A structure where you will configure the application convention names
struct
A structure where you will configure environment detection patterns
struct
struct
An optional structure to configure application wide interceptor behavior
array
An optional array of interceptor declarations for your application
struct
A structure where you define how the layout manager behaves in your application
array
An array of layout declarations for implicit layout-view-folder pairings in your application
struct
An optional structure to configure the logging and messaging in your application via LogBox
struct
An optional structure to configure application wide module behavior
struct
An optional structure to configure individual modules installed in your application.
struct
A structure where you can put your own application settings
struct
An optional structure used to define how WireBox is loaded
This is an array of interceptor definitions that you will use to register in your application. The key about this array is that ORDER matters. The interceptors will fire in the order that you register them whenever their interception points are announced, so please watch out for this caveat. Each array element is a structure that describes what interceptor to register.
Warning : Important: Order of declaration matters! Also, when declaring multiple instances of the same CFC (interceptor), make sure you use the name attribute in order to distinguish them. If not, only one will be registered (the last one declared).
Info : If you do not define a logBox DSL structure, the framework will look for the default configuration file
config/LogBox.cfc
. If it does not find it, then it will use the framework's default logging settings.
ConfigFile
You can use a configuration CFC instead of inline configuration by using this setting. The default value is config/LogBox.cfc
, so by convention you can just use that location. If no values are defined or no config file exists, the default configuration file is coldbox/system/web/config/LogBox.cfc
.
A structure where you will configure the
The logBox structure is based on the LogBox declaration DSL, see the for much more information.
This structure allows you to define a system-wide default layout and view.
Hint Please remember that the default layout is
Main.cfm
This structure configures the interceptor service in your application.
throwOnInvalidStates
This tells the interceptor service to throw an exception if the state announced for interception is not valid or does not exist. Defaults to false.
customInterceptionPoints
This key is a comma delimited list or an array of custom interception points you will be registering for custom announcements in your application. This is the way to provide an observer-observable pattern to your applications.
Info Please see the Interceptors section for more information.
The layouts array element is used to define implicit associations between layouts and views/folders, this does not mean that you need to register ALL your layouts. This is a convenience for pairing them, we are in a conventions framework remember.
Before any renderings occur or lookups, the framework will check this array of associations to try and match in what layout a view should be rendered in. It is also used to create aliases for layouts so you can use aliases in your code instead of the real file name and locations.
The modules structure is used to configure the behavior of the ColdBox Modules.
Danger Please be very careful when using the
autoReload
flag as module routing can be impaired and thread consistency will also suffer. This is PURELY a development flag that you can use at your own risk.
This directive is how you will configure the Flash RAM for operation. Below are the configuration keys and their defaults:
This structure within config/Coldbox.cfc is used to house module configurations. Please refer to each module's documentation on how to create the configuration structures. Usually the keys will match the name of the module to be configured.
Starting in ColdBox 7, you can store module configurations as their own configuration file within the application’s config folder outside of the config/Coldbox.cfc. The naming convention is config/modules/{moduleName}.cfc
The configuration CFC will have one configure() method that is expected to return a struct of configuration settings as you did before in the moduleSettings
Just like a ModuleConfig this configuration override also gets many injections:
controller
coldboxVersion
appMapping
moduleMapping
modulePath
logBox
log
wirebox
binder
cachebox
getJavaSystem
getSystemSetting
getSystemProperty
getEnv
appRouter
router
This module configuration object will also inherit the ModuleConfig.cfc behavior that if you create methods with the same name as the environment you are on, it will execute it for you as well.
Then you can change the original struct as you see fit for that environment.
binder
The location of the WireBox configuration binder to use for the application. If empty, we will use the binder in the config
folder called by conventions: WireBox.cfc
singletonReload
A great flag for development. If enabled, WireBox will flush its singleton objects on every request so you can develop without any headaches of reloading.
Warning : This operation can cause some thread issues and it is only meant for development. Use at your own risk.
ColdBox makes it easy to access the configuration stored in your Java system properties and your server's environment variables, even if you don't know which one it is in! Three methods are provided for your convenience:
config/ColdBox.cfc
or a ModuleConfig.cfc
If you are inside config/ColdBox.cfc
or a ModuleConfig.cfc
or a config/WireBox.cfc
you can use the three system settings functions directly! No additional work required.
Application.cfc
If you would like to access these methods in your Application.cfc
, create an instance of coldbox.system.core.delegates.Env
and access them off of that component. This is required when adding a datasource from environment variables.
Example:
This configuration structure is used to configure the dependency injection framework embedded in ColdBox.
If you need to access these configuration values in other components, consider adding the values to your and injecting the values into your other components
getSystemSetting
( key, defaultValue )
Looks for key
in properties first, env second. Returns the defaultValue
if neither exist.
getSystemProperty
( key, defaultValue )
Returns the Java System property for key
. Returns the defaultValue
if it does not exist.
getEnv
( key, defaultValue )
Returns the server environment variable for key
. Returns the defaultValue
if it does not exist.
These are custom application settings that you can leverage in your application.
You can read our Using Settings section to discover how to use all the settings in your application.
ColdBox supports a Routing Service that will provide you with robust URL mappings for building expressive applications and RESTFul services. By convention URL routing will allow you to create URL's without using verbose parameter delimiters like ?event=this.that&m1=val
and execute ColdBox events.
If you are leveraging CommandBox as your server, then full URL rewrites are enabled by default. This means you do not need a web server to remove the index.cfm
from the URL.
A route is a declared URL pattern that if matched it will translate the URL into one of the following:
A ColdBox event to execute
A View/Layout to render
A Reponse function to execute
A Redirection to occur
It will also inspect the URL for placeholders and translate them into the incoming Request Collection variables (RC
).
Examples
There are several benefits that you will get by using our routing system:
Complete control of how URL's are built and managed
Ability to create or build URLs' dynamically
Technology hiding
Greater application portability
URL's are more descriptive and easier to remember
As you create route-heavy applications visualizing the routes will be challenging especially for HMVC apps with lots of modules. Just install our ColdBox Route Visualizer and you will be able to visually see, test and debug all your routing needs.
The ColdBox Routing DSL will be used to register routes for your application, which exists in your application or module router object. Routing takes place using several methods inside the router, which are divided into the following 3 categories:
Initiators - Starts a URL pattern registration, but does not fully register the route until a terminator is called (target).
Modifiers - Modifies the pattern with extra metdata to listen to from the incoming request.
Terminators - Finalizes the registration process usually by telling the router what happens when the route pattern is detected. This is refered to as the target.
Please note that order of declaration of the routes is imperative. Order matters.
Please remember to check out the latest API Docs for the latest methods and argument signatures.
The following methods are used to initiate a route registration process.
Please note that a route will not register unless a terminator is called or the inline target terminator is passed.
route( pattern, [target], [name=arguments.pattern] )
- Register a new route with optional target terminators and a name
get( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a GET http verb restriction
post( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a POST http verb restriction
put( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a PUT http verb restriction
patch( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a PATCH http verb restriction
options( pattern, [target], [name] )
- Register a new route with optional target terminators, a name and a OPTIONS http verb restriction
group( struct options, body )
- Group routes together with options that will be applied to all routes declared in the body
closure/lambda.
Modifiers will tell the routing service about certain restrictions, conditions or locations for the routing process. It will not register the route just yet.
header( name, value, overwrite=true )
- attach a response header if the route matches
headers( map, overwrite=true )
- attach multiple response headers if the route matches
as( name )
- Register the route as a named route
rc( name, value, overwrite=true )
- Add an RC
value if the route matched
rcAppend map, overwrite=true )
- Add multiple values to the RC
collection if the route matched
prc( name, value, overwrite=true )
- Add an PRC
value if the route matched
prcAppend map, overwrite=true )
- Add multiple values to the PRC
collection if the route matched
withHandler( handler )
- Map the route to execute a handler
withAction( action )
- Map the route to execute a single action or a struct that represents verbs and actions
withModule( module )
- Map the route to a module
withNamespace( namespace )
- Map the route to a namespace
withSSL()
- Force SSL
withCondition( condition )
- Apply a runtime closure/lambda enclosure
withDomain( domain )
- Map the route to a domain or subdomain
withVerbs( verbs )
- Restrict the route to listen to only these HTTP Verbs
packageResolver( toggle )
- Turn on/off convention for packages
valuePairTranslator( toggle )
- Turn on/off automatic name value pair translations
Terminators finalize the routing process by registering the route in the Router.
end()
- Register the route as it exists
toAction( action )
- Send the route to a specific action or RESTFul action struct
toView( view, layout, noLayout=false, viewModule, layoutModule )
- Send the route to a view/layout
toRedirect( target, statusCode=301 )
- Relocate the route to another event
to( event )
- Execute the event if the route matches
toHandler( handler )
- Execute the handler if the route matches
toResponse( body, statusCode=200, statusText="ok" )
- Inline response action
toModuleRouting( module )
- Send to the module router for evaluation
toNamespaceRouting( namespace )
- Send to the namespace router for evaluation
Here are just a few of those rewrite rules for you for major rewrite engines. You can spice them up as needed.
Recent versions of Apache don't send the CGI.PATH_INFO variable to your cfml engine when using ProxyPass and ProxyPassMatch without the PT
( Pass Through ) directive.
Updated syntax for Apache:
The following solution will also work if you are using recent versions of Apache and Coldbox:
Now you can create a pathInfo provider function in your router.cfc which brings back your path info to the router:
It contains the incoming FORM/REMOTE/URL variables the client sent in and the object lives in the ColdFusion request
scope and you will use to for responses and interacting with client data.
This object contains two structures internally:
RC
- The Request Collection which contains the FORM/REMOTE/URL data merged into a single structure. This is considered to be unsafe data as it comes from any request.
PRC
- The Private Request Collection which is a structure that can be used to safely store sensitive data. This structure cannot be modified from the outside world.
The order of preference of variables when merged is FORM first then REMOTE then URL.
You will use these objects in the controller and view layer of your application to get/set values, get metadata about the request, generate URLs, transform data for RESTful requests, and so much more. It is the glue that binds the controller and view layer together. As we progress in the guides, you will progress in mastering the request context.
Note that there is no model layer in the diagram. This is by design; the model will receive data from the handlers/interceptors directly.
Below you can see a listing of the most commonly used methods in the request context object. Please note that when interacting with a collection you usually have an equal private collection method.
buildLink() : Build a link in SES or non SES mode for you with tons of nice abstractions.
clearCollection() : Clears the entire collection
collectionAppend() : Append a collection overwriting or not
getCollection() : Get a reference to the collection
getEventName() : The event name in use in the application (e.g. do, event, fa)
getSelf() : Returns index.cfm?event=
getValue() : get a value
getTrimValue() : get a value trimmed
isProxyRequest() : flag if the request is an incoming proxy request
isSES() : flag if ses is turned on
isAjax() : Is this request ajax based or not
noRender(boolean) : flag that tells the framework to not render any html, just process and silently stop.
overrideEvent() : Override the event in the collection
paramValue(): param a value in the collection
removeValue() : remove a value
setValue() : set a value
setLayout() : Set the layout to use for this request
setView() : Used to set a view to render
valueExists() : Checks if a value exists in the collection.
renderData() : Marshall data to JSON, JSONP, XML, WDDX, PDF, HTML, etc.
Some Samples:
getCurrentAction()
: Get the current execution action (method)
getCurrentEvent()
: Get the current incoming event, full syntax.
getCurrentHandler()
: Get the handler or handler/package path.
getCurrentLayout()
: Get the current set layout for the view to render.
getCurrentView()
: Get the current set view
getCurrentModule()
: The name of the current executing module
getCurrentRoutedNamespace()
: The current routed URL mapping namespace if found.
getCurrentRouteRecord()
: Get the current routed record used in resolving the event
getCurrentRouteMeta()
: Get the current routed record metdata struct
getCurrentRoutedURL()
: The current routed URL if matched.
getDefaultLayout()
: Get the name of the default layout.
getDefaultView()
: Get the name of the default view.
The ColdBox Controller (stored in ColdFusion application
scope as application.cbController
) stores all your application settings and also your system settings:
ColdboxSettings
: Framework specific system settings
ConfigSettings
: Your application settings
You can use the following methods to retrieve/set/validate settings in your handlers/layouts/views and interceptors:
You can also get access to these methods in handlers via the ColdBox Controller component:
or using the application scope from modules and other locations where controller
isn't injected:
You can use the WireBox injection DSL to inject settings in your models or non-ColdBox objects. Below are the available DSL notations:
coldbox:setting:{key}
: Inject a specified config setting key
coldbox:coldboxSetting:{key}
: Inject a specified ColdBox setting key
coldbox:configSettings
: Inject a reference to the application settings structure
coldbox:coldboxSettings
: Inject a reference to the ColdBox System settings structure
On current versions of Coldbox, you can also use a function in your router.cfc or use the updated PassThrough configuration with a leading slash on the index.cfm below
Please note that URL rewriting is handled by an optional module in IIS. More info here:
On every request to a ColdBox event, the framework creates an object that models the incoming request. This object is called the Request Context Object. This object will be passed to an and will be processed by an and is by convention called an event)
Please visit the latest for further information about the request context.
REMOTE variables are from leveraging the
Please see the online for the latest methods and arguments.
Every router has a default route already defined for you in the application templates, which we refer to as routing by convention:
The URL pattern in the default route includes two special position placeholders, meaning that the handler and the action will come from the URL. Also note that the :action
has a question mark (?
), which makes the placeholder optional, meaning it can exist or not from the incoming URL.
:handler
- The handler to execute (It can include a Package and/or Module reference)
:action
- The action to relocate to (See the ?
, this means that the action is optional)
Behind the scenes the router creates two routes due to the optional placeholder in the following order:
route( "/:handler/:action" )
route( "/:handler)
Tip The :handler
parameter allows you to nest module names and handler names. Ex: /module/handler/action
If no action is passed the default action is index
This route can handle pretty much all your needs by convention:
Any extra name-value pairs in the remaining URL of a discovered URL pattern will be translated to variables in the request collection (rc
) for you automagically.
Tip: You can turn this feature off by using the valuePairTranslation( false )
modifier in the routing DSL on a route by route basis
route( "/pattern" )
.to( "users.show" )
.valuePairTranslation( false );
Routing is enabled by default in the ColdBox application templates in order to work with URL's like this:
http://localhost/index.cfm/home/about
As you can see they still contain the index.cfm
in the URL. In order to enable full URL rewrites that eliminates that index.cfm
you must have a rewrite enabled webserver like Apache, nginx or IIS or a Java rewrite filter which ships with CommandBox by default.
http://localhost/home/about
CommandBox has built in rewrites powered by Tuckey and you can enable a server with rewrites by running:
Caution Some J2EE servlet containers do not support the forwarding of SES parameters via the routing template (index.cfm
) out of the box. You might need to enable full URL rewriting either through a web server or a J2EE filter.
Apache mod_rewrite via .htaccess or configuration files (Free)
Helicon Tech ISAPI rewrite filter for IIS (Paid)
IIS 7 native rewrite filter (Free)
nginx native web server (free)
Tuckey J2EE rewrite filter (free)
In your URL pattern you can also use the :
syntax to denote a variable placeholder. These position holders are alphanumeric by default:
Once a URL is matched to the route above, the placeholders (:year/:month?/:day?
) will become request collection (RC
) variables:
Sometimes we will want to declare routes that are very similar in nature and since order matters, they need to be declared in the right order. Like this one:
However, we just wrote 4 routes for this approach when we can just use optional variables by using the ?
symbol at the end of the placeholder. This tells the processor to create the routes for you in the most detailed manner first instead of you doing it manually.
Caution Just remember that an optional placeholder cannot be followed by a non-optional one. It doesn't make sense.
ColdBox gives you also the ability to declare numerical only routes by appending -numeric
to the variable placeholder so the route will only match if the placeholder is numeric. Let's modify the route from above.
This route will only accept years, months and days as numbers.
ColdBox gives you also the ability to declare alpha only routes by appending -alpha
to the variable placeholder so the route will only match if the placeholder is alpha
only.
This route will only accept page names that are alpha only.
There are two ways to place a regex constraint on a placeholder, using the -regex:
placeholder or adding a constraints
structure to the route declaration.
You can also have the ability to declare a placeholder that must match a regular expression by using the -regex( {regex_here} )
placeholder.
The rc
variable format
must match the regex supplied: (xml|json)
You can also apply a structure of regular expressions to a route instead of inlining the regular expressions in the placeholder location. You will do this using the constraints()
method of the router.
The key in the structure must match the name of the placeholder and the value is a regex expression that must be enclosed by parenthesis ()
.
Apart from routing by convention, you can also register your own expressive routes. Let's investigate the routing approaches.
The route()
method allows you to register a pattern and immediately assign it to execute an event or a response via the target argument.
The first pattern registers and if matched it will execute the wiki.page event. The second pattern if matched it will execute the profile.show event from the users module and register the route with the userprofile name.
You can also pass in a closure or lambda to the target argument and it will be treated as an inline action:
You can also pass just an HTML string with {rc_var}
replacements for the routed variables place in the request collection
to
EventsIf you will not use the inline terminators you can do a full expressive route definition to events using the to()
method, which allows you to concatenate the route pattern with modifiers:
You can also route to a handler and an action using the modifiers instead of the to()
method. This long-form is usually done for visibility or dynamic writing of routes. You can use the following methods:
withHandler()
withAction()
toHandler()
end()
You can also route to views and view/layout combinations by using the toView()
terminator:
You can also use the toRedirect()
method to re-route patterns to other patterns.
The default status code for redirects are 301 redirects which are PERMANENT redirects.
You can also pass a closure as the target
of relocation. This closure will received the parsed parameters, the incoming route record and the event object. You can determine dynamically where the relocation will go.
This is great if you need to actually parse the incoming route and do a dynamic relocation.
Happy Redirecting!
You can also redirect a pattern to a handler using the toHandler()
method. This is usually done if you have the action coming in via the URL or you are using RESTFul actions.
You can also route a pattern to HTTP RESTFul actions. This means that you can split the routing pattern according to incoming HTTP Verb. You will use a modifier withAction()
and then assign it to a handler via the toHandler()
method.
The Router allows you to create inline responses via closures/lambdas or enhanced strings to incoming URL patterns. You do not need to create handler/actions, you can put the actions inline as responses.
If you use a response closure/lambda, they each accepts three arguments:
event
- An object that models and is used to work with the current request (Request Context)
rc
- A struct that contains both URL/FORM
variables merged together (unsafe data)
prc
- A secondary struct that is private only settable from within your application (safe data)
If the response is an HTML string, then you can do {rc_var}
replacements on the strings as well:
You can also register routes that will respond to sub-domains and even capture portions of the sub-domain for multi-tenant applications or SaaS applications. You will do this using the withDomain()
method.
You can leverage the full routing DSL as long as you add the withDomain()
call with the domain you want to bind the route to. Also note that the domain string can contain placeholders which will be translated to RC
variables for you if matched.
Tip: Please note that you can leverage Routing Groups as well for domains
You can also add variables to the RC and PRC structs on a per-route basis by leveraging the following methods:
rc( name, value, overwrite=true )
- Add an RC
value if the route matched
rcAppend map, overwrite=true )
- Add multiple values to the RC
collection if the route matched
prc( name, value, overwrite=true )
- Add an PRC
value if the route matched
prcAppend map, overwrite=true )
- Add multiple values to the PRC
collection if the route matched
This is a great way to manually set variables in the incoming structures:
You can also apply runtime conditions to a route in order for it to be matched. This means that if the route matches the URL pattern then we will execute a closure/lambda to make sure that it meets the runtime conditions. We will do this with the withCondition(
) method.
Let's say you only want to fire some routes if they are using Firefox, or a user is logged in, or whatever.
Every ColdBox application has a URL router and can be located by convention at config/Router.cfc
. This is called the application router and it is based on the router core class: coldbox.system.web.routing.Router
. Here is where you will configure router settings and define routes using our routing DSL.
Please see the latest API Docs for investigating all the methods and properties of the Router.
Tip: Unlike previous versions of ColdBox, the new routing services in ColdBox 5 are automatically configured to detect the base URLs and support multi-domain hosting. There is no more need to tell the Router about your base URL.
Router.cfc
The application router is a simple CFC that virtually inherits from the core ColdBox Router class and is configured via the configure()
method. It will be decorated with all the capabilities to work with any request much like any event handler or interceptor. In this router you will be doing 1 of 2 things:
Configuring the Router
Adding Routes via the Routing DSL
The router and indeed all module routers are also registered as full fledged ColdBox interceptors. So they can listen to any event within your application.
Once the routing service loads your Router it will create several application settings for you:
SesBaseUrl
: The multi-domain URL base URL of your application: http://localhost
SesBasePath
: The multi-domain path with no protocol or host
HtmlBaseUrl
: The same path as SESBaseURL
but without any index.cfm
in it (Just in case you are using index.cfm
rewrite). This is a setting used most likely by the HTML <base>
tag.
HtmlBasePath
: Does not include protocol or host
You can use the following methods to fine tune the configuration and operation of the routing services:
Method
Description
setEnabled( boolean )
Enable/Disable routing, enabled by default
setFullRewrites( boolean )
If true, then no index.cfm
will be used in the URLs. If false, then /index.cfm/ will be added to all generated URLs. Default is false.
setBaseURL( string )
setLooseMatching( boolean )
By default URL pattern matching starts at the beginning of the URL, however, you can choose loose matching so it searches anywhere in the URL. Default is false.
setMultiDomainDiscovery( boolean )
Defaults to true. With this setting on, every request will be inspected for the incoming host for usage in building links and domain detection.
setExtensionDetection( boolean )
By default ColdBox detects URL extensions like json, xml, html, pdf
which can allow you to build awesome RESTful web services. Default is true.
setValidExtensions( list )
Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf
setThrowOnInvalidExtensions( boolean )
By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.
The next sections will discus how to register routes for your application.
Resourceful routes are convention based to help you create routing with less boilerplate.
This single resource declaration will create all the necessary variations of URL patterns and HTTP Verbs to actions to handle the resource. Please see the table below with all the permutations it will create for you.
For in-depth usage of the resources()
method, let's investigate the API Signature:
We have created a scaffolding command in CommandBox to help you register and generate resourceful routes. Just run the following command in CommandBox to get all the help you will need in generating resources:
If you are building mostly API routes and not full HTML app routes, you can use the shortcut method apiResources()
method instead. This method will work the same as above BUT it will exclude the new
and edit
actions for you since we are in API Land.
Here is the full method signature:
The base URL to use for URL writing and relocations. This is automatically detected by ColdBox 5 e.g. , ''
In ColdBox, you can register resourceful routes (resources()
) to provide automatic mappings between HTTP verbs and URLs to event handlers and actions by convention. By convention, all resources map to a handler with the same name or they can be customized if needed. This allows for a standardized convention when building routed applications and less typing
GET
/photos
photos.index
Get all photos
GET
/photos/new
photos.new
Return the HTML form for creating a photo
POST
/photos
photos.create
Create a photo
GET
/photos/:id
photos.show
Show a photo by id
GET
/photos/:id/edit
photos.edit
Return the HTML form for editing the photo
PUT/PATCH
/photos/:id
photos.update
Update a photo by id
DELETE
/photos/:id
photos.delete
Delete a photo by id
GET
/photos
photos.index
Get all photos
POST
/photos
photos.create
Create a photo
GET
/photos/:id
photos.show
Show a photo by id
PUT/PATCH
/photos/:id
photos.update
Update a photo by id
DELETE
/photos/:id
photos.delete
Delete a photo by id
The Application.cfc
is one of the most important files in your application as it is where you define all the implicit ColdFusion engine events, session, client scopes, ORM, etc. It is also how you tell ColdFusion to bootstrap the ColdBox Platform for your application. There are two ways to bootstrap your application:
Leverage composition and bootstrap ColdBox (Default)
Leverage inheritance and bootstrap ColdBox
The composition approach allows you to have a more flexible configuration as it will allow you to use per-application mappings for the location of the ColdBox Platform.
Tip: To see the difference, just open the appropriate Application.cfc
in the application templates.
You can set some variables in the Application.cfc
that can alter Bootstrapping conditions:
Variable
Default
Description
COLDBOX_APP_ROOT_PATH
App Directory
Automatically set for you. This path tells the framework what is the base root location of your application and where it should start looking for all the agreed upon conventions. You usualy will never change this, but you can.
COLDBOX_APP_MAPPING
/
The application mapping is ESSENTIAL when dealing with Flex or Remote (SOAP) applications. This is the location of the application from the root of the web root. So if your app is at the root, leave this setting blank. If your application is embedded in a sub-folder like MyApp, then this setting will be auto-calculated to /MyApp
.
COLDBOX_CONFIG_FILE
config/ColdBox.cfc
The absolute or relative path to the configuration CFC file to load. This bypasses the conventions and uses the configuration file of your choice.
COLDBOX_APP_KEY
cbController
The name of the key the framework will store the application controller under in the application scope.
COLDBOX_FAIL_FAST
true
By default if an app is reiniting and a request hits it, we will fail fast with a message. This can be a boolean indicator or a closure.
The Boostrapper also leverages a default locking timeout of 30 seconds when doing loading operations. You can modify this timeout by calling the setLockTimeout()
method on the Bootsrapper object.
In your views, layouts and handlers you can use the buildLink
method provided by the request context object (event) to build routable links in your application.
Just pass in the routed URL or event and it will create the appropriate routed URL for you:
The queryString
argument can be a simple query string or a struct that represents the query variables to append.
Please note that the to
argument can be a simple route path, but it can also be a struct. This struct is for routing to named routes. Even though we recommend to use the route()
method instead.
The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:
getCurrentRouteName()
- Gives you the name of the current route, if any
getCurrentRoute()
- Gives you the currently executed route
getCurrentRoutedURL()
- Gives you the complete routed URL pattern that matched the route
getCurrentRoutedNamespace()
- Gives you the current routed namespace, if any
You can create a-la-carte namespaces for URL routes. Namespaces are cool groupings of routes according to a specific URL entry point. So you can say that all URLs that start with /testing
will be found in the testing namespace and it will iterate through the namespace routes until it matches one of them.
Much how modules work, where you have a module entry point, you can create virtual entry point to ANY route by namespacing it. This route can be a module a non-module, package, or whatever you like. You start off by registering the namespace using the addNamespace( pattern, namespace )
method or the fluent route().toNamespaceRouting()
method.
Once you declare the namespace you can use the grouping functionality to declare all the namespace routes or you can use a route().withNamespace()
combination.
Hint You can also register multiple URL patterns that point to the same namespace
There will be a time where your routes will become very verbose and you would like to group them into logical declarations. These groupings can also help you prefixes repetitive patterns in many routes with a single declarative construct. These needs are met with the group()
method in the router.
The best way to see how it works is by example:
As you can see from the routes above, we have lots of repetitive code that we can clean out. So let's look at the same routes but using some nice grouping action.
You can register routes in ColdBox with a human friendly name so you can reference them later for link generation and more.
You will do this in two forms:
Using the route()
method and the name
argument
Using the as()
method
If you do not pass the name
argument to the route()
method, we will use the pattern
as the name of the route.
You will generate URLs to named routes by leveraging the route()
method in the request context object (event).
Let's say you register the following named routes:
Then we can create routing URLs to them easily with the event.route()
method:
The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:
getCurrentRouteName()
- Gives you the name of the current route, if any
getCurrentRoute()
- Gives you the currently executed route
getCurrentRoutedURL()
- Gives you the complete routed URL pattern that matched the route
getCurrentRoutedNamespace()
- Gives you the current routed namespace, if any
The options struct can contain any values that you can use within the closure. Grouping can also be very nice when creating , which is our next section.
ColdBox allows you to detect incoming extensions from incoming paths automatically for you. This is great for building multi-type responses or to just create virtual extensions for events.
If an extension is detected in the incoming URL, ColdBox will grab it and store it in the request collection (RC
) as the variable format
. If there is no extension, then rc.format
will not be stored and thus will not exist.
You can configure the extension detection using the following configuration methods:
Method
Description
setExtensionDetection( boolean )
By default ColdBox detects URL extensions like json, xml, html, pdf
which can allow you to build awesome RESTful web services. Default is true.
setValidExtensions( list )
Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf
setThrowOnInvalidExtensions( boolean )
By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.
Please note that if you have set to throw exceptions if an invalid extension is detected then a 406 exception will be thrown.
The base
tag in HTML allows you to tell the browser what is the base URL for assets in your application. This is something that is always missed when using frameworks that enable routing.
base
tag defines the base location for links on a page. Relative links within a document (such as <a href="someplace.html"... or <img src="someimage.jpg"... ) will become relative to the URI specified in the base tag. The base tag must go inside the head element.
We definitely recommend using this HTML tag reference as it will simplify your asset retrievals.
Caution If you do not use this tag, then every asset reference must be an absolute URL reference.
Although we have access to all the HTTP verbs, modern browsers still only support GET and POST. With ColdBox and HTTP Method Spoofing, you can take advantage of all the HTTP verbs in your web forms.
By convention, ColdBox will look for an _method
field in the FORM scope. If one exists, the value of this field is used as the HTTP method instead of the method from the execution. For instance, the following block of code would execute with the DELETE action instead of the POST action:
You can manually add these _method
fields yourselves, or you can take advantage of ColdBox's HTML Helper startForm()
method. Just pass the method you like, we will take care of the rest:
Event handlers are ColdBox's version of controllers in the MVC design pattern. So every time you hear "event handler", you are talking about a controller that can listen to external events or internal events in ColdBox. Event handlers are responsible for controlling your application flow, calling business logic, preparing a display to a user and much more.
All your handlers will be stored in the handlers folder of your application template. If you get to the point where your application needs even more decoupling and separation, please consider building ColdBox Modules instead.
Tip: You can create packages or sub-folders inside of the handlers directory. This is encouraged on large applications so you can organize or package handlers logically to facilitate better maintenance and URL experience.
You can also declare a HandlersExternalLocation
directive in your Configuration CFC. This will be a dot notation path or instantiation path where more external event handlers can be found.
If an external event handler has the same name as an internal conventions event, the internal conventions event will take precedence.
By default, ColdBox will only scan for event handlers on startup. For development we highly encourage you leverage the following configuration directives:
Event handlers are CFCs that will respond to FORM posts, HTTP requests and/or remote requests (like Flex,Air, SOAP, REST) via an incoming RC variable called event or by URL mappings (Which we saw in the previous section).
You can also remove the inheritance from the CFC and WireBox will extend the coldbox.system.EventHandler
for you using Virtual Inheritance.
Event Handlers are treated as singletons by ColdBox, so make sure you make them thread-safe and properly scoped. Persistence is controlled by the coldbox.handlerCaching
directive
They are composed of functions which are called actions that will always have the following signature:
Each action receives three arguments:
event
- An object that models and is used to work with the current request (otherwise known as the Request Context)
rc
- A struct that contains both URL/FORM
variables (unsafe data)
prc
- A secondary struct that is private. This structure is only accessible from within your application (safe data)
An action will usually do one of the following:
Set a view/layout to render
Return HTML
Return Complex Data which is converted to JSON by default
Relocate to another event/URL Route
The default action for all event handlers is called index()
. This means that when you execute an event, you can omit the index if you so desire.
So what about private
functions? Private functions are not executable from the outside world, but can be executed internally via a function available to all handlers called runEvent()
, which we will explore later.
It is important to note that there is a pre-defined object model behind every event handler controller that will enable you to do your work more efficiently. The following are the automatically generated properties every event handler makes available in their variables
scope:)
cachebox : A reference to the CacheBox library (coldbox.system.cache.CacheFactory
)
controller : A reference to the Application Controller (coldbox.system.web.Controller
)
flash: A flash memory object (coldbox.system.web.flash.AbstractFlashScope
)
logbox: A reference to the application LogBox (coldbox.system.logging.LogBox
)
log: A pre-configured logging logger object (coldbox.system.logging.Logger
)
wirebox : A reference to the application WireBox Injector (coldbox.system.ioc.Injector
)
$super: A reference to the virtual super class if using non-inheritance approach.
By default, the URL mapping processor will detect routes by looking at the CGI.PATH_INFO
variable, but you can override this and provide your own function. This feature can be useful to set flags for each request based on a URL and then clean or parse the URL to a more generic form to allow for simple route declarations. Uses may include internationalization (i18n) and supporting multiple experiences based on devices such as Desktop, Tablet, Mobile and TV.
To modify the URI used by the Routing Services before route detection occurs simply follow the convention of adding a function called pathInfoProvider()
to your application Router (config/Router.cfc
).
The pathInfoProvider()
function is responsible for returning the string used to match a route.
The Rewrite rules section has another useful example for a pathInfo provider
We all need values in our applications. That is why we interact with the request context in order to place data from our model layer into it so our views can display it, or to retrieve data from a user's request. You will either interact with the event object to get/set values or put/read values directly via the received rc
and prc
references.
We would recommend you use the private request collection (prc
) for setting manual data and using the standard request collection (rc
) for reading the user's unsafe request variables. This way a clear distinction can be made on what was sent from the user and what was set by your code.
Important The most important paradigm shift from procedural to an MVC framework is that you NO LONGER will be talking to URL, FORM, REQUEST or any ColdFusion scope from within your handlers, layouts, and views. The request collection already has URL, FORM, and REQUEST scope capabilities, so leverage it.
Events are determined via a special variable that can be sent in via the FORM, URL, or REMOTELY called event
. If no event is detected as an incoming variable, the framework will look in the configuration directives for the DefaultEvent
and use that instead. If you did not set a DefaultEvent
setting then the framework will use the following convention for you: main.index
In order to call them you will use the following event syntax notation format:
no event : Default event by convention is main.index
event={handler} : Default action method by convention is index()
event={handler}.{action} : Explicit handler + action method
event={package}.{handler}.{action} : Packaged notation
This looks very similar to a Java or CFC method call, example: String.getLength(),
but without the parentheses. Once the event variable is set and detected by the framework, the framework will tokenize the event string to retrieve the CFC and action call to validate it against the internal registry of registered events. It then continues to instantiate the event handler CFC or retrieve it from cache, finally executing the event handler's action method.
Examples
Hint : You can even change the event
variable name by updating the EventName
setting in your coldbox
.
Please note that ColdBox supports both normal variable routing and , usually referred to as pretty URLs.
event={module}:{package}.{handler}.{action} : Module Notation (See )
The event
object is the object that will let you set the views that you want to render, so please explore its API in the CFC Docs. To quickly set a view to render, do the following:
The view name is the name of the template in the views directory without appending the .cfm. If the view is inside another directory, you would do this:
The views you set will use the default layout defined in your configuration file which by default is the layouts/Main.cfm
We recommend that you set your views following the naming convention of your event. If your event is users.index, your view should be users/index. This will go a long way with maintainability and consistency and also will activate implicit views where you don't even have to use the set view method call.
You can also use the setView(), setLayout()
methods to tell the framework which view and layout combination to use:
You can also tell the framework to set a view for rendering by itself with no layout using the noLayout
argument
Here are the arguments for the setView()
method:
You can leverage the caching arguments in the setView()
method in order to render and cache the output of the views once the framework renders it. These cached views will be stored in the template cache region, which you can retrieve or purge by talking to it: getCache( 'template' )
.
Data can be passed from your handler to the view via rc or prc. If you want to pass data to a view without polluting rc and prc, you can pass it directly via the args parameter, much like a method call.
Access the data in the view like so:
If you don't want to, you don't have to. The framework gives you a method in the event object that you can use if this specific request should just terminate gracefully and not render anything at all. All you need to do is use the event object to call on the noRender()
method and it will present to the user a lovely white page of death.
Handler actions can return data back to its callers in many different formats. Either to create RESTFul services, or just send data that's not HTML back to the user. The different usages can be:
Complex Data
HTML
Rendered Data via event.renderData()
The last option on the list is to support native REST Applications in ColdBox by leveraging the REST Handler.
By default, any complex data returned from handler actions will automatically be marshaled to JSON:
Simple as that. ColdBox detects the complex object and tries to convert it to JSON for you automatically.
renderdata
Action AnnotationIf you want ColdBox to marshall the content to another type like XML or PDF. Then you can use the renderdata
annotation on the action itself. The renderdata
annotation can be any of the following values:
json
jsonp
jsont
xml
html
text
renderdata
Component AnnotationYou can also add the renderData annotation to the component definition and this will override the default of JSON. So if you want XML as the default, you can do this:
If the returned complex data is an object and it contains a function called $renderData(),
then ColdBox will call it for you automatically. So instead of marshaling to JSON automatically, your object decides how to marshal itself.
By default if your handlers return simple values, then they will be treated as returning HTML.
Using the renderdata()
method of the event object is the most flexible for RESTFul web services or pure data marshaling. Out of the box ColdBox can marshall data (structs, queries, arrays, complex or even ORM entities) into the following output formats:
XML
JSON
JSONP
JSONT
HTML
TEXT
WDDX
CUSTOM
Here is the method signature:
Below are a few simple examples:
As you can see, it is very easy to render data back to the browser or caller. You can even choose plain and send HTML back if you wanted to.
You can also render out PDFs from ColdBox using the render data method. The data argument can be either the full binary of the PDF or simple values to be rendered out as a PDF; like views, layouts, strings, etc.
There is also a pdfArgs
argument in the render data method that can take in a structure of name-value pairs that will be used in the cfdocument
(See docs) tag when generating the PDF. This is a great way to pass in arguments to really control the way PDF's are generated uniformly.
The renderData()
method also has two powerful arguments: formats & formatsView
. If you currently have code like this:
Where you need to param the incoming format extension, then do a switch and do some code for marshalling data into several formats. Well, no more, you can use our formats argument and ColdBox will marshall and code all that nasty stuff for you:
That's it! ColdBox will figure out how to deal with all the passed in formats for you that renderdata
can use. By convention it will use the name of the incoming event as the view that will be rendered for HTML and PDF; implicit views. If the event was users.list, then the view would be views/users/list.cfm. However, you can tell us which view you like if it is named different:
If you need to redirect for html events, you can pass any arguments you normally would pass to setNextEvent
to formatsRedirect
.
You can do custom data conversion by convention when marshalling CFCs. If you pass in a CFC as the data
argument and that CFC has a method called $renderdata()
, then the marshalling utility will call that function for you instead of using the internal marshalling utilities. You can pass in the custom content type for encoding as well:
The CFC converter:
In this approach your $renderdata()
function can be much more customizable than our internal serializers. Just remember to use the right contentType argument so the browser knows what to do with it.
The framework provides you with the relocate()
method that you can use to relocate to other events thanks to the framework super type object, the grand daddy of all things ColdBox.
After calling relocate, further code will still be processed until you return from handler.
Please see the Super Type CFC Docs for further investigation of all the goodness of methods you have available.
It is extremely important that you use this method when relocating instead of the native ColdFusion methods as it allows you to gracefully relocate to other events or external URIs. By graceful, we mean it does a lot more behind the scenes like making sure the flash scope is persisted, logging, post processing interceptions can occur and safe relocations.
So always remember that you relocate via relocate()
and if I asked you: "Where in the world does event handlers get this method from?", you need to answer: "From the super typed inheritance".
We all need to deliver files to users at one point in time. ColdBox makes it easy to deliver any type of file even binary files via the Request Context's (event) sendFile()
method.
The API Docs can help you see the entire format of the method: https://apidocs.ortussolutions.com/coldbox/6.6.1/coldbox/system/web/context/RequestContext.html#sendFile()
The method signature is as follows:
Please note that the file
argument can be an absolute path or an actual binary file to stream out.
With this interceptor you can intercept local event actions and execute things before the requested action executes. You can do it globally by using the preHandler()
method or targeted to a specific action pre{actionName}()
.
The arguments received by these interceptors are:
event
: The request context reference
action
: The action name that was intercepted by preHandler()
eventArguments
: The struct of extra arguments sent to an action if executed via runEvent()
rc
: The RC reference
prc
: The PRC Reference
Here are a few options for altering the default event execution:
Use event.overrideEvent('myHandler.myAction')
to execute a different event than the default.
Use event.noExecution()
to halt execution of the current event. ONLY works when executed by interceptions before the main event. It will never work in pre/post advices.
See the RequestContext documentation for more details.
You can fine tune these interception methods by leveraging two public properties in the handler:
this.prehandler_only
: A list of actions that preHandler()
will ONLY fire on
this.prehandler_except
: A list of actions that preHandler()
will NOT fire on
There are several simple implicit AOP (Aspect Oriented Programming) interceptor methods, usually referred to as advices, that can be declared in your event handler that the framework will use in order to execute them before/after and around an event as its fired from the current handler.
This is great for intercepting calls, pre/post processing, localized security, logging, RESTful conventions, and much more. Yes, you got that right, Aspect Oriented Programming just for you and without all the complicated setup involved! If you declared them, the framework will execute them.
Interceptor Method
Description
preHandler()
Executes before any requested action (In the same handler CFC)
pre{action}()
Executes before the {action}
requested ONLY
postHandler()
Executes after any requested action (In the same handler CFC)
post{action}()
Executes after the {action}
requested ONLY
aroundHandler()
Executes around any request action (In the same handler CFC)
around{action}()
Executes around the {action}
requested ONLY
With this interceptor you can intercept local event actions and execute things after the requested action executes. You can do it globally by using the postHandler()
method or targeted to a specific action post{actionName}()
.
The arguments received by these interceptors are:
event
: The request context reference
action
: The action name that was intercepted by postHandler()
eventArguments
: The struct of extra arguments sent to an action if executed via runEvent()
rc
: The RC reference
prc
: The PRC Reference
You can fine tune these interception methods by leveraging two public properties in the handler:
this.posthandler_only
: A list of actions that the postHandler()
action will fire ONLY!
this.posthandler_except
: A list of actions that the postHandler()
action will NOT fire on
Around advices are the most powerful of all as you completely hijack the requested action with your own action that looks, smells and feels exactly as the requested action. This is usually referred to as a proxy design pattern.
This will allow you to run both before and after advices but also surround the method call with whatever logic you want like transactions
, try/catch
blocks, locks
or even decide to NOT execute the action at all.
You can do it globally by using the aroundHandler()
method or targeted to a specific action around{actionName}()
.
Examples
The arguments received by these interceptors are:
event
: The request context reference
targetAction
: The function pointer to the action that got the around interception. It will be your job to execute it (Look at samples)
eventArguments
: The struct of extra arguments sent to an action if any
rc
: The RC reference
prc
: The PRC Reference
You can fine tune these interception methods by leveraging two public properties in the handler:
this.aroundhandler_only
: A list of actions that the aroundHandler()
action will fire ONLY!
this.aroundhandler_except
: A list of actions that the aroundHandler()
action will NOT fire on
Easily bind incoming data into your models.
ColdBox allows you to populate data from FORM/URL/REMOTE/XML/JSON/Struct
into your model objects by convention. This is done via WireBox's object population capabilities.
The super type has a method called populate
which you will use to trigger the data binding. The supported incoming data sources are:
request collection RC
structs
json
xml
query
The method will try to match the incoming variables names in the data packet to a setter
or property
in the target model object. If there is a match, then it will inject that data into the model or call it's setter
for you.
Let's explore the API
Let's do a quick example. Here is a Person.cfc
that has two properties with automatic getters/setters.
Person.cfc
editor.cfm
Here is an editor to submit a form to create the person.
Event Handler -> person.cfc
Here is an event handler to do the saving
Run the code, and you will see that the populator matched the incoming variables into the model and thus binding it.
The populator can also bind ORM objects natively. However, it can also build out relationships via the composeRelationships
argument. This allows you to create the objects from simple identifiers for the following relationships:
one-to-one
: The id of the relationship
many-to-one
: The id of the parent relationship
one-to-many
: A list of identifiers to link to the target object which ends up being an array of objects
many-to-many
: A list of identifiers to link to the target object which ends up being an array of objects
So if we have a User
object with a many-to-one
of Role
and a many-to-many
of Permissions
, we could populate the User
with all of it's data graph:
This packet has the two relationships role
and permissions
as an incoming list. The list can be a simple string or an actual JSON array. To populate we can do this:
That's it. ColdBox will take care of matching and building out the relationships.
You can also use the nullEmptyInclude
and nullEmptyExclude
properties to include and exclude null
value population.
You can also use ignoreEmpty
to ignore all the empty values on population so the ORM treats them as null
this.population
ColdBox also supports the ability for target objects to dictate how they will be populated. This convention allows for objects to encapsulate the way the mass population of data is treated. This way, you don’t have to scrub or pass include excludes lists via population arguments; it can all be nicely encapsulated in the targeted objects. Just declare a this.population
in your object's pseudo constructor:
The populator will look for a this.population
struct with the following keys:
include
: an array of property names to allow population ONLY
exclude
: an array of property names to NEVER allow population
The population methods also have an argument called: ignoreTargetLists
which defaults to false
, meaning it inspects the objects for these population markers. If you pass it as true
then the markers will be ignored. This is great for the population of objects from queries or an array of structs or mementos that YOU have control of. Great in DAOs or service layers.`
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.
More often you will find that certain web operations need to be restricted in terms of what HTTP verb is used to access a resource. For example, you do not want form submissions to be done via GET but via POST or PUT operations. HTTP Verb recognition is also essential when building strong RESTFul APIs when security is needed as well.
You can do this manually, but why do the extra coding :)
This solution is great and works, but it is not THAT great. We can do better.
Another feature property on an event handler is called this.allowedMethods
. It is a declarative structure that you can use to determine what the allowed HTTP methods are for any action on the event handler.
If the request action HTTP method is not found in the approved list, it will look for a onInvalidHTTPMethod()
on the handler and call it if found. Otherwise ColdBox throws a 405 exception that is uniform across requests.
You can listen for global invalid HTTP methods using the coldbox.onInvalidHTTPMethodHandler
located in your config/ColdBox.cfc.
If the action is not listed in the structure, then it means that we allow all HTTP methods. Just remember to either use the onError()
or onInvalidHTTPMethod()
method conventions or an exception handler to deal with the security exceptions.
You can tag your event actions with a allowedMethods
annotation and add a list of the allowed HTTP verbs as well. This gives you a nice directed ability right at the function level instead of a property. It is also useful when leveraging DocBox documentation as it will show up in the API Docs that are generated.
Every event handler controller has some implicit methods that if you create them, they come alive. Just like the implicit methods in Application.cfc
With this convention you can create virtual events that do not even need to be created or exist in a handler. Every time an event requests an action from an event handler and that action does not exist in the handler, the framework will check if an onMissingAction()
method has been declared. If it has, it will execute it. This is very similar to ColdFusion's onMissingMethod()
but on an event-driven framework.
This event has an extra argument: missingAction which is the missing action that was requested. You can then do any kind of logic against this missing action and decide to do internal processing, error handling or anything you like. The power of this convention method is extraordinary, you have tons of possibilities as you can create virtual events on specific event handlers.
This is a localized error handler for your event handler. If any type of runtime error occurs in an event handler and this method exists, then the framework will call your method so you can process the error first. If the method does not exist, then normal error procedures ensue.
Please note that compile time errors will not fire this method, only runtime exceptions.
This method will be called for you if a request is trying to execute an action in your handler without the proper approved HTTP Verb. It will then be your job to determine what to do next:
A part from using runEvent()
to execute events, you can also abstract it by using the runRoute()
method. This method is fairly similar but with the added benefit of executing a NAMED route instead of the direct event it represents. This gives you the added flexibility of abstracting the direct event and leveraging the named route.
All the same feature of runEvent()
apply to runRoute()
Just like you can create links based on named routes and params, you can execute named routes and params as well internally via runRoute()
The params argument you pass to the runRoute() method will be translated into event arguments. Therefore they will be passed as arguments to the event the route represents:
In the example above, the userData
named route points to the user.data
event.
If you want to execute module routes, no problem! Just use our @ or :
notation to tell the controller from which module's router we should pick the route from.
A viewlet is a self sufficient view or a widget that can live on its own, its data is pre-fetched and can just be renderer anywhere in your system.
What in the world is this? Well, imagine a portal, in which each section of the portal is self-sufficient, with controls and data. You don't want to call all the handlers for this data for every single piece of content. It's not efficient, you need to create a separation. Well, a viewlet is such a separation that provides you with the ability to create reusable events. So how do we achieve this?
You will use the method runEvent()
anywhere you want a viewlet to be displayed or the content rendered. This calls an internal event that will be in charge to prepare and render the viewlet.
Create the portable event but make sure it returns the produced content.
This code just renders out the results of a runEvent()
method call. Please note that you can pass in arguments to the event using the eventArguments
argument. This makes the event act like a method call with arguments to it. Remember that all events you call via runEvent()
will share the same RC/PRC.
As you can see from the code above, the handler signature can accept arguments which are passed via the eventArguments
structure. It talks to a service layer and place some data on the private request collection the viewlet will use. It then returns the results of a view()
call that will render out the exact viewlet I want. You can be more creative and do things like:
render a layout + view combo
render data
return your own custom strings
etc
Caution We would suggest you namespace or prefix your private request collection variables for viewlets in order to avoid collisions from multiple viewlet events in the same execution thread or instead pass the necessary arguments into a view via the args
argument.
The view is a normal standard view, it doesn't even know it is a viewlet, remember, views are DUMB!
A content variable is a variable that contains HTML/XML or any kind of visual content that can easily be rendered anywhere. So instead of running the viewlet event in the view, you can abstract it to the controller layer and assign the output to a content variable:
So how do I render it?
Another example, is what if we do not know if the content variable will actually exist? How can we do this? Well, we use the event object for this and its magic getValue() method.
So now, if no content variable exists, an empty string will be rendered.
Important String manipulation in Java relies on immutable structures, so performance penalties might ensue. If you will be doing a lot of string manipulation, concatenation or rendering, try to leverage native java objects: StringBuilder or StringBuffer
I would suggest you look at to discover all arguments to the runEvent()
method call.
Apart from executing events from the URL/FORM or Remote interfaces, you can also execute events internally, either public or private from within your event handlers or from interceptors, other handlers, layouts or views.
You do this by using the runEvent()
method which is inherited from our FrameworkSuperType
class. Here is the method signature:
The interesting aspect of internal executions is that all the same rules apply, so your handlers can return content like widgets, views, or even data. Also, the eventArguments enables you to pass arguments to the method just like method calls:
As you can see from the function signature you can tell ColdBox to cache the result of the event call. All of the cached content will go into the template cache by default unless you use the cacheProvider
argument. The cache keys are also based on the name of the event and the signature of the eventArguments
structure. Meaning, the framework can cache multiple permutations of the same event call as long as the eventArguments
are different.
Tip: You can disable event caching by using the coldbox.eventCaching
directive in your config/ColdBox.cfc
ColdBox Core MVC does not have validation built-in but it is implemented via the official core cbValidation
module. You can easily install the module in your application via:
You can find much more information about this module in the following resources:
Documentation: https://coldbox-validation.ortusbooks.com
ForgeBox : http://forgebox.io/view/cbvalidation
Views are HTML content that can be rendered inside a layout or by themselves. They can be rendered on-demand or set by an event handler. Views can also produce content apart from HTML, like JSON/XML/WDDX, via our view renderer, which we will discover. So get ready for some rendering goodness!
setView()
Event handlers are usually the ones responsible for setting views for rendering. However, it's important to understand that ANY object with access to the request context object can also perform this task. This knowledge will keep you informed about the rendering process. The setView()
method in the request context object is the tool that enables this functionality.
Setting a view does not mean that it gets rendered immediately. This means that it is deposited in the context of the request. Later on in the execution process, the framework will pick those variables up and do the actual rendering. To do immediate rendering, you will use the inline rendering methods described later.
We use the setView()
method to set the view views/general/index.cfm
to be rendered. The cool thing about this is that we can override the view to be rendered anytime during the request flow. So, the last process to execute the setView()
method is the one that counts. Also, notice a few things:
No extension is needed.
You can traverse directories by using /
like normal cfinclude
notation.
The view can exist in the conventions directory views
or your configured external locations
You did not specify a layout for the view, so the application's default layout (Main
) will be used.
It is best practice that view locations should simulate the event. So, if the event is general.index, there should be a general folder in the root views folder with a view called index.cfm/bxm
Let's look at the view code:
I am using our cool HTML Helper class, which is smart enough to render tables, data, HTML 5 elements, etc., and even bind to ColdFusion ORM entities.
So what happens if I DO NOT want the view rendered within a layout? Am I doomed? Of course not, use the same method with the noLayout
argument or event.noLayout()
method:
If you need the view to be rendered in a specific layout, then use the layout
argument or the setLayout()
method:
If you need the set a view to be rendered from a specific ColdBox Module then use the module
argument alongside any other argument combination:
The setView()
method also has a name
argument, which you can use to denote a rendering region. This will not affect the main set view rendered by the framework. This will set up the arguments to render the view, and then YOU will use the #view( name : "myRegion" )#
code to render it wherever you like. The purpose of this method is to encapsulate rendering mechanics from views and let the handlers control it.
Now that you have set the named region, you can evaluate it and render it it using the view()
method
You can also tell the renderer not to render anything back to the user by using the event.noRender()
method. Maybe you just took some input and need to gracefully shut down the request into the infamous white screen of death.
You can also omit the explicit event.setView()
if you want, ColdBox will then look for the view according to the executing event's syntax by convention. So if the incoming event is called general.index
and no view is explicitly defined in your handler, ColdBox will look for a view in the general
folder called index.cfm
. We recommend matching event resolution to view resolution, even if you use implicit views.
Tip: This feature is more for convention purists than anything else. However, we do recommend, as best practice, explicitly declaring the view to be rendered when working with team environments, as everybody will know what happens.
Caution: If using implicit views, please note that the name of the view will ALWAYS be in lowercase. So please be aware of this limitation. I would suggest creating URL Mappings with explicit event declarations to control case and location. When using implicit views, you will also lose fine rendering control.
You can also disable implicit views by using the coldbox.implicitViews
configuration setting in your config/ColdBox.cfc
. This is useful as implicit lookups are time-consuming.
The ColdBox rendering engine can also be tweaked to use case-insensitive or sensitive implicit views by using the coldbox.caseSensitiveImplicitViews
directive in your config/ColdBox.cfc
. The default is to turn all implicit views to lowercase, so the value is always false.
ColdBox provides you with a very simple but flexible and powerful layout manager and content renderer. You no longer need to create module tags or convoluted broken up HTML anymore. You can concentrate on the big picture and create as many layouts as your application needs. Then you can programmatically change rendering schemas (or skinning) and also create composite or component based views.
In this section we will explore the different rendering mechanisms that ColdBox offers and also how to utilize them. As you know, event handlers are our controller layer in ColdBox and we will explore how these objects can interact with the user in order to render content, whether HTML, JSON, XML or any type of rendering data.
Please note that you can use ColdBox as a pure API solution with modern JavaScript frameworks for the front end like VueJS, Reactor, Angular, etc.
Let's do a recap of our conventions for layouts and view locations:
It is imperative to know who does the rendering in ColdBox and that is the Renderer class that you can see from our diagram above. As you can tell from the diagram, it includes your layouts and/or views into itself in order to render out content. So by this association and inheritance all layouts and views have some variables and methods at their disposal since they get absorbed into the object. You can visit the API docs to learn about all the Renderer methods.
All of the following property members exist in all layouts and views rendered by the Renderer:
Property
Description
event
A reference to the Request Context object
rc
A reference to the request collection inside of the request context (For convenience)
prc
A reference to the private request collection inside of the request context (For convenience)
html
cacheBox
controller
A reference to the application's ColdBox Controller (coldbox.system.web.Controller)
flash
logbox
log
wirebox
As you can see, all views and layouts have direct reference to the request collections so it makes it incredibly easy to get and put data into it. Also, remember that the Renderer inherits from the Framework SuperType so all methods are at your disposal if needed.
You can also inject the ColdBox Renderer into your models so you can render email templates, views, etc. directly from your model code:
In previous versions you would need to use a provider:
syntax due to the Renderer being a transient. This is no longer true in ColdBox 6.0.
Event caching is extremely useful and easy to use. ColdBox will act like a cache proxy between your events and the clients requesting the events, much like squid, nginx or HA Proxy. All you need to do is add several metadata arguments to the action methods and the framework will cache the output of the event in the template cache provider in CacheBox. In other words, the event executes and produces output that the framework then caches. Subsequent calls to the same event with the same incoming RC variables will not do any processing, but just output the content back to the user.
For example, you have an event called blog.showEntry
. This event executes, gets an entry from the database and sets a view to be rendered. The framework then renders the view and if event caching is turned on for this event, the framework will cache the HTML produced. So the next incoming show entry event will just spit out the cached HTML. The cache key is created by hashing the incoming request collection.
Important to note also, that any combination of URL/FORM parameters on an event will produce a unique cacheable key. So event=blog.showEntry&id=1
& event=blog.showEntry&id=2
are two different cacheable events.
To enable event caching, you will need to set a setting in your ColdBox.cfc
called coldbox.eventcaching
to true
.
Important Enabling event caching does not mean that ALL events will be cached. It just means that you enable this feature.
The way to set up an event for caching is on the function declaration with the following annotations:
Annotation
Type
Description
cache
boolean
A true or false will let the framework know whether to cache this event or not. The default is FALSE. So setting to false makes no sense
cachetimeout
numeric
The timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default object timeout in the cache settings. You can place a 0 in order to tell the framework to cache the event's output for the entire application timeout controlled by coldfusion, NOT GOOD. Always set a decent timeout for content.
cacheLastAccesstimeout
numeric
The last access timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default last access object timeout in the cache settings. This tells the framework that if the object has not been accessed in X amount of minutes, then purge it.
cacheProvider
string
The cache provider to store the results in. By default it uses the template cache.
Important Please be aware that you should not cache output with 0 timeouts (forever). Always use a timeout.
Alert: DO NOT cache events as unlimited timeouts. Also, all events can have an unlimited amount of permutations, so make sure they expire and you purge them constantly. Every event + URL/FORM variable combination will produce a new cacheable entry.
All event and view caching are stored in a named cache called template
which all ColdBox applications have by default. You can open or create a new CacheBox configuration object and decide where the storage is, timeouts, providers, etc. You have complete control of how event and view caching is stored.
We also have a great way to purge these events programmatically via our cache provider interface.
Methods for event purging:
clearEvent( string eventSnippet, string querystring="" )
: Clears all the event permutations from the cache according to snippet and querystring. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations
clearEventMulti( eventsnippets,string querystring="" )
: Clears all the event permutations from the cache according to the list of snippets and querystrings. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations
clearAllEvents( [boolean async=true] )
: Can clear ALL cached events in one shot and can be run asynchronously.
You can now leverage the cache suffix property in handlers to be declared as a closure so it can be evaluated at runtime so it can add dynamic suffixes to cache keys. This can allow you to incorporate elements into the cache key at runtime instead of statically. This is a great way to incorporate the user's language locale or session identifier to make unique entries.
OnRequestCapture
- Influence Cache KeysWe have provided an interception point in ColdBox that allows you to add variables into the request collection before a snapshot is made so you can influence the cache key of a cacheable event. What this means is that you can use it to mix in variables into the request collection that can make this event cache unique for a user, a specific language, country, etc. This is a great way to leverage event caching on multi-lingual or session based sites.
With the simple example above, the user's locale will be added to all your event caching permutations and thus create entries for different languages.
Several event interception points are NOT available during event caching.
When using event caching the framework will NOT execute ANY event at all. It will stream the content directly from the selected cache provider. This means that any interceptors or code that executes in the event is also NOT executed. The only interception points that will execute are:
preProcess
postProcess
So please make sure you take note of this when planning for event security.
CacheBox has an intuitive and powerful monitor that can be used via the ColdBox Debugger Module. From the monitor you can purge, expire and view cache elements, etc.
We have now seen how to set views to be rendered from our handlers. However, we can use some cool methods to render views and layouts on demand. These methods exist in the Renderer, and several facade methods exist in the supertype, so you can call it from any handler, interceptor, view, or layout.
view()
externalView()
layout()
Inline renderings are a great asset for reusing views and doing layout compositions
If you need rendering capabilities in your model layer, we suggest using the following injection DSL:
A reference to the that can help you build interactive and safe HTML
A reference to the framework factory (coldbox.system.cache.CacheFactory)
A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope (derived coldbox.system.web.flash.AbstractFlashScope)
The reference to the library (coldbox.system.logging.LogBox)
A pre-configured LogBox object for this specific class object (coldbox.system.logging.Logger)
A reference to the object factory (coldbox.system.ioc.Injector)
Check out the latest for the latest arguments.
This will inject a of a Renderer into your model objects. Remember that renderers are transient objects so you cannot treat them as singletons. The provider is a proxy to the transient object, but you can use it just like the normal object:
So what if I want to render a view outside of my application without using the setting explained above? Well, you use the externalView()
method.
If you are using ColdBox 6.4 or older, you will want to use the renderExternalView()
method name. In ColdBox 6.5.2+, renderExternalView()
was deprecated in favor of the new externalView()
method.
This is a nifty little feature that enables you to create nice helper templates on a per-view, per-folder and per-application basis. If the framework detects the helper, it will inject it into the rendering view so you can use methods, properties or whatever. All you need to do is follow a set of conventions. Let's say we have a view in the following location:
Then we can create the following templates
homeHelper.cfm
: Helper for the home.cfm view.
generalHelper.cfm
: Helper for any view in the general folder.
homeHelper.cfm
That's it. Just append Helper to the view or folder name and there you go, the framework will use it as a helper for that view specifically. What can you put in these helper templates:
NO BUSINESS CODE
UI logic functions or properties
Helper functions or properties
Dynamic JavaScript or CSS
Hint External views can also use our helper conventions
You can also use the coldbox.viewsHelper
directive to tell the framework what helper file to use for ALL views and layouts rendered:
You have a few arguments in the view()
method that deal with collection rendering. Meaning you can pass any array or query and the Renderer will iterate over that collection and render out the view as many times as the records in the colleciton.
collection
: A data collection that can be a query or an array of objects, structs or whatever
collectionAs
: The name of the variable in the variables scope that will hold the collection pivot.
collectionStartRow
: Defaults to 1 or your offset row for the collection rendering
collectionMaxRows
: Defaults to show all rows or you can cap the rendering display
collectionDelim
: An optional delimiter to use to separate the collection renderings. By default it is empty.
Once you call view()
with a collection, the renderer will render the view once for each member in the collection. The views have access to the collection via arguments.collection or the member currently iterating. The name of the member being iterated as is by convention the same name as the view. So if we do this in any layout or simple view:
Then the tags/comment
will be rendered as many times as the collection rc.comments
has members on it and by convention the name of the variable is comment the same as the view name.
If you don't like that, then use the collectionAs argument:
So let's see the collection view now:
You can see that I just call methods on the member as if I was looping (which we are for you). But you will also see two little variables here:
_counter
: A variable created for you that tells you in which record we are currently looping on
_items
: A variable created for you that tells you how many records exist in the collection
This will then render that specific dynamic HTML view as many times as their are records in the rc.comments array and concatenate them all for you. In my case, I separate each iteration with a simple but you can get fancy and creative.
You can pass localized arguments to the view() and layout()
methods in order to encapsulate the rendering via the args
struct argument. Much like how you make method calls with arguments. Inside of your layouts and views you will receive the same args
struct reference as well.
This gives you great DRYness (yes that is a word) when building new and edit forms or views as you can pass distinct arguments to distinguish them and keep structure intact.
You can also pass in the caching arguments below and your view will be rendered once and then cached for further renderings. Every ColdBox application has two active cache regions: default and template
. All view and event caching renderings go into the template
cache.
So now that our views are cached, how do I purge them programmatically? Well, you need to talk to the template
cache provider and use the clearing methods:
Then we can perform several operations on views:
clearView(string viewSnippet)
: Used to clear a view from the cache by using a snippet matched according to name + cache suffix.
clearMultiView(any viewSnippets)
: Clear using a list or array of view snippets.
clearAllViews([boolean async=true])
: Can clear ALL cached views in one shot and can be run asynchronously.
To turn off view caching for your entire application, set the viewCaching
setting to false in your config/Coldbox.cfc
config file.
Argument
Type
Required
Default
Description
cache
boolean
false
false
Cache the view to be rendered
cacheTimeout
numeric
false
(provider default)
The timeout in minutes or whatever the cache provider defines
cacheLastAccessTimeout
numeric
false
(provider default)
The idle timeout in minutes or whatever the cache provider defines
cacheSuffix
string
false
---
Adds a suffix key to the cached key. Used for providing uniqueness to the cacheable entry
All rendered views have associated events that are announced whenever the view is rendered via ColdBox Interceptors. These are great ways for you to be able to intercept when views are rendered and transform them, append to them, or even remove content from them in a completely decoupled approach. The way to listen for events in ColdBox is to write Interceptors, which are essential simple CFC's that by convention have a method that is the same name as the event they would like to listen to. Each event has the capability to receive a structure of information which you can alter, append or remove from. Once you write these Interceptors you can either register them in your Configuration File or programmatically.
Event
Data
Description
preViewRender
view - The name of the view to render
Executed before a view is about to be rendered
postViewRender
All of the data above plus:
Executed after a view was rendered
Caution You can disable the view events on a per-rendering basis by passing the
prePostExempt
argument as true when callingrenderView()
methods.
Here is a sample interceptor that trims any content before it is renderer:
Of course, I am pretty sure you will be more creative than that!
A layout is simply an HTML file that acts as your shell where views can be rendered in and exists in the layouts
folder of your application. The layouts can be composed of multiple views and one main view. The main view is the view that gets set in the request collection during a request via event.setView()
usually in your handlers or interceptors.
You can have as many layouts as you need in your application and they are super easy to override or assign to different parts of your application. Imagine switching content from a normal HTML layout to a PDF or mobile layout with one line of code. How cool would that be? Well, it's that cool with ColdBox. Another big benefit of layouts, is that you can see the whole picture of the layout, not the usual cfmodule calls where tables are broken, or divs are left open as the module wraps itself around content. The layout is a complete html document and you basically describe where views will be rendered. Much easier to build and simpler to maintain.
Info Another important aspect of layouts is that they can also consume other layouts as well. So you can create nested layouts very easily via the
renderLayout()
method.
The default layout in a ColdBox application is layouts/main.cfm
by convention. You can change this by using the layoutSettings
in your Coldbox.cfc
.
There is no default view in ColdBox, but you can configure one by using the same layoutSettings
configuration directive and the defaultView
directive:
This means that if no view is set in the request context, ColdBox will fallback to this view for rendering.