1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
*## TbRelationalColumn class file |
4
|
|
|
* |
5
|
|
|
* @author: antonio ramirez <[email protected]> |
6
|
|
|
* @copyright Copyright © Clevertech 2012- |
7
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
*## TbRelationalColumn class |
12
|
|
|
* |
13
|
|
|
* Displays a clickable column that will make an ajax request and display its resulting data |
14
|
|
|
* into a new row. |
15
|
|
|
* |
16
|
|
|
* @package booster.widgets.grids.columns |
17
|
|
|
*/ |
18
|
|
|
class TbRelationalColumn extends TbDataColumn { |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var string $url the route to call via AJAX to get the data from |
22
|
|
|
*/ |
23
|
|
|
public $url; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var string $cssClass the class name that will wrap up the cell content. |
27
|
|
|
* Important Note: this class will be used as the trigger for the AJAX call, so make sure is unique for the |
28
|
|
|
* column. |
29
|
|
|
*/ |
30
|
|
|
public $cssClass = 'tbrelational-column'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var bool $cacheData if set to true, there won't be more than one AJAX request. If set to false, the widget will |
34
|
|
|
* continuously make AJAX requests. This is useful if the data could vary. If the data doesn't change then is better |
35
|
|
|
* to set it to true. Defaults to true. |
36
|
|
|
*/ |
37
|
|
|
public $cacheData = true; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var string|CJavaScriptExpression a javascript function that will be invoked if an AJAX call occurs. |
41
|
|
|
* |
42
|
|
|
* The function signature is <code>function(tr, rowid, data)</code> |
43
|
|
|
* <ul> |
44
|
|
|
* <li><code>tr</code> is the newly created TR HTML object that will display the returned server data.</li> |
45
|
|
|
* <li><code>rowid</code> the model id of the row.</li> |
46
|
|
|
* <li><code>data</code> is the data returned by the server that is already displayed on the row.</li> |
47
|
|
|
* </ul> |
48
|
|
|
* Note: This handler is not called for JSONP requests. |
49
|
|
|
* |
50
|
|
|
* Example (add in a call to TbRelationalColumn): |
51
|
|
|
* <pre> |
52
|
|
|
* ... |
53
|
|
|
* 'afterAjaxUpdate'=>'js:function(tr,rowid, data){ console.log(rowid); }', |
54
|
|
|
* ... |
55
|
|
|
* </pre> |
56
|
|
|
*/ |
57
|
|
|
public $afterAjaxUpdate; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var string $ajaxErrorMessage the message that is displayed on the newly created row in case there is an AJAX |
61
|
|
|
* error. |
62
|
|
|
*/ |
63
|
|
|
public $ajaxErrorMessage = 'Error'; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @var array $submitData allows you to merge extra data into the query string being sent to the server. |
67
|
|
|
* normally the row id is sent as 'id' |
68
|
|
|
*/ |
69
|
|
|
public $submitData=array(); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* widget initialization |
73
|
|
|
*/ |
74
|
|
|
public function init() { |
75
|
|
|
|
76
|
|
|
parent::init(); |
77
|
|
|
|
78
|
|
|
if (empty($this->url)) |
79
|
|
|
$this->url = Yii::app()->getRequest()->requestUri; |
80
|
|
|
|
81
|
|
|
$this->registerClientScript(); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Overrides CDataColumn renderDataCell in order to wrap up its content with the object that will be used as a |
86
|
|
|
* trigger. |
87
|
|
|
* Important: Making use of links as a content for this of column is an error. |
88
|
|
|
* |
89
|
|
|
* @param int $row |
90
|
|
|
*/ |
91
|
|
|
public function renderDataCell($row) { |
92
|
|
|
|
93
|
|
|
$data = $this->grid->dataProvider->data[$row]; |
94
|
|
|
$options = $this->htmlOptions; |
95
|
|
|
|
96
|
|
|
if ($this->cssClassExpression !== null) { |
97
|
|
|
$class = $this->evaluateExpression($this->cssClassExpression, array('row' => $row, 'data' => $data)); |
98
|
|
|
if (isset($options['class'])) { |
99
|
|
|
$options['class'] .= ' ' . $class; |
100
|
|
|
} else { |
101
|
|
|
$options['class'] = $class; |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
echo CHtml::openTag('td', $options); |
106
|
|
|
echo CHtml::openTag('span', array('class' => $this->cssClass.'_'.$this->id, 'data-rowid' => $this->getPrimaryKey($data), 'data-colid' => $this->id)); |
107
|
|
|
$this->renderDataCellContent($row, $data); |
108
|
|
|
echo CHtml::closeTag('span'); |
109
|
|
|
echo CHtml::closeTag('td'); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Helper function to return the primary key of the $data |
114
|
|
|
* * IMPORTANT: composite keys on CActiveDataProviders will return the keys joined by two dashes: `--` |
115
|
|
|
* |
116
|
|
|
* @param CActiveRecord $data |
117
|
|
|
* |
118
|
|
|
* @return null|string |
119
|
|
|
*/ |
120
|
|
|
protected function getPrimaryKey($data) { |
121
|
|
|
|
122
|
|
|
if ($this->grid->dataProvider instanceof CActiveDataProvider) { |
|
|
|
|
123
|
|
|
$key = $this->grid->dataProvider->keyAttribute === null ? $data->getPrimaryKey() |
124
|
|
|
: $data->{$this->grid->dataProvider->keyAttribute}; |
125
|
|
|
return is_array($key) ? implode('--', $key) : $key; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
if ($this->grid->dataProvider instanceof CArrayDataProvider || $this->grid->dataProvider instanceof CSqlDataProvider) { |
|
|
|
|
129
|
|
|
return is_object($data) ? $data->{$this->grid->dataProvider->keyField} |
130
|
|
|
: $data[$this->grid->dataProvider->keyField]; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
return null; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Register script that will handle its behavior |
138
|
|
|
*/ |
139
|
|
|
public function registerClientScript() { |
140
|
|
|
|
141
|
|
|
Booster::getBooster()->registerAssetCss('bootstrap-relational.css'); |
142
|
|
|
|
143
|
|
|
/** @var $cs CClientScript */ |
144
|
|
|
$cs = Yii::app()->getClientScript(); |
145
|
|
|
if ($this->afterAjaxUpdate !== null) { |
146
|
|
|
if ((!$this->afterAjaxUpdate instanceof CJavaScriptExpression) |
|
|
|
|
147
|
|
|
&& (strpos($this->afterAjaxUpdate,'js:') !== 0) |
148
|
|
|
) { |
149
|
|
|
$this->afterAjaxUpdate = new CJavaScriptExpression($this->afterAjaxUpdate); |
150
|
|
|
} |
151
|
|
|
} else { |
152
|
|
|
$this->afterAjaxUpdate = 'js:$.noop'; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
$this->ajaxErrorMessage = CHtml::encode($this->ajaxErrorMessage); |
156
|
|
|
$afterAjaxUpdate = CJavaScript::encode($this->afterAjaxUpdate); |
157
|
|
|
$span = count($this->grid->columns); |
158
|
|
|
$loadingPic = CHtml::image(Booster::getBooster()->getAssetsUrl() . '/img/loading.gif'); |
159
|
|
|
$cache = $this->cacheData ? 'true' : 'false'; |
160
|
|
|
$data = !empty($this->submitData) && is_array($this->submitData) ? $this->submitData : 'js:{}'; |
161
|
|
|
$data = CJavascript::encode($data); |
162
|
|
|
list($parentId) = explode('_',$this->id); |
163
|
|
|
$_id = $this->cssClass.'_'.$this->id; |
164
|
|
|
$js = <<<EOD |
165
|
|
|
$(document).on('click','#{$parentId} .{$_id}', function() { |
166
|
|
|
var span = $span; |
167
|
|
|
var that = $(this); |
168
|
|
|
var status = that.data('status'); |
169
|
|
|
var rowid = that.data('rowid'); |
170
|
|
|
var colid = that.data('colid'); |
171
|
|
|
var tr = $('#relatedinfo'+rowid+colid); |
172
|
|
|
var parent = that.parents('tr').eq(0); |
173
|
|
|
var afterAjaxUpdate = {$afterAjaxUpdate}; |
174
|
|
|
|
175
|
|
|
if (status && status=='on'){return} |
176
|
|
|
that.data('status','on'); |
177
|
|
|
|
178
|
|
|
if (tr.length && !tr.is(':visible') && {$cache}) |
179
|
|
|
{ |
180
|
|
|
$("tr[id^=relatedinfo"+rowid+"]").not(tr).hide(); // .slideUp(); |
181
|
|
|
tr.slideDown(); |
182
|
|
|
that.data('status','off'); |
183
|
|
|
return; |
184
|
|
|
}else if (tr.length && tr.is(':visible')) |
185
|
|
|
{ |
186
|
|
|
tr.slideUp(); |
187
|
|
|
that.data('status','off'); |
188
|
|
|
return; |
189
|
|
|
} |
190
|
|
|
if (tr.length) |
191
|
|
|
{ |
192
|
|
|
$("tr[id^=relatedinfo"+rowid+"]").not(tr).hide(); // .slideUp(); |
193
|
|
|
tr.find('td').html('{$loadingPic}'); |
194
|
|
|
if (!tr.is(':visible')){ |
195
|
|
|
tr.slideDown(); |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
else |
199
|
|
|
{ |
200
|
|
|
$("tr[id^=relatedinfo"+rowid+"]").hide(); // .slideUp(); |
201
|
|
|
var td = $('<td/>').html('{$loadingPic}').attr({'colspan':$span}); |
202
|
|
|
tr = $('<tr/>').prop({'id':'relatedinfo'+rowid+colid}).append(td); |
203
|
|
|
/* we need to maintain zebra styles :) */ |
204
|
|
|
var fake = $('<tr class="hide"/>').append($('<td/>').attr({'colspan':$span})); |
205
|
|
|
parent.after(tr); |
206
|
|
|
tr.after(fake); |
207
|
|
|
} |
208
|
|
|
var data = $.extend({$data}, {id:rowid}); |
209
|
|
|
$.ajax({ |
210
|
|
|
url: '{$this->url}', |
211
|
|
|
data: data, |
212
|
|
|
success: function(data){ |
213
|
|
|
tr.find('td').html(data); |
214
|
|
|
that.data('status','off'); |
215
|
|
|
if ($.isFunction(afterAjaxUpdate)) |
216
|
|
|
{ |
217
|
|
|
afterAjaxUpdate(tr,rowid,data); |
218
|
|
|
} |
219
|
|
|
}, |
220
|
|
|
error: function() |
221
|
|
|
{ |
222
|
|
|
tr.find('td').html('{$this->ajaxErrorMessage}'); |
223
|
|
|
that.data('status','off'); |
224
|
|
|
} |
225
|
|
|
}); |
226
|
|
|
}); |
227
|
|
|
EOD; |
228
|
|
|
$cs->registerScript(__CLASS__ . '#' . $this->id, $js); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.