Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TDataBoundControl class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TDataBoundControl.php 2492 2008-08-09 01:30:05Z knut $
10
 * @package System.Web.UI.WebControls
11
 */
12
 
13
Prado::using('System.Web.UI.WebControls.TDataSourceControl');
14
Prado::using('System.Web.UI.WebControls.TDataSourceView');
15
Prado::using('System.Collections.TPagedDataSource');
16
 
17
/**
18
 * TDataBoundControl class.
19
 *
20
 * TDataBoundControl is the based class for controls that need to populate
21
 * data from data sources. It provides basic properties and methods that allow
22
 * the derived controls to associate with data sources and retrieve data from them.
23
 *
24
 * TBC....
25
 *
26
 * TDataBoundControl is equipped with paging capabilities. By setting
27
 * {@link setAllowPaging AllowPaging} to true, the input data will be paged
28
 * and only one page of data is actually populated into the data-bound control.
29
 * This saves a lot of memory when dealing with larget datasets.
30
 *
31
 * To specify the number of data items displayed on each page, set
32
 * the {@link setPageSize PageSize} property, and to specify which
33
 * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}.
34
 *
35
 * When the size of the original data is too big to be loaded all in the memory,
36
 * one can enable custom paging. In custom paging, the total number of data items
37
 * is specified manually via {@link setVirtualItemCount VirtualItemCount},
38
 * and the data source only needs to contain the current page of data. To enable
39
 * custom paging, set {@link setAllowCustomPaging AllowCustomPaging} to true.
40
 *
41
 * @author Qiang Xue <qiang.xue@gmail.com>
42
 * @version $Id: TDataBoundControl.php 2492 2008-08-09 01:30:05Z knut $
43
 * @package System.Web.UI.WebControls
44
 * @since 3.0
45
 */
46
abstract class TDataBoundControl extends TWebControl
47
{
48
	private $_initialized=false;
49
	private $_dataSource=null;
50
	private $_requiresBindToNull=false;
51
	private $_requiresDataBinding=false;
52
	private $_prerendered=false;
53
	private $_currentView=null;
54
	private $_currentDataSource=null;
55
	private $_currentViewValid=false;
56
	private $_currentDataSourceValid=false;
57
	private $_currentViewIsFromDataSourceID=false;
58
	private $_parameters=null;
59
	private $_isDataBound=false;
60
 
61
	/**
62
	 * @return Traversable data source object, defaults to null.
63
	 */
64
	public function getDataSource()
65
	{
66
		return $this->_dataSource;
67
	}
68
 
69
	/**
70
	 * Sets the data source object associated with the databound control.
71
	 * The data source must implement Traversable interface.
72
	 * If an array is given, it will be converted to xxx.
73
	 * If a string is given, it will be converted to xxx.
74
	 * @param Traversable|array|string data source object
75
	 */
76
	public function setDataSource($value)
77
	{
78
		$this->_dataSource=$this->validateDataSource($value);
79
		$this->onDataSourceChanged();
80
	}
81
 
82
	/**
83
	 * @return string ID path to the data source control. Defaults to empty.
84
	 */
85
	public function getDataSourceID()
86
	{
87
		return $this->getViewState('DataSourceID','');
88
	}
89
 
90
	/**
91
	 * @param string ID path to the data source control. The data source
92
	 * control must be locatable via {@link TControl::findControl} call.
93
	 */
94
	public function setDataSourceID($value)
95
	{
96
		$dsid=$this->getViewState('DataSourceID','');
97
		if($dsid!=='' && $value==='')
98
			$this->_requiresBindToNull=true;
99
		$this->setViewState('DataSourceID',$value,'');
100
		$this->onDataSourceChanged();
101
	}
102
 
103
	/**
104
	 * @return boolean if the databound control uses the data source specified
105
	 * by {@link setDataSourceID}, or it uses the data source object specified
106
	 * by {@link setDataSource}.
107
	 */
108
	protected function getUsingDataSourceID()
109
	{
110
		return $this->getDataSourceID()!=='';
111
	}
112
 
113
	/**
114
	 * Sets {@link setRequiresDataBinding RequiresDataBinding} as true if the control is initialized.
115
	 * This method is invoked when either {@link setDataSource} or {@link setDataSourceID} is changed.
116
	 */
117
	public function onDataSourceChanged()
118
	{
119
		$this->_currentViewValid=false;
120
		$this->_currentDataSourceValid=false;
121
		if($this->getInitialized())
122
			$this->setRequiresDataBinding(true);
123
	}
124
 
125
	/**
126
	 * @return boolean whether the databound control has been initialized.
127
	 * By default, the control is initialized after its viewstate has been restored.
128
	 */
129
	protected function getInitialized()
130
	{
131
		return $this->_initialized;
132
	}
133
 
134
	/**
135
	 * Sets a value indicating whether the databound control is initialized.
136
	 * If initialized, any modification to {@link setDataSource DataSource} or
137
	 * {@link setDataSourceID DataSourceID} will set {@link setRequiresDataBinding RequiresDataBinding}
138
	 * as true.
139
	 * @param boolean a value indicating whether the databound control is initialized.
140
	 */
141
	protected function setInitialized($value)
142
	{
143
		$this->_initialized=TPropertyValue::ensureBoolean($value);
144
	}
145
 
146
	/**
147
	 * @return boolean whether databind has been invoked in the previous page request
148
	 */
149
	protected function getIsDataBound()
150
	{
151
		return $this->_isDataBound;
152
	}
153
 
154
	/**
155
	 * @param boolean if databind has been invoked in this page request
156
	 */
157
	protected function setIsDataBound($value)
158
	{
159
		$this->_isDataBound=$value;
160
	}
161
 
162
	/**
163
	 * @return boolean whether a databind call is required (by the data bound control)
164
	 */
165
	protected function getRequiresDataBinding()
166
	{
167
		return $this->_requiresDataBinding;
168
	}
169
 
170
	/**
171
	 * @return boolean whether paging is enabled. Defaults to false.
172
	 */
173
	public function getAllowPaging()
174
	{
175
		return $this->getViewState('AllowPaging',false);
176
	}
177
 
178
	/**
179
	 * @param boolean whether paging is enabled
180
	 */
181
	public function setAllowPaging($value)
182
	{
183
		$this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false);
184
	}
185
 
186
	/**
187
	 * @return boolean whether the custom paging is enabled. Defaults to false.
188
	 */
189
	public function getAllowCustomPaging()
190
	{
191
		return $this->getViewState('AllowCustomPaging',false);
192
	}
193
 
194
	/**
195
	 * Sets a value indicating whether the custom paging should be enabled.
196
	 * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount}
197
	 * property is used to determine the paging, and the data items in the
198
	 * {@link setDataSource DataSource} are considered to be in the current page.
199
	 * @param boolean whether the custom paging is enabled
200
	 */
201
	public function setAllowCustomPaging($value)
202
	{
203
		$this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false);
204
	}
205
 
206
	/**
207
	 * @return integer the zero-based index of the current page. Defaults to 0.
208
	 */
209
	public function getCurrentPageIndex()
210
	{
211
		return $this->getViewState('CurrentPageIndex',0);
212
	}
213
 
214
	/**
215
	 * @param integer the zero-based index of the current page
216
	 * @throws TInvalidDataValueException if the value is less than 0
217
	 */
218
	public function setCurrentPageIndex($value)
219
	{
220
		if(($value=TPropertyValue::ensureInteger($value))<0)
221
			$value=0;
222
		$this->setViewState('CurrentPageIndex',$value,0);
223
	}
224
 
225
	/**
226
	 * @return integer the number of data items on each page. Defaults to 10.
227
	 */
228
	public function getPageSize()
229
	{
230
		return $this->getViewState('PageSize',10);
231
	}
232
 
233
	/**
234
	 * @param integer the number of data items on each page.
235
	 * @throws TInvalidDataValueException if the value is less than 1
236
	 */
237
	public function setPageSize($value)
238
	{
239
		if(($value=TPropertyValue::ensureInteger($value))<1)
240
			throw new TInvalidDataValueException('databoundcontrol_pagesize_invalid',get_class($this));
241
		$this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10);
242
	}
243
 
244
	/**
245
	 * @return integer number of pages of data items available
246
	 */
247
	public function getPageCount()
248
	{
249
		return $this->getViewState('PageCount',1);
250
	}
251
 
252
	/**
253
	 * @return integer virtual number of data items in the data source. Defaults to 0.
254
	 * @see setAllowCustomPaging
255
	 */
256
	public function getVirtualItemCount()
257
	{
258
		return $this->getViewState('VirtualItemCount',0);
259
	}
260
 
261
	/**
262
	 * @param integer virtual number of data items in the data source.
263
	 * @throws TInvalidDataValueException if the value is less than 0
264
	 * @see setAllowCustomPaging
265
	 */
266
	public function setVirtualItemCount($value)
267
	{
268
		if(($value=TPropertyValue::ensureInteger($value))<0)
269
			throw new TInvalidDataValueException('databoundcontrol_virtualitemcount_invalid',get_class($this));
270
		$this->setViewState('VirtualItemCount',$value,0);
271
	}
272
 
273
	/**
274
	 * Sets a value indicating whether a databind call is required by the data bound control.
275
	 * If true and the control has been prerendered while it uses the data source
276
	 * specified by {@link setDataSourceID}, a databind call will be called by this method.
277
	 * @param boolean whether a databind call is required.
278
	 */
279
	protected function setRequiresDataBinding($value)
280
	{
281
		$value=TPropertyValue::ensureBoolean($value);
282
		if($value && $this->_prerendered)
283
		{
284
			$this->_requiresDataBinding=true;
285
			$this->ensureDataBound();
286
		}
287
		else
288
			$this->_requiresDataBinding=$value;
289
	}
290
 
291
	/**
292
	 * Ensures any pending {@link dataBind} is called.
293
	 * This method calls {@link dataBind} if the data source is specified
294
	 * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding}
295
	 * is true.
296
	 */
297
	protected function ensureDataBound()
298
	{
299
		if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull))
300
		{
301
			$this->dataBind();
302
			$this->_requiresBindToNull=false;
303
		}
304
	}
305
 
306
	/**
307
	 * @return TPagedDataSource creates a paged data source
308
	 */
309
	protected function createPagedDataSource()
310
	{
311
		$ds=new TPagedDataSource;
312
		$ds->setCurrentPageIndex($this->getCurrentPageIndex());
313
		$ds->setPageSize($this->getPageSize());
314
		$ds->setAllowPaging($this->getAllowPaging());
315
		$ds->setAllowCustomPaging($this->getAllowCustomPaging());
316
		$ds->setVirtualItemCount($this->getVirtualItemCount());
317
		return $ds;
318
	}
319
 
320
	/**
321
	 * Performs databinding.
322
	 * This method overrides the parent implementation by calling
323
	 * {@link performSelect} which fetches data from data source and does
324
	 * the actual binding work.
325
	 */
326
	public function dataBind()
327
	{
328
		$this->setRequiresDataBinding(false);
329
		$this->dataBindProperties();
330
		$this->onDataBinding(null);
331
 
332
		if(($view=$this->getDataSourceView())!==null)
333
			$data=$view->select($this->getSelectParameters());
334
		else
335
			$data=null;
336
 
337
		if($data instanceof Traversable)
338
		{
339
			if($this->getAllowPaging())
340
			{
341
				$ds=$this->createPagedDataSource();
342
				$ds->setDataSource($data);
343
				$this->setViewState('PageCount',$ds->getPageCount());
344
				if($ds->getCurrentPageIndex()>=$ds->getPageCount())
345
				{
346
					$ds->setCurrentPageIndex($ds->getPageCount()-1);
347
					$this->setCurrentPageIndex($ds->getCurrentPageIndex());
348
				}
349
				$this->performDataBinding($ds);
350
			}
351
			else
352
			{
353
				$this->clearViewState('PageCount');
354
				$this->performDataBinding($data);
355
			}
356
		}
357
		$this->setIsDataBound(true);
358
		$this->onDataBound(null);
359
	}
360
 
361
	public function dataSourceViewChanged($sender,$param)
362
	{
363
		if(!$this->_ignoreDataSourceViewChanged)
364
			$this->setRequiresDataBinding(true);
365
	}
366
 
367
	protected function getDataSourceView()
368
	{
369
		if(!$this->_currentViewValid)
370
		{
371
			if($this->_currentView && $this->_currentViewIsFromDataSourceID)
372
				$this->_currentView->detachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged'));
373
			if(($dataSource=$this->determineDataSource())!==null)
374
			{
375
				if(($view=$dataSource->getView($this->getDataMember()))===null)
376
					throw new TInvalidDataValueException('databoundcontrol_datamember_invalid',$this->getDataMember());
377
				if($this->_currentViewIsFromDataSourceID=$this->getUsingDataSourceID())
378
					$view->attachEventHandler('OnDataSourceViewChanged',array($this,'dataSourceViewChanged'));
379
				$this->_currentView=$view;
380
			}
381
			else
382
				$this->_currentView=null;
383
			$this->_currentViewValid=true;
384
		}
385
		return $this->_currentView;
386
	}
387
 
388
	protected function determineDataSource()
389
	{
390
		if(!$this->_currentDataSourceValid)
391
		{
392
			if(($dsid=$this->getDataSourceID())!=='')
393
			{
394
				if(($dataSource=$this->getNamingContainer()->findControl($dsid))===null)
395
					throw new TInvalidDataValueException('databoundcontrol_datasourceid_inexistent',$dsid);
396
				else if(!($dataSource instanceof IDataSource))
397
					throw new TInvalidDataValueException('databoundcontrol_datasourceid_invalid',$dsid);
398
				else
399
					$this->_currentDataSource=$dataSource;
400
			}
401
			else if(($dataSource=$this->getDataSource())!==null)
402
				$this->_currentDataSource=new TReadOnlyDataSource($dataSource,$this->getDataMember());
403
			else
404
				$this->_currentDataSource=null;
405
			$this->_currentDataSourceValid=true;
406
		}
407
		return $this->_currentDataSource;
408
	}
409
 
410
	abstract protected function performDataBinding($data);
411
 
412
	/**
413
	 * Raises <b>OnDataBound</b> event.
414
	 * This method should be invoked after a databind is performed.
415
	 * It is mainly used by framework and component developers.
416
	 */
417
	public function onDataBound($param)
418
	{
419
		$this->raiseEvent('OnDataBound',$this,$param);
420
	}
421
 
422
	/**
423
	 * Sets page's <b>OnPreLoad</b> event handler as {@link pagePreLoad}.
424
	 * If viewstate is disabled and the current request is a postback,
425
	 * {@link setRequiresDataBinding RequiresDataBinding} will be set true.
426
	 * This method overrides the parent implementation.
427
	 * @param TEventParameter event parameter
428
	 */
429
	public function onInit($param)
430
	{
431
		parent::onInit($param);
432
		$page=$this->getPage();
433
		$page->attachEventHandler('OnPreLoad',array($this,'pagePreLoad'));
434
	}
435
 
436
	/**
437
	 * Sets {@link getInitialized} as true.
438
	 * This method is invoked when page raises <b>PreLoad</b> event.
439
	 * @param mixed event sender
440
	 * @param TEventParameter event parameter
441
	 */
442
	public function pagePreLoad($sender,$param)
443
	{
444
		$this->_initialized=true;
445
		$isPostBack=$this->getPage()->getIsPostBack();
446
		if(!$isPostBack || ($isPostBack && (!$this->getEnableViewState(true) || !$this->getIsDataBound())))
447
			$this->setRequiresDataBinding(true);
448
	}
449
 
450
	/**
451
	 * Ensures any pending databind is performed.
452
	 * This method overrides the parent implementation.
453
	 * @param TEventParameter event parameter
454
	 */
455
	public function onPreRender($param)
456
	{
457
		$this->_prerendered=true;
458
		$this->ensureDataBound();
459
		parent::onPreRender($param);
460
	}
461
 
462
	/**
463
	 * Validates if the parameter is a valid data source.
464
	 * If it is a string or an array, it will be converted as a TList object.
465
	 * @param Traversable|array|string data source to be validated
466
	 * @return Traversable the data that is traversable
467
	 * @throws TInvalidDataTypeException if the data is neither null nor Traversable
468
	 */
469
	protected function validateDataSource($value)
470
	{
471
		if(is_string($value))
472
		{
473
			$list=new TList;
474
			foreach(TPropertyValue::ensureArray($value) as $key=>$value)
475
			{
476
				if(is_array($value))
477
					$list->add($value);
478
				else
479
					$list->add(array($value,is_string($key)?$key:$value));
480
			}
481
			return $list;
482
		}
483
		else if(is_array($value))
484
			return new TMap($value);
485
		else if($value instanceof TDbDataReader) {
486
			// read array from TDbDataReader since it's forward-only stream and can only be traversed once
487
			return $value->readAll();
488
		}
489
		else if(($value instanceof Traversable) || $value===null)
490
			return $value;
491
		else
492
			throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid',get_class($this));
493
	}
494
 
495
	public function getDataMember()
496
	{
497
		return $this->getViewState('DataMember','');
498
	}
499
 
500
	public function setDataMember($value)
501
	{
502
		$this->setViewState('DataMember',$value,'');
503
	}
504
 
505
	public function getSelectParameters()
506
	{
507
		if(!$this->_parameters)
508
			$this->_parameters=new TDataSourceSelectParameters;
509
		return $this->_parameters;
510
	}
511
}
512
 
513
 
514
/**
515
 * TListItemType class.
516
 * TListItemType defines the enumerable type for the possible types
517
 * that databound list items could take.
518
 *
519
 * The following enumerable values are defined:
520
 * - Header: header item
521
 * - Footer: footer item
522
 * - Item: content item (neither header nor footer)
523
 * - Separator: separator between items
524
 * - AlternatingItem: alternating content item
525
 * - EditItem: content item in edit mode
526
 * - SelectedItem: selected content item
527
 * - Pager: pager
528
 *
529
 * @author Qiang Xue <qiang.xue@gmail.com>
530
 * @version $Id: TDataBoundControl.php 2492 2008-08-09 01:30:05Z knut $
531
 * @package System.Web.UI.WebControls
532
 * @since 3.0.4
533
 */
534
class TListItemType extends TEnumerable
535
{
536
	const Header='Header';
537
	const Footer='Footer';
538
	const Item='Item';
539
	const Separator='Separator';
540
	const AlternatingItem='AlternatingItem';
541
	const EditItem='EditItem';
542
	const SelectedItem='SelectedItem';
543
	const Pager='Pager';
544
}
545
 
546
 
547
/**
548
 * IItemDataRenderer interface.
549
 *
550
 * IItemDataRenderer defines the interface that an item renderer
551
 * needs to implement. Besides the {@link getData Data} property, a list item
552
 * renderer also needs to provide {@link getItemIndex ItemIndex} and
553
 * {@link getItemType ItemType} property.
554
 *
555
 * @author Qiang Xue <qiang.xue@gmail.com>
556
 * @version $Id: TDataBoundControl.php 2492 2008-08-09 01:30:05Z knut $
557
 * @package System.Web.UI.WebControls
558
 * @since 3.1.0
559
 */
560
interface IItemDataRenderer extends IDataRenderer
561
{
562
	/**
563
	 * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection.
564
	 * If the item is not in the collection (e.g. it is a header item), it returns -1.
565
	 * @return integer zero-based index of the item.
566
	 */
567
	public function getItemIndex();
568
 
569
	/**
570
	 * Sets the zero-based index for the item.
571
	 * If the item is not in the item collection (e.g. it is a header item), -1 should be used.
572
	 * @param integer zero-based index of the item.
573
	 */
574
	public function setItemIndex($value);
575
 
576
	/**
577
	 * @return TListItemType the item type.
578
	 */
579
	public function getItemType();
580
 
581
	/**
582
	 * @param TListItemType the item type.
583
	 */
584
	public function setItemType($value);
585
}
586
 
587
?>