Multi-Step form in Model-Glue with Services

While I don't make any claims at being a model-glue expert, I did come up with a way to run my multi-step forms in model-glue. I basically use a single 'event' entry point that has several named results, and the single entry point controller runs 'makeEventBean()' against my form bean (kept in session), and then passes the bean to the service and asks 'what is my next step?' The next named step gets fired off to the named result, and the user see's the appropriate screen in the multi-step process. I am using Transfer ORM, so inside the service you will see some use of that code. Also, I am not going to paste the display files here, this is just really to blog about the gist of my methods to handle a multi-step form. There are quite a few little code chunks here, so without further ado...


coldspring.xml <bean id=<span class='cc_value'>"forgotPassword"</span> class=<span class='cc_value'>"forms.forgotPassword"</span> singleton=<span class='cc_value'>"false"</span>>
</bean>

ModelGlue.xml <span class='comment'></span>
      <event-handler name=<span class='cc_value'>"user.forgotPassword"</span> access=<span class='cc_value'>"public"</span>>
         <broadcasts>
            <message name=<span class='cc_value'>"forgotPassword"</span>>
         </message>
         <views>
         <results>
<result name=<span class='cc_value'>"needIdentity"</span> do=<span class='cc_value'>"user.forgotPassword.NeedIdentity"</span> redirect=<span class='cc_value'>"no"</span> preservestate=<span class='cc_value'>"true"</span>>
<result name=<span class='cc_value'>"needSecretAnswer"</span> do=<span class='cc_value'>"user.forgotPassword.needSecretAnswer"</span> redirect=<span class='cc_value'>"no"</span> preservestate=<span class='cc_value'>"true"</span>>
<result name=<span class='cc_value'>"needNewPassword"</span> do=<span class='cc_value'>"user.forgotPassword.needNewPassword"</span> redirect=<span class='cc_value'>"no"</span> preservestate=<span class='cc_value'>"true"</span>>
<result name=<span class='cc_value'>"success"</span> do=<span class='cc_value'>"user.forgotPassword.success"</span> redirect=<span class='cc_value'>"no"</span> preservestate=<span class='cc_value'>"true"</span>>
         </result>
      </result>
      
      <event-handler name=<span class='cc_value'>"user.forgotPassword.NeedIdentity"</span> access=<span class='cc_value'>"public"</span>>
         <broadcasts>
         <views>
            <include name=<span class='cc_value'>"body"</span> template=<span class='cc_value'>"user/forms/forgotPassword/dspNeedIdentity.cfm"</span>>
<value name=<span class='cc_value'>"xe.submit"</span> value=<span class='cc_value'>"user/forgotPassword"</span>>
</value>
         </include>
<results>
<result do=<span class='cc_value'>"view.template"</span>>
</result>
      </results>
      
      <event-handler name=<span class='cc_value'>"user.forgotPassword.NeedSecretAnswer"</span> access=<span class='cc_value'>"public"</span>>
         <broadcasts>
         <views>
            <include name=<span class='cc_value'>"body"</span> template=<span class='cc_value'>"user/forms/forgotPassword/dspNeedSecretAnswer.cfm"</span>>
<value name=<span class='cc_value'>"xe.submit"</span> value=<span class='cc_value'>"user/forgotPassword"</span>>
</value>
         </include>
<results>
<result do=<span class='cc_value'>"view.template"</span>>
</result>
      </results>
      
      <event-handler name=<span class='cc_value'>"user.forgotPassword.NeedNewPassword"</span> access=<span class='cc_value'>"public"</span>>
         <broadcasts>
         <views>
            <include name=<span class='cc_value'>"body"</span> template=<span class='cc_value'>"user/forms/forgotPassword/dspNeedNewPassword.cfm"</span>>
<value name=<span class='cc_value'>"xe.submit"</span> value=<span class='cc_value'>"user/forgotPassword"</span>>
</value>
         </include>
<results>
<result do=<span class='cc_value'>"view.template"</span>>
</result>
      </results>
      
      <event-handler name=<span class='cc_value'>"user.forgotPassword.success"</span> access=<span class='cc_value'>"public"</span>>
         <broadcasts>
         <views>
            <include name=<span class='cc_value'>"body"</span> template=<span class='cc_value'>"user/forms/forgotPassword/dspSuccess.cfm"</span>>
<value name=<span class='cc_value'>"xe.submit"</span> value=<span class='cc_value'>"user/forgotPassword"</span>>
</value>
         </include>
<results>
<result do=<span class='cc_value'>"view.template"</span>>
</result>
      </results>      
      <span class='comment'></span>
</views></broadcasts></event-handler></views></broadcasts></event-handler></views></broadcasts></event-handler></views></broadcasts></event-handler></result></result></results></views></broadcasts></event-handler>

userController.cfc <cffunction name=<span class='cc_value'>"forgotPassword"</span> access=<span class='cc_value'>"public"</span> returntype=<span class='cc_value'>"void"</span> output=<span class='cc_value'>"false"</span>>
      <cfargument name=<span class='cc_value'>"event"</span> type=<span class='cc_value'>"any"</span>>
      <cfset var=<span class='cc_value'>""</span> result=<span class='cc_value'>""</span>>
      <cfset var=<span class='cc_value'>""</span> forgotpasswordbean=<span class='cc_value'>""</span>>
      
      <!--<span class='cc_comment'>-// Instance bean if necessary, otherwise grab from session //---></span>      <cfif not=<span class='cc_value'>""</span> structkeyexists(session,=<span class='cc_value'>""</span> forgotpasswordbean=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
         <cfset session.forgotpasswordbean=<span class='cc_value'>"getModelGlue().getBean(&amp;quot;forgotPassword&amp;quot;)"</span>>
      </cfset>
      
      <!--<span class='cc_comment'>-// Get the bean from the session scope //---></span>      <cfset forgotpasswordbean=<span class='cc_value'>"session.forgotPasswordBean"</span>>
      
      <!--<span class='cc_comment'>-// Run all setters in forgotPassword bean that have matching event values //---></span>      <cfset arguments.event.makeeventbean(forgotpasswordbean)=<span class='cc_value'>""</span>>         
      
      <!--<span class='cc_comment'>-// Obtain form status by passing bean into userService function for processing //---></span>      <cfset result=<span class='cc_value'>"getUserService().forgotPassword(forgotPasswordBean)"</span>>
      
      <!--<span class='cc_comment'>-// Put the form bean into the event scope //---></span>      <cfset arguments.event.setvalue(=<span class='cc_value'>""</span> forgotpasswordbean=<span class='cc_value'>""</span> ,=<span class='cc_value'>""</span> forgotpasswordbean)=<span class='cc_value'>""</span>>
      
      <!--<span class='cc_comment'>-// If the reset form was a success, remove it from the session scope //---></span>      <cfif result=<span class='cc_value'>""</span> eq=<span class='cc_value'>""</span> success=<span class='cc_value'>""</span>>
         <cfset structdelete(session,=<span class='cc_value'>""</span> forgotpasswordbean=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
      </cfset>
      
      <!--<span class='cc_comment'>-// Run the appropriate result action, or by default run needIdentity //---></span>      <cfif len(result)=<span class='cc_value'>""</span>>
         <cfset arguments.event.addresult(result)=<span class='cc_value'>""</span>>
      <cfelse>
         <cfset arguments.event.addresult(=<span class='cc_value'>""</span> needidentity=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
      </cfset>
   </cfelse>
</cfset></cfif></cfif></cfset></cfset></cfset></cfset></cfif></cfset></cfset></cfargument></cffunction>

userService.cfc <cffunction name=<span class='cc_value'>"forgotPassword"</span> returntype=<span class='cc_value'>"any"</span> output=<span class='cc_value'>"false"</span> access=<span class='cc_value'>"public"</span>>
      <cfargument name=<span class='cc_value'>"forgotPasswordBean"</span> required=<span class='cc_value'>"true"</span> type=<span class='cc_value'>"forms.forgotPassword"</span>>
      <cfset var=<span class='cc_value'>""</span> result=<span class='cc_value'>"needIdentity"</span>>
      <cfset var=<span class='cc_value'>""</span> bean=<span class='cc_value'>"arguments.forgotPasswordBean"</span>>
      <cfset var=<span class='cc_value'>""</span> user=<span class='cc_value'>""</span>>
            
      <!--<span class='cc_comment'>-// OK, the user has provided a username or email address, check for unique //---></span>      <cfif not=<span class='cc_value'>""</span> isnumeric(bean.getuserid())=<span class='cc_value'>""</span>>
         <cfif len(bean.getusername())=<span class='cc_value'>""</span>>
            <cfset user=<span class='cc_value'>"getUsersByProperty(&amp;quot;username&amp;quot;,"</span> bean.getusername())=<span class='cc_value'>""</span>>
         <cfelseif len(bean.getemail())=<span class='cc_value'>""</span>>
            <cfset user=<span class='cc_value'>"getUsersByProperty(&amp;quot;email&amp;quot;,"</span> bean.getemail())=<span class='cc_value'>""</span>>
         <cfelse> <!--<span class='cc_comment'>-// Nothing is defined, need identity //---></span>            <cfset result=<span class='cc_value'>"needIdentity"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>                     
         </cfreturn>
         
         <cfif structkeyexists(user,=<span class='cc_value'>""</span> recordcount=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> user.recordcount=<span class='cc_value'>""</span> eq=<span class='cc_value'>""</span> 1=<span class='cc_value'>""</span>>         
            <cfset bean.setuserid(user.id)=<span class='cc_value'>""</span>>
            <cfset bean.seterror(=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
         <cfelseif structkeyexists(user,=<span class='cc_value'>""</span> recordcount=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> user.recordcount=<span class='cc_value'>""</span> gt=<span class='cc_value'>""</span> 1=<span class='cc_value'>""</span>>
            <cfset bean.seterror(=<span class='cc_value'>""</span> more=<span class='cc_value'>""</span> than=<span class='cc_value'>""</span> 1=<span class='cc_value'>""</span> user=<span class='cc_value'>""</span> matched=<span class='cc_value'>""</span> that=<span class='cc_value'>""</span> criteria,=<span class='cc_value'>""</span> you=<span class='cc_value'>""</span> will=<span class='cc_value'>""</span> need=<span class='cc_value'>""</span> call=<span class='cc_value'>""</span> to=<span class='cc_value'>""</span> have=<span class='cc_value'>""</span> your=<span class='cc_value'>""</span> password=<span class='cc_value'>""</span> reset.=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needIdentity"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>
         <cfelse>
            <cfset bean.seterror(=<span class='cc_value'>""</span> user=<span class='cc_value'>""</span> not=<span class='cc_value'>""</span> found=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needIdentity"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>
         </cfreturn>
      </cfset>
      
      <!--<span class='cc_comment'>-// User is found and defined, lets get their secret question if not already defined //---></span>      <cfif isnumeric(bean.getuserid())=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> not=<span class='cc_value'>""</span> len(bean.getsecretquestion())=<span class='cc_value'>""</span>>
         <cfset bean.setsecretquestion(getuser(bean.getuserid()).getsecretquestion().getquestion())=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needSecretAnswer"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>
      </cfreturn>
      
      <!--<span class='cc_comment'>-// If the question and answer are defined? //---></span>      <cfif len(bean.getsecretquestion())=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> len(bean.getsecretanswer())=<span class='cc_value'>""</span>>
         <!--<span class='cc_comment'>-// Is the secret answer supplied by the user correct? //---></span>         <cfif bean.getsecretanswer()=<span class='cc_value'>""</span> eq=<span class='cc_value'>""</span> getuser(bean.getuserid()).getsecretanswer()=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> not=<span class='cc_value'>""</span> len(bean.getnewpassword())=<span class='cc_value'>""</span>>
            <cfset bean.setsecretanswerok(=<span class='cc_value'>""</span> true=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset bean.seterror(=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needNewPassword"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>
         <cfelseif bean.getsecretanswer()=<span class='cc_value'>""</span> neq=<span class='cc_value'>""</span> getuser(bean.getuserid()).getsecretanswer()=<span class='cc_value'>""</span>>
            <cfset bean.seterror(=<span class='cc_value'>""</span> your=<span class='cc_value'>""</span> answer=<span class='cc_value'>""</span> does=<span class='cc_value'>""</span> not=<span class='cc_value'>""</span> match=<span class='cc_value'>""</span> the=<span class='cc_value'>""</span> one=<span class='cc_value'>""</span> on=<span class='cc_value'>""</span> file.=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needSecretAnswer"</span>>
            <cfreturn result=<span class='cc_value'>""</span>>
         </cfreturn>
      </cfset>
      
      <!--<span class='cc_comment'>-// Secret answer is correct, and new password is defined? //---></span>      <cfif bean.getsecretanswerok()=<span class='cc_value'>""</span> is=<span class='cc_value'>""</span> true=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> len(bean.getnewpassword())=<span class='cc_value'>""</span>>
         <!--<span class='cc_comment'>-// Does new password match re-entered new password? //---></span>         <cfif bean.getnewpassword()=<span class='cc_value'>""</span> eq=<span class='cc_value'>""</span> bean.getnewpasswordconfirm()=<span class='cc_value'>""</span> and=<span class='cc_value'>""</span> len(bean.getnewpassword())=<span class='cc_value'>""</span> gte=<span class='cc_value'>""</span> 5=<span class='cc_value'>""</span>>
            
            <!--<span class='cc_comment'>-// Everything looks great, save new password to the user //---></span>            <cfset bean.seterror(=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset user=<span class='cc_value'>"getUser(bean.getUserID())"</span>>            
            <cfset user.setpassword(variables.cryptoservice.gethash(bean.getnewpassword()=<span class='cc_value'>""</span> &=<span class='cc_value'>""</span> user.getuuid()))=<span class='cc_value'>""</span>>
            <cfset user.save()=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"success"</span>>
         <cfelseif len(bean.getnewpassword())=<span class='cc_value'>""</span> lt=<span class='cc_value'>""</span> 5=<span class='cc_value'>""</span>> <!--<span class='cc_comment'>-// Password too short //---></span>            <cfset bean.seterror(=<span class='cc_value'>""</span> your=<span class='cc_value'>""</span> new=<span class='cc_value'>""</span> password=<span class='cc_value'>""</span> is=<span class='cc_value'>""</span> too=<span class='cc_value'>""</span> short.=<span class='cc_value'>""</span> it=<span class='cc_value'>""</span> must=<span class='cc_value'>""</span> be=<span class='cc_value'>""</span> at=<span class='cc_value'>""</span> least=<span class='cc_value'>""</span> 5=<span class='cc_value'>""</span> characters=<span class='cc_value'>""</span> long.=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needNewPassword"</span>>                     
         <cfelse> <!--<span class='cc_comment'>-// Passwords dont match //---></span>
            <cfset bean.seterror(=<span class='cc_value'>""</span> the=<span class='cc_value'>""</span> passwords=<span class='cc_value'>""</span> you=<span class='cc_value'>""</span> have=<span class='cc_value'>""</span> entered=<span class='cc_value'>""</span> do=<span class='cc_value'>""</span> not=<span class='cc_value'>""</span> match.=<span class='cc_value'>""</span> )=<span class='cc_value'>""</span>>
            <cfset result=<span class='cc_value'>"needNewPassword"</span>>
         </cfset>
      </cfset>
      
      <cfreturn result=<span class='cc_value'>""</span>>
   </cfreturn>
</cfelse></cfset></cfset></cfelseif></cfset></cfset></cfset></cfset></cfset></cfif></cfif></cfset></cfelseif></cfreturn></cfset></cfset></cfset></cfif></cfif></cfset></cfset></cfif></cfset></cfelse></cfreturn></cfset></cfset></cfelseif></cfset></cfset></cfif></cfset></cfelse></cfset></cfelseif></cfset></cfif></cfif></cfset></cfset></cfset></cfargument></cffunction>

Basically the entire logic for this event is contained inside the service, which just returns the appropriate step that the user should be on based on inspecting the bean argument for its current status. Any comments good or bad would be appreciated, and while I'm not claiming this is the perfect way, it certainly works for me and is fairly easy to follow along!


Digg StumbleUpon Facebook Technorati Fav newsvine reddit FARK Google Bookmarks