PageRenderTime 560ms CodeModel.GetById 241ms app.highlight 3ms RepoModel.GetById 271ms app.codeStats 1ms

/wheels/model/associations.cfm

http://cfwheels.googlecode.com/
ColdFusion | 204 lines | 190 code | 12 blank | 2 comment | 30 complexity | 01c5e62305284e0097cbf16f722a20e7 MD5 | raw file
  1<!--- PUBLIC MODEL INITIALIZATION METHODS --->
  2
  3<cffunction name="belongsTo" returntype="void" access="public" output="false" hint="Sets up a `belongsTo` association between this model and the specified one. Use this association when this model contains a foreign key referencing another model."
  4	examples=
  5	'
  6		<!--- Specify that instances of this model belong to an author. (The table for this model should have a foreign key set on it, typically named `authorid`.) --->
  7		<cfset belongsTo("author")>
  8
  9		<!--- Same as above, but because we have broken away from the foreign key naming convention, we need to set `modelName` and `foreignKey` --->
 10		<cfset belongsTo(name="bookWriter", modelName="author", foreignKey="authorId")>
 11	'
 12	categories="model-initialization,associations" chapters="associations" functions="hasOne,hasMany">
 13	<cfargument name="name" type="string" required="true" hint="Gives the association a name that you refer to when working with the association (in the `include` argument to @findAll, to name one example).">
 14	<cfargument name="modelName" type="string" required="false" default="" hint="Name of associated model (usually not needed if you follow Wheels conventions because the model name will be deduced from the `name` argument).">
 15	<cfargument name="foreignKey" type="string" required="false" default="" hint="Foreign key property name (usually not needed if you follow Wheels conventions since the foreign key name will be deduced from the `name` argument).">
 16	<cfargument name="joinKey" type="string" required="false" default="" hint="Column name to join to if not the primary key (usually not needed if you follow wheels conventions since the join key will be the tables primary key/keys).">
 17	<cfargument name="joinType" type="string" required="false" hint="Use to set the join type when joining associated tables. Possible values are `inner` (for `INNER JOIN`) and `outer` (for `LEFT OUTER JOIN`).">
 18	<cfscript>
 19		$args(name="belongsTo", args=arguments);
 20		// deprecate the class argument (change of name only)
 21		if (StructKeyExists(arguments, "class"))
 22		{
 23			$deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
 24			arguments.modelName = arguments.class;
 25			StructDelete(arguments, "class");
 26		}
 27
 28		arguments.type = "belongsTo";
 29		arguments.methods = "#arguments.name#,has#capitalize(arguments.name)#";
 30		$registerAssociation(argumentCollection=arguments);
 31	</cfscript>
 32</cffunction>
 33
 34<cffunction name="hasMany" returntype="void" access="public" output="false" hint="Sets up a `hasMany` association between this model and the specified one."
 35	examples=
 36	'
 37		<!---
 38			Example1: Specify that instances of this model has many comments.
 39			(The table for the associated model, not the current, should have the foreign key set on it.)
 40		--->
 41		<cfset hasMany("comments")>
 42
 43		<!---
 44			Example 2: Specify that this model (let''s call it `reader` in this case) has many subscriptions and setup a shortcut to the `publication` model.
 45			(Useful when dealing with many-to-many relationships.)
 46		--->
 47		<cfset hasMany(name="subscriptions", shortcut="publications")>
 48		
 49		<!--- Example 3: Automatically delete all associated `comments` whenever this object is deleted --->
 50		<cfset hasMany(name="comments", dependent="deleteAll")>
 51		
 52		<!---
 53			Example 4: When not following Wheels naming conventions for associations, it can get complex to define how a `shortcut` works.
 54			In this example, we are naming our `shortcut` differently than the actual model''s name.
 55		--->
 56		<!--- In the models/Customer.cfc `init()` method --->
 57		<cfset hasMany(name="subscriptions", shortcut="magazines", through="publication,subscriptions")>
 58		
 59		<!--- In the models/Subscriptions.cfc `init()` method --->
 60		<cfset belongsTo("customer")>
 61		<cfset belongsTo("publication")>
 62		
 63		<!--- In the models/Publication `init()` method --->
 64		<cfset hasMany("subscriptions")>
 65	'
 66	categories="model-initialization,associations" chapters="associations" functions="belongsTo,hasOne">
 67	<cfargument name="name" type="string" required="true" hint="See documentation for @belongsTo.">
 68	<cfargument name="modelName" type="string" required="false" default="" hint="See documentation for @belongsTo.">
 69	<cfargument name="foreignKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
 70	<cfargument name="joinKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
 71	<cfargument name="joinType" type="string" required="false" hint="See documentation for @belongsTo.">
 72	<cfargument name="dependent" type="string" required="false" hint="Defines how to handle dependent models when you delete a record from this model. Set to `delete` to instantiate associated models and call their @delete method, `deleteAll` to delete without instantiating, `nullify` to remove the foreign key, or `false` to do nothing.">
 73	<cfargument name="shortcut" type="string" required="false" default="" hint="Set this argument to create an additional dynamic method that gets the object(s) from the other side of a many-to-many association.">
 74	<cfargument name="through" type="string" required="false" default="#singularize(arguments.shortcut)#,#arguments.name#" hint="Set this argument if you need to override Wheels conventions when using the `shortcut` argument. Accepts a list of two association names representing the chain from the opposite side of the many-to-many relationship to this model.">
 75	<cfscript>
 76		var singularizeName = capitalize(singularize(arguments.name));
 77		var capitalizeName = capitalize(arguments.name);
 78		$args(name="hasMany", args=arguments);
 79		// deprecate the class argument (change of name only)
 80		if (StructKeyExists(arguments, "class"))
 81		{
 82			$deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
 83			arguments.modelName = arguments.class;
 84			StructDelete(arguments, "class");
 85		}
 86
 87		arguments.type = "hasMany";
 88		arguments.methods = "#arguments.name#,#singularizeName#Count,add#singularizeName#,create#singularizeName#,delete#singularizeName#,deleteAll#capitalizeName#,findOne#singularizeName#,has#capitalizeName#,new#singularizeName#,remove#singularizeName#,removeAll#capitalizeName#";
 89		$registerAssociation(argumentCollection=arguments);
 90	</cfscript>
 91</cffunction>
 92
 93<cffunction name="hasOne" returntype="void" access="public" output="false" hint="Sets up a `hasOne` association between this model and the specified one."
 94	examples=
 95	'
 96		<!--- Specify that instances of this model has one profile. (The table for the associated model, not the current, should have the foreign key set on it.) --->
 97		<cfset hasOne("profile")>
 98
 99		<!--- Same as above but setting the `joinType` to `inner`, which basically means this model should always have a record in the `profiles` table --->
100		<cfset hasOne(name="profile", joinType="inner")>
101		
102		<!--- Automatically delete the associated `profile` whenever this object is deleted --->
103		<cfset hasMany(name="comments", dependent="delete")>
104	'
105	categories="model-initialization,associations" chapters="associations" functions="belongsTo,hasMany">
106	<cfargument name="name" type="string" required="true" hint="See documentation for @belongsTo.">
107	<cfargument name="modelName" type="string" required="false" default="" hint="See documentation for @belongsTo.">
108	<cfargument name="foreignKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
109	<cfargument name="joinKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
110	<cfargument name="joinType" type="string" required="false" hint="See documentation for @belongsTo.">
111	<cfargument name="dependent" type="string" required="false" hint="See documentation for @hasMany.">
112	<cfscript>
113		var capitalizeName = capitalize(arguments.name);
114		$args(name="hasOne", args=arguments);
115		// deprecate the class argument (change of name only)
116		if (StructKeyExists(arguments, "class"))
117		{
118			$deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
119			arguments.modelName = arguments.class;
120			StructDelete(arguments, "class");
121		}
122
123		arguments.type = "hasOne";
124		arguments.methods = "#arguments.name#,create#capitalizeName#,delete#capitalizeName#,has#capitalizeName#,new#capitalizeName#,remove#capitalizeName#,set#capitalizeName#";
125		$registerAssociation(argumentCollection=arguments);
126	</cfscript>
127</cffunction>
128
129<!--- PRIVATE MODEL INITIALIZATION METHODS --->
130
131<cffunction name="$registerAssociation" returntype="void" access="public" output="false" hint="Called from the association methods above to save the data to the class struct of the model.">
132	<cfscript>
133		// assign the name for the association
134		var associationName = arguments.name;
135		
136		// default our nesting to false and set other nesting properties
137		arguments.nested = {};
138		arguments.nested.allow = false;
139		arguments.nested.delete = false;
140		arguments.nested.autosave = false;
141		arguments.nested.sortProperty = "";
142		arguments.nested.rejectIfBlank = "";
143		// remove the name argument from the arguments struct
144		structDelete(arguments, "name", false);
145		// infer model name and foreign key from association name unless developer specified it already
146		if (!Len(arguments.modelName))
147		{
148			if (arguments.type == "hasMany")
149				arguments.modelName = singularize(associationName);
150			else
151				arguments.modelName = associationName;
152		}
153		// store all the settings for the association in the class struct (one struct per association with the name of the association as the key)
154		variables.wheels.class.associations[associationName] = arguments;
155	</cfscript>
156</cffunction>
157
158
159<cffunction name="$deleteDependents" returntype="void" access="public" output="false">
160	<cfscript>
161	var loc = {};
162	for (loc.key in variables.wheels.class.associations)
163	{
164		if (ListFindNoCase("hasMany,hasOne", variables.wheels.class.associations[loc.key].type) && variables.wheels.class.associations[loc.key].dependent != false)
165		{
166			loc.all = "";
167			if (variables.wheels.class.associations[loc.key].type == "hasMany")
168				loc.all = "All";
169		
170			switch(variables.wheels.class.associations[loc.key].dependent)
171			{
172				case "delete":
173				{
174					loc.invokeArgs = {};
175					loc.invokeArgs.instantiate = true;
176					$invoke(componentReference=this, method="delete#loc.all##loc.key#", invokeArgs=loc.invokeArgs);
177					break;
178				}
179				case "remove":
180				{
181					loc.invokeArgs = {};
182					loc.invokeArgs.instantiate = true;
183					$invoke(componentReference=this, method="remove#loc.all##loc.key#", invokeArgs=loc.invokeArgs);
184					break;
185				}
186				case "deleteAll":
187				{
188					$invoke(componentReference=this, method="delete#loc.all##loc.key#");
189					break;
190				}
191				case "removeAll":
192				{
193					$invoke(componentReference=this, method="remove#loc.all##loc.key#");
194					break;
195				}
196				default:
197				{
198					$throw(type="Wheels.InvalidArgument", message="'#variables.wheels.class.associations[loc.key].dependent#' is not a valid dependency.", extendedInfo="Use `delete`, `deleteAll`, `removeAll` or false.");
199				}
200			}
201		}	
202	}
203	</cfscript>
204</cffunction>