PageRenderTime 43ms CodeModel.GetById 17ms app.highlight 5ms RepoModel.GetById 15ms app.codeStats 0ms

/wheels/model/miscellaneous.cfm

http://cfwheels.googlecode.com/
ColdFusion | 406 lines | 318 code | 37 blank | 51 comment | 19 complexity | 8cbe20494595ec18b0e2046e27c5ccfd MD5 | raw file
  1<!--- PUBLIC MODEL INITIALIZATION METHODS --->
  2
  3<cffunction name="dataSource" returntype="void" access="public" output="false" hint="Use this method to override the data source connection information for this model."
  4	examples=
  5	'
  6		<!--- In models/User.cfc --->
  7		<cffunction name="init">
  8			<!--- Tell Wheels to use the data source named `users_source` instead of the default one whenever this model makes SQL calls  --->
  9  			<cfset dataSource("users_source")>
 10		</cffunction>
 11	'
 12	categories="model-initialization,miscellaneous" chapters="using-multiple-data-sources" functions="">
 13	<cfargument name="datasource" type="string" required="true" hint="The data source name to connect to.">
 14	<cfargument name="username" type="string" required="false" default="" hint="The username for the data source.">
 15	<cfargument name="password" type="string" required="false" default="" hint="The password for the data source.">
 16	<cfscript>
 17		StructAppend(variables.wheels.class.connection, arguments, true);
 18	</cfscript>
 19</cffunction>
 20
 21<cffunction name="getDataSource" returntype="struct" access="public" output="false" hint="returns the connection (datasource) information for the model."
 22	examples=
 23	'
 24		<!--- get the datasource information so we can write custom queries --->
 25		<cfquery name="q" datasource="##getDataSource().datasource##">
 26		select * from mytable
 27		</cfquery>
 28	'>
 29	<cfreturn variables.wheels.class.connection>
 30</cffunction>
 31
 32<cffunction name="table" returntype="void" access="public" output="false" hint="Use this method to tell Wheels what database table to connect to for this model. You only need to use this method when your table naming does not follow the standard Wheels convention of a singular object name mapping to a plural table name."
 33	examples=
 34	'
 35		<!--- In models/User.cfc --->
 36		<cffunction name="init">
 37			<!--- Tell Wheels to use the `tbl_USERS` table in the database for the `user` model instead of the default (which would be `users`) --->
 38			<cfset table("tbl_USERS")>
 39		</cffunction>
 40	'
 41	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,tableName">
 42	<cfargument name="name" type="string" required="true" hint="Name of the table to map this model to.">
 43	<cfset variables.wheels.class.tableName = arguments.name>
 44</cffunction>
 45
 46<cffunction name="setTableNamePrefix" returntype="void" access="public" output="false" hint="Sets a prefix to prepend to the table name when this model runs SQL queries."
 47	examples='
 48		<!--- In `models/User.cfc`, add a prefix to the default table name of `tbl` --->
 49		<cffunction name="init">
 50			<cfset setTableNamePrefix("tbl")>
 51		</cffunction>
 52	'
 53	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,table">
 54	<cfargument name="prefix" type="string" required="true" hint="A prefix to prepend to the table name.">
 55	<cfset variables.wheels.class.tableNamePrefix =  arguments.prefix>
 56</cffunction>
 57
 58<cffunction name="setPrimaryKey" returntype="void" access="public" output="false" hint="Allows you to pass in the name(s) of the property(s) that should be used as the primary key(s). Pass as a list if defining a composite primary key. Also aliased as `setPrimaryKeys()`."
 59	examples='
 60		<!--- In `models/User.cfc`, define the primary key as a column called `userID` --->
 61		<cffunction name="init">
 62			<cfset setPrimaryKey("userID")>
 63		</cffunction>
 64	'
 65	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,table">
 66	<cfargument name="property" type="string" required="true" hint="Property (or list of properties) to set as the primary key.">
 67	<cfset var loc = {}>
 68	<cfloop list="#arguments.property#" index="loc.i">
 69		<cfset variables.wheels.class.keys = ListAppend(variables.wheels.class.keys, loc.i)>
 70	</cfloop>
 71</cffunction>
 72
 73<cffunction name="setPrimaryKeys" returntype="void" access="public" output="false" hint="Alias for @setPrimaryKey. Use this for better readability when you're setting multiple properties as the primary key."
 74	examples='
 75		<!--- In `models/Subscription.cfc`, define the primary key as composite of the columns `customerId` and `publicationId` --->
 76		<cffunction name="init">
 77			<cfset setPrimaryKeys("customerId,publicationId")>
 78		</cffunction>
 79	'
 80	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,table">
 81	<cfargument name="property" type="string" required="true" hint="Property (or list of properties) to set as the primary key.">
 82	<cfset setPrimaryKey(argumentCollection=arguments)>
 83</cffunction>
 84
 85<!--- PUBLIC MODEL CLASS METHODS --->
 86
 87<cffunction name="columnNames" returntype="string" access="public" output="false" hint="Returns a list of column names in the table mapped to this model. The list is ordered according to the columns' ordinal positions in the database table."
 88	examples=
 89	'
 90		<!--- Get a list of all the column names in the table mapped to the `author` model --->
 91		<cfset columns = model("author").columnNames()>
 92	'
 93	categories="model-class,miscellaneous" chapters="object-relational-mapping" functions="dataSource,property,propertyNames,table,tableName">
 94	<cfreturn variables.wheels.class.columnList>
 95</cffunction>
 96
 97<cffunction name="primaryKey" returntype="string" access="public" output="false" hint="Returns the name of the primary key for this model's table. This is determined through database introspection. If composite primary keys have been used, they will both be returned in a list. This function is also aliased as `primaryKeys()`."
 98	examples=
 99	'
100		<!--- Get the name of the primary key of the table mapped to the `employee` model (which is the `employees` table by default) --->
101		<cfset keyName = model("employee").primaryKey()>
102	'
103	categories="model-class,miscellaneous" chapters="object-relational-mapping" functions="primaryKeys">
104	<cfargument name="position" type="numeric" required="false" default="0" hint="If you are accessing a composite primary key, pass the position of a single key to fetch.">
105	<cfif arguments.position gt 0>
106		<cfreturn ListGetAt(variables.wheels.class.keys, arguments.position)>
107	</cfif>
108	<cfreturn variables.wheels.class.keys>
109</cffunction>
110
111<cffunction name="primaryKeys" returntype="string" access="public" output="false" hint="Alias for @primaryKey. Use this for better readability when you're accessing multiple primary keys."
112	examples=
113	'
114		<!--- Get a list of the names of the primary keys in the table mapped to the `employee` model (which is the `employees` table by default) --->
115		<cfset keyNames = model("employee").primaryKeys()>
116	'
117	categories="model-class,miscellaneous" chapters="object-relational-mapping" functions="primaryKey"
118>
119	<cfargument name="position" type="numeric" required="false" default="0" hint="See documentation for @primaryKey.">
120	<cfreturn primaryKey(argumentCollection=arguments)>
121</cffunction>
122
123<cffunction name="tableName" returntype="string" access="public" output="false" hint="Returns the name of the database table that this model is mapped to."
124	examples=
125	'
126		<!--- Check what table the user model uses --->
127		<cfset whatAmIMappedTo = model("user").tableName()>
128	'
129	categories="model-class,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,table">
130	<cfreturn variables.wheels.class.tableName>
131</cffunction>
132
133<cffunction name="getTableNamePrefix" returntype="string" access="public" output="false" hint="Returns the table name prefix set for the table."
134	examples='
135		<!--- Get the table name prefix for this user when running a custom query --->
136		<cffunction name="getDisabledUsers" returntype="query">
137			<cfset var loc = {}>
138			<cfquery datasource="##get(''dataSourceName'')##" name="loc.disabledUsers">
139				SELECT
140					*
141				FROM
142					##this.getTableNamePrefix()##users
143				WHERE
144					disabled = 1
145			</cfquery>
146			<cfreturn loc.disabledUsers>
147		</cffunction>
148	'
149	categories="model-class,miscellaneous" chapters="object-relational-mapping" functions="columnNames,dataSource,property,propertyNames,table">
150	<cfreturn variables.wheels.class.tableNamePrefix>
151</cffunction>
152
153<!--- PUBLIC MODEL OBJECT METHODS --->
154
155<cffunction name="compareTo" access="public" output="false" returntype="boolean" hint="Pass in another Wheels model object to see if the two objects are the same."
156	examples='
157		<!--- Load a user requested in the URL/form and restrict access if it doesn''t match the user stored in the session --->
158		<cfset user = model("user").findByKey(params.key)>
159		<cfif not user.compareTo(session.user)>
160			<cfset renderPage(action="accessDenied")>
161		</cfif>
162	'
163	categories="model-object,miscellaneous" chapters="" functions="">
164	<cfargument name="object" type="component" required="true">
165	<cfreturn Compare(this.$objectId(), arguments.object.$objectId()) eq 0 />
166</cffunction>
167
168<cffunction name="$objectId" access="public" output="false" returntype="string">
169	<cfreturn variables.wheels.instance.tickCountId />
170</cffunction>
171
172<cffunction name="$alias" access="public" output="false" returntype="void">
173	<cfargument name="associationName" type="string" required="true">
174	<cfset variables.wheels.class.aliases[arguments.associationName] = tableName() & StructCount(variables.wheels.class.aliases)>
175</cffunction>
176
177<cffunction name="$aliasName" access="public" output="false" returntype="string">
178	<cfargument name="associationName" type="string" required="false" default="">
179	<cfscript>
180		if (!Len(arguments.associationName) or !StructKeyExists(variables.wheels.class.aliases, arguments.associationName))
181			return tableName();
182	</cfscript>
183	<cfreturn variables.wheels.class.aliases[arguments.associationName]>
184</cffunction>
185
186<cffunction name="isInstance" returntype="boolean" access="public" output="false" hint="Use this method to check whether you are currently in an instance object."
187	examples='
188		<!--- Use the passed in `id` when we''re not already in an instance --->
189		<cffunction name="memberIsAdmin">
190			<cfif isInstance()>
191				<cfreturn this.admin>
192			<cfelse>
193				<cfreturn this.findByKey(arguments.id).admin>
194			</cfif>
195		</cffunction>
196	'
197	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="isClass">
198	<cfreturn StructKeyExists(variables.wheels, "instance")>
199</cffunction>
200
201<cffunction name="isClass" returntype="boolean" access="public" output="false" hint="Use this method within a model's method to check whether you are currently in a class-level object."
202	examples='
203		<!--- Use the passed in `id` when we''re already in an instance --->
204		<cffunction name="memberIsAdmin">
205			<cfif isClass()>
206				<cfreturn this.findByKey(arguments.id).admin>
207			<cfelse>
208				<cfreturn this.admin>
209			</cfif>
210		</cffunction>
211	'
212	categories="model-initialization,miscellaneous" chapters="object-relational-mapping" functions="isInstance">
213	<cfreturn !isInstance(argumentCollection=arguments)>
214</cffunction>
215
216<cffunction name="setPagination" access="public" output="false" returntype="void" hint="Allows you to set a pagination handle for a custom query so you can perform pagination on it in your view with `paginationLinks()`."
217	examples=
218	'
219		<!---
220			Note that there are two ways to do pagination yourself using
221			a custom query.
222
223			1) Do a query that grabs everything that matches and then use
224			the `cfouput` or `cfloop` tag to page through the results.
225
226			2) Use your database to make 2 queries. The first query
227			basically does a count of the total number of records that match
228			the criteria and the second query actually selects the page of
229			records for retrieval.
230
231			In the example below, we will show how to write a custom query
232			using both of these methods. Note that the syntax where your
233			database performs the pagination will differ depending on the
234			database engine you are using. Plese consult your database
235			engine''s documentation for the correct syntax.
236
237			Also note that the view code will differ depending on the method
238			used.
239		--->
240
241		<!---
242			First method: Handle the pagination through your CFML engine
243		--->
244
245		<!--- Model code --->
246		<!--- In your model (ie. User.cfc), create a custom method for your custom query --->
247		<cffunction name="myCustomQuery">
248			<cfargument name="page" type="numeric">
249			<cfargument name="perPage" type="numeric" required="false" default="25">
250
251			<cfquery name="local.customQuery" datasource="##get(''dataSourceName'')##">
252				SELECT * FROM users
253			</cfquery>
254
255			<cfset setPagination(totalRecords=local.customQuery.RecordCount, currentPage=arguments.page, perPage=arguments.perPage, handle="myCustomQueryHandle")>
256			<cfreturn customQuery>
257		</cffunction>
258
259		<!--- Controller code --->
260		<cffunction name="list">
261			<cfparam name="params.page" default="1">
262			<cfparam name="params.perPage" default="25">
263
264			<cfset allUsers = model("user").myCustomQuery(page=params.page, perPage=params.perPage)>
265			<!---
266				Because we''re going to let `cfoutput`/`cfloop` handle the pagination,
267				we''re going to need to get some addition information about the
268				pagination.
269			 --->
270			<cfset paginationData = pagination("myCustomQueryHandle")>
271		</cffunction>
272
273		<!--- View code (using `cfloop`) --->
274		<!--- Use the information from `paginationData` to page through the records --->
275		<cfoutput>
276		<ul>
277		    <cfloop query="allUsers" statrow="##paginationData.startrow##" endrow="##paginationData.endrow##">
278		        <li>##allUsers.firstName## ##allUsers.lastName##</li>
279		    </cfloop>
280		</ul>
281		##paginationLinks(handle="myCustomQueryHandle")##
282		</cfoutput>
283
284		<!--- View code (using `cfoutput`) --->
285		<!--- Use the information from `paginationData` to page through the records --->
286		<ul>
287		    <cfoutput query="allUsers" statrow="##paginationData.startrow##" maxrows="##paginationData.maxrows##">
288		        <li>##allUsers.firstName## ##allUsers.lastName##</li>
289		    </cfoutput>
290		</ul>
291		<cfoutput>##paginationLinks(handle="myCustomQueryHandle")##</cfoutput>
292
293
294		<!---
295			Second method: Handle the pagination through the database
296		--->
297
298		<!--- Model code --->
299		<!--- In your model (ie. `User.cfc`), create a custom method for your custom query --->
300		<cffunction name="myCustomQuery">
301			<cfargument name="page" type="numeric">
302			<cfargument name="perPage" type="numeric" required="false" default="25">
303
304			<cfquery name="local.customQueryCount" datasource="##get(''dataSouceName'')##">
305				SELECT COUNT(*) AS theCount FROM users
306			</cfquery>
307
308			<cfquery name="local.customQuery" datasource="##get(''dataSourceName'')##">
309				SELECT * FROM users
310				LIMIT ##arguments.page## OFFSET ##arguments.perPage##
311			</cfquery>
312
313			<!--- Notice the we use the value from the first query for `totalRecords`  --->
314			<cfset setPagination(totalRecords=local.customQueryCount.theCount, currentPage=arguments.page, perPage=arguments.perPage, handle="myCustomQueryHandle")>
315			<!--- We return the second query --->
316			<cfreturn customQuery>
317		</cffunction>
318
319		<!--- Controller code --->
320		<cffunction name="list">
321			<cfparam name="params.page" default="1">
322			<cfparam name="params.perPage" default="25">
323			<cfset allUsers = model("user").myCustomQuery(page=params.page, perPage=params.perPage)>
324		</cffunction>
325
326		<!--- View code (using `cfloop`) --->
327		<cfoutput>
328		<ul>
329		    <cfloop query="allUsers">
330		        <li>##allUsers.firstName## ##allUsers.lastName##</li>
331		    </cfloop>
332		</ul>
333		##paginationLinks(handle="myCustomQueryHandle")##
334		</cfoutput>
335
336		<!--- View code (using `cfoutput`) --->
337		<ul>
338		    <cfoutput query="allUsers">
339		        <li>##allUsers.firstName## ##allUsers.lastName##</li>
340		    </cfoutput>
341		</ul>
342		<cfoutput>##paginationLinks(handle="myCustomQueryHandle")##</cfoutput>
343	'
344	categories="model-class,miscellaneous" chapters="getting-paginated-data" functions="findAll,paginationLinks">
345	<cfargument name="totalRecords" type="numeric" required="true" hint="Total count of records that should be represented by the paginated links.">
346	<cfargument name="currentPage" type="numeric" required="false" default="1" hint="Page number that should be represented by the data being fetched and the paginated links.">
347	<cfargument name="perPage" type="numeric" required="false" default="25" hint="Number of records that should be represented on each page of data.">
348	<cfargument name="handle" type="string" required="false" default="query" hint="Name of handle to reference in @paginationLinks.">
349	<cfscript>
350		var loc = {};
351
352		// all numeric values must be integers
353		arguments.totalRecords = fix(arguments.totalRecords);
354		arguments.currentPage = fix(arguments.currentPage);
355		arguments.perPage = fix(arguments.perPage);
356
357		// totalRecords cannot be negative
358		if (arguments.totalRecords lt 0)
359		{
360			arguments.totalRecords = 0;
361		}
362
363		// perPage less then zero
364		if (arguments.perPage lte 0)
365		{
366			arguments.perPage = 25;
367		}
368
369		// calculate the total pages the query will have
370		arguments.totalPages = Ceiling(arguments.totalRecords/arguments.perPage);
371
372		// currentPage shouldn't be less then 1 or greater then the number of pages
373		if (arguments.currentPage gte arguments.totalPages)
374		{
375			arguments.currentPage = arguments.totalPages;
376		}
377		if (arguments.currentPage lt 1)
378		{
379			arguments.currentPage = 1;
380		}
381
382		// as a convinence for cfquery and cfloop when doing oldschool type pagination
383		// startrow for cfquery and cfloop
384		arguments.startRow = (arguments.currentPage * arguments.perPage) - arguments.perPage + 1;
385
386		// maxrows for cfquery
387		arguments.maxRows = arguments.perPage;
388
389		// endrow for cfloop
390		arguments.endRow = arguments.startRow + arguments.perPage;
391
392		// endRow shouldn't be greater then the totalRecords or less than startRow
393		if (arguments.endRow gte arguments.totalRecords)
394		{
395			arguments.endRow = arguments.totalRecords;
396		}
397		if (arguments.endRow lt arguments.startRow)
398		{
399			arguments.endRow = arguments.startRow;
400		}
401
402		loc.args = duplicate(arguments);
403		structDelete(loc.args, "handle", false);
404		request.wheels[arguments.handle] = loc.args;
405	</cfscript>
406</cffunction>