1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
<?php
/**
* TActiveRecordRelationContext class.
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2014 PradoSoft
* @license http://www.pradosoft.com/license/
* @package Prado\Data\ActiveRecord\Relations
*/
namespace Prado\Data\ActiveRecord\Relations;
use Prado\Collections\TList;
use Prado\Data\ActiveRecord\Exceptions\TActiveRecordException;
use Prado\Data\ActiveRecord\TActiveRecord;
use Prado\Data\ActiveRecord\TActiveRecordCriteria;
use Prado\Prado;
/**
* TActiveRecordRelationContext holds information regarding record relationships
* such as record relation property name, query criteria and foreign object record
* class names.
*
* This class is use internally by passing a context to the TActiveRecordRelation
* constructor.
*
* @author Wei Zhuo <weizho[at]gmail[dot]com>
* @package Prado\Data\ActiveRecord\Relations
* @since 3.1
*/
class TActiveRecordRelationContext
{
private $_property;
private $_record;
private $_relation; //data from an entry of TActiveRecord::$RELATION
private $_fkeys;
public function __construct($record, $property=null, $relation=null)
{
$this->_record=$record;
$this->_property=$property;
$this->_relation=$relation;
}
/**
* @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
* @since 3.1.2
*/
public function hasRecordRelation()
{
return $this->_relation!==null;
}
public function getPropertyValue()
{
$obj = $this->getSourceRecord();
return $obj->getColumnValue($this->getProperty());
}
/**
* @return string name of the record property that the relationship results will be assigned to.
*/
public function getProperty()
{
return $this->_property;
}
/**
* @return TActiveRecord the active record instance that queried for its related records.
*/
public function getSourceRecord()
{
return $this->_record;
}
/**
* @return array foreign key of this relations, the keys is dependent on the
* relationship type.
* @since 3.1.2
*/
public function getRelationForeignKeys()
{
if($this->_fkeys===null)
$this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
return $this->_fkeys;
}
/**
* @return string HAS_MANY, HAS_ONE, or BELONGS_TO
*/
public function getRelationType()
{
return $this->_relation[0];
}
/**
* @return string foreign record class name.
*/
public function getForeignRecordClass()
{
return $this->_relation[1];
}
/**
* @return string foreign key field names, comma delimited.
* @since 3.1.2
*/
public function getFkField()
{
return $this->_relation[2];
}
/**
* @return string the query condition for the relation as specified in RELATIONS
* @since 3.1.2
*/
public function getCondition()
{
return isset($this->_relation[3])?$this->_relation[3]:null;
}
/**
* @return array the query parameters for the relation as specified in RELATIONS
* @since 3.1.2
*/
public function getParameters()
{
return isset($this->_relation[4])?$this->_relation[4]:array();
}
/**
* @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
* @since 3.1.2
*/
public function hasFkField()
{
$notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
}
/**
* @return string the M-N relationship association table name.
*/
public function getAssociationTable()
{
return $this->_relation[2];
}
/**
* @return boolean true if the relationship is HAS_MANY and requires an association table.
*/
public function hasAssociationTable()
{
$isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
}
/**
* @return TActiveRecord corresponding relationship foreign object finder instance.
*/
public function getForeignRecordFinder()
{
return TActiveRecord::finder($this->getForeignRecordClass());
}
/**
* Creates and return the TActiveRecordRelation handler for specific relationships.
* An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
* or TActiveRecordHasManyAssocation will be returned.
* @param TActiveRecordCriteria search criteria
* @return TActiveRecordRelation record relationship handler instnace.
* @throws TActiveRecordException if property is not defined or missing.
*/
public function getRelationHandler($criteria=null)
{
if(!$this->hasRecordRelation())
{
throw new TActiveRecordException('ar_undefined_relation_prop',
$this->_property, get_class($this->_record), 'RELATIONS');
}
if($criteria===null)
$criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
switch($this->getRelationType())
{
case TActiveRecord::HAS_MANY:
Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
return new TActiveRecordHasMany($this, $criteria);
case TActiveRecord::MANY_TO_MANY:
Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
return new TActiveRecordHasManyAssociation($this, $criteria);
case TActiveRecord::HAS_ONE:
Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
return new TActiveRecordHasOne($this, $criteria);
case TActiveRecord::BELONGS_TO:
Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
return new TActiveRecordBelongsTo($this, $criteria);
default:
throw new TActiveRecordException('ar_invalid_relationship');
}
}
/**
* @return TActiveRecordRelationCommand
*/
public function updateAssociatedRecords($updateBelongsTo=false)
{
$success=true;
foreach($this->_record->getRecordRelations() as $data)
{
list($property, $relation) = $data;
$belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
{
$obj = $this->getSourceRecord();
if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
{
$context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
$success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
}
}
}
return $success;
}
protected function isEmptyFkObject($obj)
{
if(is_object($obj))
return $obj instanceof TList ? $obj->count() === 0 : false;
else if(is_array($obj))
return count($obj)===0;
else
return empty($obj);
}
}
|