PageRenderTime 63ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/data/model/Relationship.php

https://bitbucket.org/d1rk/lithium
PHP | 162 lines | 70 code | 19 blank | 73 comment | 8 complexity | 9d0775117cc5b696f2be3164dd512040 MD5 | raw file
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\data\model;
  9. use lithium\core\Libraries;
  10. use lithium\util\Inflector;
  11. use lithium\core\ClassNotFoundException;
  12. /**
  13. * The `Relationship` class encapsulates the data and functionality necessary to link two model
  14. * classes together.
  15. */
  16. class Relationship extends \lithium\core\Object {
  17. /**
  18. * A relationship linking type defined by one document or record (or multiple) being embedded
  19. * within another.
  20. */
  21. const LINK_EMBEDDED = 'embedded';
  22. /**
  23. * The reciprocal of `LINK_EMBEDDED`, this defines a linking type wherein an embedded document
  24. * references the document that contains it.
  25. */
  26. const LINK_CONTAINED = 'contained';
  27. /**
  28. * A one-to-one or many-to-one relationship in which a key contains an ID value linking to
  29. * another document or record.
  30. */
  31. const LINK_KEY = 'key';
  32. /**
  33. * A many-to-many relationship in which a key contains an embedded array of IDs linking to other
  34. * records or documents.
  35. */
  36. const LINK_KEY_LIST = 'keylist';
  37. /**
  38. * A relationship defined by a database-native reference mechanism, linking a key to an
  39. * arbitrary record or document in another data collection or entirely separate database.
  40. */
  41. const LINK_REF = 'ref';
  42. /**
  43. * Constructs an object that represents a relationship between two model classes.
  44. *
  45. * @param array $config The relationship's configuration, which defines how the two models in
  46. * question are bound. The available options are:
  47. *
  48. * - `'name'` _string_: The name of the relationship in the context of the
  49. * originating model. For example, a `Posts` model might define a relationship to
  50. * a `Users` model like so:
  51. * {{{ public $hasMany = array('Author' => array('to' => 'Users')); }}}
  52. * In this case, the relationship is bound to the `Users` model, but `'Author'` would be the
  53. * relationship name. This is the name with which the relationship is referenced in the
  54. * originating model.
  55. * - `'key'` _mixed_: An array of fields that define the relationship, where the
  56. * keys are fields in the originating model, and the values are fields in the
  57. * target model. If the relationship is not deined by keys, this array should be
  58. * empty.
  59. * - `'type'` _string_: The type of relationship. Should be one of `'belongsTo'`,
  60. * `'hasOne'` or `'hasMany'`.
  61. * - `'from'` _string_: The fully namespaced class name of the model where this
  62. * relationship originates.
  63. * - `'to'` _string_: The fully namespaced class name of the model that this
  64. * relationship targets.
  65. * - `'link'` _string_: A constant specifying how the object bound to the
  66. * originating model is linked to the object bound to the target model. For
  67. * relational databases, the only valid value is `LINK_KEY`, which means a foreign
  68. * key in one object matches another key (usually the primary key) in the other.
  69. * For document-oriented and other non-relational databases, different types of
  70. * linking, including key lists, database reference objects (such as MongoDB's
  71. * `MongoDBRef`), or even embedding.
  72. * - `'fields'` _mixed_: An array of the subset of fields that should be selected
  73. * from the related object(s) by default. If set to `true` (the default), all
  74. * fields are selected.
  75. * - `'fieldName'` _string_: The name of the field used when accessing the related
  76. * data in a result set. For example, in the case of `Posts hasMany Comments`, the
  77. * field name defaults to `'comments'`, so comment data is accessed (assuming
  78. * `$post = Posts::first()`) as `$post->comments`.
  79. * - `'constraint'` _mixed_: A string or array containing additional constraints
  80. * on the relationship query. If a string, can contain a literal SQL fragment or
  81. * other database-native value. If an array, maps fields from the related object
  82. * either to fields elsewhere, or to arbitrary expressions. In either case, _the
  83. * values specified here will be literally interpreted by the database_.
  84. */
  85. public function __construct(array $config = array()) {
  86. $defaults = array(
  87. 'name' => null,
  88. 'key' => array(),
  89. 'type' => null,
  90. 'to' => null,
  91. 'from' => null,
  92. 'link' => static::LINK_KEY,
  93. 'fields' => true,
  94. 'fieldName' => null,
  95. 'constraint' => array()
  96. );
  97. parent::__construct($config + $defaults);
  98. }
  99. protected function _init() {
  100. parent::_init();
  101. $config =& $this->_config;
  102. $type = $config['type'];
  103. $name = ($type == 'hasOne') ? Inflector::pluralize($config['name']) : $config['name'];
  104. $config['fieldName'] = $config['fieldName'] ?: lcfirst($name);
  105. if (!$config['to']) {
  106. $assoc = preg_replace("/\\w+$/", "", $config['from']) . $name;
  107. $config['to'] = Libraries::locate('models', $assoc);
  108. }
  109. if (!$config['key'] || !is_array($config['key'])) {
  110. $config['key'] = $this->_keys($config['key']);
  111. }
  112. }
  113. public function data($key = null) {
  114. if (!$key) {
  115. return $this->_config;
  116. }
  117. return isset($this->_config[$key]) ? $this->_config[$key] : null;
  118. }
  119. public function constraints() {
  120. $constraints = array();
  121. $config = $this->_config;
  122. $relFrom = $config['from']::meta('name');
  123. $relTo = $config['name'];
  124. foreach ($this->_config['key'] as $from => $to) {
  125. $constraints["{$relFrom}.{$from}"] = "{$relTo}.{$to}";
  126. }
  127. return $constraints + (array) $this->_config['constraint'];
  128. }
  129. public function __call($name, $args = array()) {
  130. return $this->data($name);
  131. }
  132. protected function _keys($keys) {
  133. $config = $this->_config;
  134. if (!($related = ($config['type'] == 'belongsTo') ? $config['to'] : $config['from'])) {
  135. return array();
  136. }
  137. if (class_exists($related)) {
  138. return array_combine((array) $keys, (array) $related::key());
  139. }
  140. throw new ClassNotFoundException("Related model class '{$related}' not found.");
  141. }
  142. }
  143. ?>