1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Undocumented. |
4
|
|
|
*/ |
5
|
|
|
class Field_Reference extends Field |
6
|
|
|
{ |
7
|
|
|
/** @var string */ |
8
|
|
|
public $model_name = null; |
9
|
|
|
|
10
|
|
|
/** @var string */ |
11
|
|
|
public $display_field = null; |
12
|
|
|
|
13
|
|
|
/** @var string */ |
14
|
|
|
public $dereferenced_field = null; |
15
|
|
|
|
16
|
|
|
/** @var string */ |
17
|
|
|
public $table_alias = null; |
18
|
|
|
|
19
|
|
|
/** @var Model */ |
20
|
|
|
public $model; |
21
|
|
|
|
22
|
|
|
|
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Set model |
26
|
|
|
* |
27
|
|
|
* @param Model|string $model |
28
|
|
|
* @param string|bool $display_field |
29
|
|
|
* |
30
|
|
|
* @return Model|$this |
31
|
|
|
*/ |
32
|
|
|
public function setModel($model, $display_field = null) |
33
|
|
|
{ |
34
|
|
|
if ($model instanceof Model) { |
35
|
|
|
return AbstractObject::setModel($model); |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
$this->model_name = is_string($model) ? $model : get_class($model); |
39
|
|
|
$this->model_name = (string) $this->app->normalizeClassName($this->model_name, 'Model'); |
40
|
|
|
|
41
|
|
|
if ($display_field) { |
42
|
|
|
$this->display_field = (string) $display_field; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
if ($display_field !== false) { |
46
|
|
|
$this->owner->addExpression($this->getDereferenced()) |
47
|
|
|
->set(array($this, 'calculateSubQuery'))->caption($this->caption()); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
$this->system(true); |
51
|
|
|
$this->editable(true); |
52
|
|
|
$this->visible(false); |
53
|
|
|
|
54
|
|
|
return $this; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Return model of field |
59
|
|
|
* |
60
|
|
|
* @return Model |
61
|
|
|
*/ |
62
|
|
|
public function getModel() |
63
|
|
|
{ |
64
|
|
|
if (!$this->model) { |
65
|
|
|
$this->model = $this->add($this->model_name); |
66
|
|
|
} |
67
|
|
|
if ($this->display_field) { |
68
|
|
|
$this->model->title_field = $this->display_field; |
|
|
|
|
69
|
|
|
} |
70
|
|
|
if ($this->table_alias) { |
71
|
|
|
$this->model->table_alias = $this->table_alias; |
|
|
|
|
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
return $this->model; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
View Code Duplication |
public function sortable($x = undefined) |
78
|
|
|
{ |
79
|
|
|
/** @var Field|bool */ |
80
|
|
|
$f = $this->owner->hasElement($this->getDereferenced()); |
81
|
|
|
if ($f) { |
82
|
|
|
$f->sortable($x); |
|
|
|
|
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
return parent::sortable($x); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
View Code Duplication |
public function caption($x = undefined) |
89
|
|
|
{ |
90
|
|
|
/** @var Field|bool */ |
91
|
|
|
$f = $this->owner->hasElement($this->getDereferenced()); |
92
|
|
|
if ($f) { |
93
|
|
|
$f->caption($x); |
|
|
|
|
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return parent::caption($x); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* ref() will traverse reference and will attempt to load related model's entry. If the entry will fail to load |
101
|
|
|
* it will return model which would not be loaded. This can be changed by specifying an argument:. |
102
|
|
|
* |
103
|
|
|
* 'model' - simply create new model and return it without loading anything |
104
|
|
|
* false or 'ignore' - will not even try to load anything |
105
|
|
|
* null (default) - will tryLoad() |
106
|
|
|
* 'load' - will always load the model and if record is not present, will fail |
107
|
|
|
* 'create' - if record fails to load, will create new record, save, get ID and insert into $this |
108
|
|
|
* 'link' - if record fails to load, will return new record, with appropriate afterSave hander, which will |
109
|
|
|
* update current model also and save it too. |
110
|
|
|
* |
111
|
|
|
* @param string|bool|null $mode |
112
|
|
|
* |
113
|
|
|
* @return Model |
114
|
|
|
*/ |
115
|
|
|
public function ref($mode = null) |
116
|
|
|
{ |
117
|
|
|
if ($mode == 'model') { |
118
|
|
|
return $this->add($this->model_name); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$this->getModel()->unload(); |
122
|
|
|
|
123
|
|
|
if ($mode === false || $mode == 'ignore') { |
124
|
|
|
return $this->model; |
125
|
|
|
} |
126
|
|
|
if ($mode == 'load') { |
127
|
|
|
return $this->model->load($this->get()); |
128
|
|
|
} |
129
|
|
|
if ($mode === null) { |
130
|
|
|
if ($this->get()) { |
131
|
|
|
$this->model->tryLoad($this->get()); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return $this->model; |
135
|
|
|
} |
136
|
|
|
if ($mode == 'create') { |
137
|
|
|
if ($this->get()) { |
138
|
|
|
$this->model->tryLoad($this->get()); |
139
|
|
|
} |
140
|
|
|
if (!$this->model->loaded()) { |
141
|
|
|
$this->model->save(); |
142
|
|
|
$this->set($this->model->id); |
143
|
|
|
$this->owner->save(); |
144
|
|
|
|
145
|
|
|
return $this->model; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
if ($mode == 'link') { |
149
|
|
|
/** @var Model */ |
150
|
|
|
$m = $this->add($this->model_name); |
151
|
|
|
if ($this->get()) { |
152
|
|
|
$m->tryLoad($this->get()); |
|
|
|
|
153
|
|
|
} |
154
|
|
|
$t = $this; |
155
|
|
|
if (!$m->loaded()) { |
|
|
|
|
156
|
|
|
$m->addHook('afterSave', function ($m) use ($t) { |
157
|
|
|
$t->set($m->id); |
158
|
|
|
$t->owner->saveLater(); |
159
|
|
|
}); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
return $m; |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Return DSQL for field |
168
|
|
|
* |
169
|
|
|
* @return SQL_Model |
170
|
|
|
*/ |
171
|
|
|
public function refSQL() |
172
|
|
|
{ |
173
|
|
|
/** @var SQL_Model $q */ |
174
|
|
|
$q = $this->ref('model'); |
175
|
|
|
$q->addCondition($q->id_field, $this); |
176
|
|
|
|
177
|
|
|
return $q; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Return name of dereferenced field |
182
|
|
|
* |
183
|
|
|
* @return string |
184
|
|
|
*/ |
185
|
|
|
public function getDereferenced() |
186
|
|
|
{ |
187
|
|
|
if ($this->dereferenced_field) { |
188
|
|
|
return $this->dereferenced_field; |
189
|
|
|
} |
190
|
|
|
$f = preg_replace('/_id$/', '', $this->short_name); |
191
|
|
|
if ($f != $this->short_name) { |
192
|
|
|
return $f; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$f = $this->short_name.'_text'; |
196
|
|
|
if ($this->owner->hasElement($f)) { |
197
|
|
|
return $f; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
$f = $this->_unique($this->owner->elements, $f); |
201
|
|
|
$this->dereferenced_field = $f; |
202
|
|
|
|
203
|
|
|
return $f; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Destroy this field and dereferenced field. |
208
|
|
|
* |
209
|
|
|
* @return $this |
210
|
|
|
*/ |
211
|
|
|
public function destroy() |
212
|
|
|
{ |
213
|
|
|
if ($e = $this->owner->hasElement($this->getDereferenced())) { |
214
|
|
|
$e->destroy(); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return parent::destroy(); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @return string |
222
|
|
|
*/ |
223
|
|
|
public function calculateSubQuery() |
224
|
|
|
{ |
225
|
|
|
if (!$this->model) { |
226
|
|
|
$this->getModel(); //$this->model=$this->add($this->model_name); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
if ($this->display_field) { |
230
|
|
|
/** @var SQL_Model $this->model */ |
231
|
|
|
$title = $this->model->dsql()->del('fields'); |
232
|
|
|
$this->model->getElement($this->display_field)->updateSelectQuery($title); |
|
|
|
|
233
|
|
|
} elseif ($this->model->hasMethod('titleQuery')) { |
234
|
|
|
/** @var SQL_Model $this->model */ |
235
|
|
|
$title = $this->model->titleQuery(); |
236
|
|
|
} else { |
237
|
|
|
// possibly references non-sql model, so just display field value |
238
|
|
|
return $this->owner->dsql()->bt($this->short_name); |
239
|
|
|
} |
240
|
|
|
$title->del('order') |
241
|
|
|
->where($this, $title->getField($this->model->id_field)); |
242
|
|
|
|
243
|
|
|
return $title; |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.