/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. <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."
  3. examples=
  4. '
  5. <!--- 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`.) --->
  6. <cfset belongsTo("author")>
  7. <!--- Same as above, but because we have broken away from the foreign key naming convention, we need to set `modelName` and `foreignKey` --->
  8. <cfset belongsTo(name="bookWriter", modelName="author", foreignKey="authorId")>
  9. '
  10. categories="model-initialization,associations" chapters="associations" functions="hasOne,hasMany">
  11. <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).">
  12. <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).">
  13. <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).">
  14. <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).">
  15. <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`).">
  16. <cfscript>
  17. $args(name="belongsTo", args=arguments);
  18. // deprecate the class argument (change of name only)
  19. if (StructKeyExists(arguments, "class"))
  20. {
  21. $deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
  22. arguments.modelName = arguments.class;
  23. StructDelete(arguments, "class");
  24. }
  25. arguments.type = "belongsTo";
  26. arguments.methods = "#arguments.name#,has#capitalize(arguments.name)#";
  27. $registerAssociation(argumentCollection=arguments);
  28. </cfscript>
  29. </cffunction>
  30. <cffunction name="hasMany" returntype="void" access="public" output="false" hint="Sets up a `hasMany` association between this model and the specified one."
  31. examples=
  32. '
  33. <!---
  34. Example1: Specify that instances of this model has many comments.
  35. (The table for the associated model, not the current, should have the foreign key set on it.)
  36. --->
  37. <cfset hasMany("comments")>
  38. <!---
  39. 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.
  40. (Useful when dealing with many-to-many relationships.)
  41. --->
  42. <cfset hasMany(name="subscriptions", shortcut="publications")>
  43. <!--- Example 3: Automatically delete all associated `comments` whenever this object is deleted --->
  44. <cfset hasMany(name="comments", dependent="deleteAll")>
  45. <!---
  46. Example 4: When not following Wheels naming conventions for associations, it can get complex to define how a `shortcut` works.
  47. In this example, we are naming our `shortcut` differently than the actual model''s name.
  48. --->
  49. <!--- In the models/Customer.cfc `init()` method --->
  50. <cfset hasMany(name="subscriptions", shortcut="magazines", through="publication,subscriptions")>
  51. <!--- In the models/Subscriptions.cfc `init()` method --->
  52. <cfset belongsTo("customer")>
  53. <cfset belongsTo("publication")>
  54. <!--- In the models/Publication `init()` method --->
  55. <cfset hasMany("subscriptions")>
  56. '
  57. categories="model-initialization,associations" chapters="associations" functions="belongsTo,hasOne">
  58. <cfargument name="name" type="string" required="true" hint="See documentation for @belongsTo.">
  59. <cfargument name="modelName" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  60. <cfargument name="foreignKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  61. <cfargument name="joinKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  62. <cfargument name="joinType" type="string" required="false" hint="See documentation for @belongsTo.">
  63. <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.">
  64. <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.">
  65. <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.">
  66. <cfscript>
  67. var singularizeName = capitalize(singularize(arguments.name));
  68. var capitalizeName = capitalize(arguments.name);
  69. $args(name="hasMany", args=arguments);
  70. // deprecate the class argument (change of name only)
  71. if (StructKeyExists(arguments, "class"))
  72. {
  73. $deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
  74. arguments.modelName = arguments.class;
  75. StructDelete(arguments, "class");
  76. }
  77. arguments.type = "hasMany";
  78. arguments.methods = "#arguments.name#,#singularizeName#Count,add#singularizeName#,create#singularizeName#,delete#singularizeName#,deleteAll#capitalizeName#,findOne#singularizeName#,has#capitalizeName#,new#singularizeName#,remove#singularizeName#,removeAll#capitalizeName#";
  79. $registerAssociation(argumentCollection=arguments);
  80. </cfscript>
  81. </cffunction>
  82. <cffunction name="hasOne" returntype="void" access="public" output="false" hint="Sets up a `hasOne` association between this model and the specified one."
  83. examples=
  84. '
  85. <!--- 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.) --->
  86. <cfset hasOne("profile")>
  87. <!--- Same as above but setting the `joinType` to `inner`, which basically means this model should always have a record in the `profiles` table --->
  88. <cfset hasOne(name="profile", joinType="inner")>
  89. <!--- Automatically delete the associated `profile` whenever this object is deleted --->
  90. <cfset hasMany(name="comments", dependent="delete")>
  91. '
  92. categories="model-initialization,associations" chapters="associations" functions="belongsTo,hasMany">
  93. <cfargument name="name" type="string" required="true" hint="See documentation for @belongsTo.">
  94. <cfargument name="modelName" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  95. <cfargument name="foreignKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  96. <cfargument name="joinKey" type="string" required="false" default="" hint="See documentation for @belongsTo.">
  97. <cfargument name="joinType" type="string" required="false" hint="See documentation for @belongsTo.">
  98. <cfargument name="dependent" type="string" required="false" hint="See documentation for @hasMany.">
  99. <cfscript>
  100. var capitalizeName = capitalize(arguments.name);
  101. $args(name="hasOne", args=arguments);
  102. // deprecate the class argument (change of name only)
  103. if (StructKeyExists(arguments, "class"))
  104. {
  105. $deprecated("The `class` argument will be deprecated in a future version of Wheels, please use the `modelName` argument instead");
  106. arguments.modelName = arguments.class;
  107. StructDelete(arguments, "class");
  108. }
  109. arguments.type = "hasOne";
  110. arguments.methods = "#arguments.name#,create#capitalizeName#,delete#capitalizeName#,has#capitalizeName#,new#capitalizeName#,remove#capitalizeName#,set#capitalizeName#";
  111. $registerAssociation(argumentCollection=arguments);
  112. </cfscript>
  113. </cffunction>
  114. <!--- PRIVATE MODEL INITIALIZATION METHODS --->
  115. <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.">
  116. <cfscript>
  117. // assign the name for the association
  118. var associationName = arguments.name;
  119. // default our nesting to false and set other nesting properties
  120. arguments.nested = {};
  121. arguments.nested.allow = false;
  122. arguments.nested.delete = false;
  123. arguments.nested.autosave = false;
  124. arguments.nested.sortProperty = "";
  125. arguments.nested.rejectIfBlank = "";
  126. // remove the name argument from the arguments struct
  127. structDelete(arguments, "name", false);
  128. // infer model name and foreign key from association name unless developer specified it already
  129. if (!Len(arguments.modelName))
  130. {
  131. if (arguments.type == "hasMany")
  132. arguments.modelName = singularize(associationName);
  133. else
  134. arguments.modelName = associationName;
  135. }
  136. // store all the settings for the association in the class struct (one struct per association with the name of the association as the key)
  137. variables.wheels.class.associations[associationName] = arguments;
  138. </cfscript>
  139. </cffunction>
  140. <cffunction name="$deleteDependents" returntype="void" access="public" output="false">
  141. <cfscript>
  142. var loc = {};
  143. for (loc.key in variables.wheels.class.associations)
  144. {
  145. if (ListFindNoCase("hasMany,hasOne", variables.wheels.class.associations[loc.key].type) && variables.wheels.class.associations[loc.key].dependent != false)
  146. {
  147. loc.all = "";
  148. if (variables.wheels.class.associations[loc.key].type == "hasMany")
  149. loc.all = "All";
  150. switch(variables.wheels.class.associations[loc.key].dependent)
  151. {
  152. case "delete":
  153. {
  154. loc.invokeArgs = {};
  155. loc.invokeArgs.instantiate = true;
  156. $invoke(componentReference=this, method="delete#loc.all##loc.key#", invokeArgs=loc.invokeArgs);
  157. break;
  158. }
  159. case "remove":
  160. {
  161. loc.invokeArgs = {};
  162. loc.invokeArgs.instantiate = true;
  163. $invoke(componentReference=this, method="remove#loc.all##loc.key#", invokeArgs=loc.invokeArgs);
  164. break;
  165. }
  166. case "deleteAll":
  167. {
  168. $invoke(componentReference=this, method="delete#loc.all##loc.key#");
  169. break;
  170. }
  171. case "removeAll":
  172. {
  173. $invoke(componentReference=this, method="remove#loc.all##loc.key#");
  174. break;
  175. }
  176. default:
  177. {
  178. $throw(type="Wheels.InvalidArgument", message="'#variables.wheels.class.associations[loc.key].dependent#' is not a valid dependency.", extendedInfo="Use `delete`, `deleteAll`, `removeAll` or false.");
  179. }
  180. }
  181. }
  182. }
  183. </cfscript>
  184. </cffunction>