From b3b115146990c2518feb1d648f51e7a30a3ffd40 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 6 Aug 2013 17:00:32 -0400 Subject: [PATCH] Finished draft implementation of GridView. --- framework/yii/base/Application.php | 2 +- framework/yii/classes.php | 8 +- framework/yii/grid/CheckboxColumn.php | 84 ++++++ framework/yii/grid/Column.php | 142 +++++++++ framework/yii/grid/DataColumn.php | 108 +++++++ framework/yii/grid/GridView.php | 406 ++++++++++++++++++++++++++ framework/yii/grid/GridViewAsset.php | 26 ++ framework/yii/grid/SerialColumn.php | 33 +++ framework/yii/widgets/GridView.php | 340 --------------------- framework/yii/widgets/grid/CheckboxColumn.php | 75 ----- framework/yii/widgets/grid/Column.php | 142 --------- framework/yii/widgets/grid/DataColumn.php | 94 ------ framework/yii/widgets/grid/GridViewAsset.php | 26 -- framework/yii/widgets/grid/SerialColumn.php | 32 -- 14 files changed, 804 insertions(+), 714 deletions(-) create mode 100644 framework/yii/grid/CheckboxColumn.php create mode 100644 framework/yii/grid/Column.php create mode 100644 framework/yii/grid/DataColumn.php create mode 100644 framework/yii/grid/GridView.php create mode 100644 framework/yii/grid/GridViewAsset.php create mode 100644 framework/yii/grid/SerialColumn.php delete mode 100644 framework/yii/widgets/GridView.php delete mode 100644 framework/yii/widgets/grid/CheckboxColumn.php delete mode 100644 framework/yii/widgets/grid/Column.php delete mode 100644 framework/yii/widgets/grid/DataColumn.php delete mode 100644 framework/yii/widgets/grid/GridViewAsset.php delete mode 100644 framework/yii/widgets/grid/SerialColumn.php diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 687f1a3..aeba99b 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -169,7 +169,7 @@ abstract class Application extends Module public function registerErrorHandlers() { if (YII_ENABLE_ERROR_HANDLER) { - ini_set('display_errors', 0); + //ini_set('display_errors', 0); set_exception_handler(array($this, 'handleException')); set_error_handler(array($this, 'handleError'), error_reporting()); if ($this->memoryReserveSize > 0) { diff --git a/framework/yii/classes.php b/framework/yii/classes.php index a638dc0..367cc9a 100644 --- a/framework/yii/classes.php +++ b/framework/yii/classes.php @@ -230,10 +230,10 @@ return array( 'yii\widgets\ContentDecorator' => YII_PATH . '/widgets/ContentDecorator.php', 'yii\widgets\DetailView' => YII_PATH . '/widgets/DetailView.php', 'yii\widgets\FragmentCache' => YII_PATH . '/widgets/FragmentCache.php', - 'yii\widgets\grid\CheckboxColumn' => YII_PATH . '/widgets/grid/CheckboxColumn.php', - 'yii\widgets\grid\Column' => YII_PATH . '/widgets/grid/Column.php', - 'yii\widgets\grid\DataColumn' => YII_PATH . '/widgets/grid/DataColumn.php', - 'yii\widgets\GridView' => YII_PATH . '/widgets/GridView.php', + 'yii\grid\CheckboxColumn' => YII_PATH . '/grid/CheckboxColumn.php', + 'yii\grid\Column' => YII_PATH . '/grid/Column.php', + 'yii\grid\DataColumn' => YII_PATH . '/grid/DataColumn.php', + 'yii\grid\GridView' => YII_PATH . '/grid/GridView.php', 'yii\widgets\InputWidget' => YII_PATH . '/widgets/InputWidget.php', 'yii\widgets\LinkPager' => YII_PATH . '/widgets/LinkPager.php', 'yii\widgets\LinkSorter' => YII_PATH . '/widgets/LinkSorter.php', diff --git a/framework/yii/grid/CheckboxColumn.php b/framework/yii/grid/CheckboxColumn.php new file mode 100644 index 0000000..e9170f4 --- /dev/null +++ b/framework/yii/grid/CheckboxColumn.php @@ -0,0 +1,84 @@ + + * @since 2.0 + */ +class CheckboxColumn extends Column +{ + public $name = 'selection'; + public $checkboxOptions = array(); + public $multiple = true; + + + public function init() + { + parent::init(); + if (empty($this->name)) { + throw new InvalidConfigException('The "name" property must be set.'); + } + if (substr($this->name, -2) !== '[]') { + $this->name .= '[]'; + } + } + + /** + * Renders the header cell content. + * The default implementation simply renders {@link header}. + * This method may be overridden to customize the rendering of the header cell. + * @return string the rendering result + */ + protected function renderHeaderCellContent() + { + $name = rtrim($this->name, '[]') . '_all'; + $id = $this->grid->options['id']; + $options = json_encode(array( + 'name' => $this->name, + 'multiple' => $this->multiple, + 'checkAll' => $name, + )); + $this->grid->getView()->registerJs("jQuery('#$id').yiiGridView('setSelectionColumn', $options);"); + + if ($this->header !== null || !$this->multiple) { + return parent::renderHeaderCellContent(); + } else { + return Html::checkBox($name, false, array('class' => 'select-on-check-all')); + } + } + + /** + * Renders the data cell content. + * @param mixed $model the data model + * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. + * @return string the rendering result + */ + protected function renderDataCellContent($model, $index) + { + if ($this->checkboxOptions instanceof Closure) { + $options = call_user_func($this->checkboxOptions, $model, $index, $this); + } else { + $options = $this->checkboxOptions; + } + return Html::checkbox($this->name, !empty($options['checked']), $options); + } +} diff --git a/framework/yii/grid/Column.php b/framework/yii/grid/Column.php new file mode 100644 index 0000000..b49f73e --- /dev/null +++ b/framework/yii/grid/Column.php @@ -0,0 +1,142 @@ + + * @since 2.0 + */ +class Column extends Object +{ + /** + * @var GridView the grid view object that owns this column. + */ + public $grid; + /** + * @var string the header cell content. Note that it will not be HTML-encoded. + */ + public $header; + /** + * @var string the footer cell content. Note that it will not be HTML-encoded. + */ + public $footer; + /** + * @var callable + */ + public $content; + /** + * @var boolean whether this column is visible. Defaults to true. + */ + public $visible = true; + public $options = array(); + public $headerOptions = array(); + /** + * @var array|\Closure + */ + public $contentOptions = array(); + public $footerOptions = array(); + /** + * @var array the HTML attributes for the filter cell tag. + */ + public $filterOptions=array(); + + + /** + * Renders the header cell. + */ + public function renderHeaderCell() + { + return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions); + } + + /** + * Renders the footer cell. + */ + public function renderFooterCell() + { + return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions); + } + + /** + * Renders a data cell. + * @param mixed $model the data model being rendered + * @param integer $index the zero-based index of the data item among the item array returned by [[dataProvider]]. + * @return string the rendering result + */ + public function renderDataCell($model, $index) + { + if ($this->contentOptions instanceof Closure) { + $options = call_user_func($this->contentOptions, $model, $index, $this); + } else { + $options = $this->contentOptions; + } + return Html::tag('td', $this->renderDataCellContent($model, $index), $options); + } + + /** + * Renders the filter cell. + */ + public function renderFilterCell() + { + return Html::tag('td', $this->renderFilterCellContent(), $this->filterOptions); + } + + /** + * Renders the header cell content. + * The default implementation simply renders {@link header}. + * This method may be overridden to customize the rendering of the header cell. + * @return string the rendering result + */ + protected function renderHeaderCellContent() + { + return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell; + } + + /** + * Renders the footer cell content. + * The default implementation simply renders {@link footer}. + * This method may be overridden to customize the rendering of the footer cell. + * @return string the rendering result + */ + protected function renderFooterCellContent() + { + return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell; + } + + /** + * Renders the data cell content. + * @param mixed $model the data model + * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. + * @return string the rendering result + */ + protected function renderDataCellContent($model, $index) + { + if ($this->content !== null) { + return call_user_func($this->content, $model, $index, $this); + } else { + return $this->grid->emptyCell; + } + } + + /** + * Renders the filter cell content. + * The default implementation simply renders a space. + * This method may be overridden to customize the rendering of the filter cell (if any). + * @return string the rendering result + */ + protected function renderFilterCellContent() + { + return $this->grid->emptyCell; + } +} diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php new file mode 100644 index 0000000..29f6278 --- /dev/null +++ b/framework/yii/grid/DataColumn.php @@ -0,0 +1,108 @@ + + * @since 2.0 + */ +class DataColumn extends Column +{ + /** + * @var string the attribute name associated with this column. When neither [[content]] nor [[value]] + * is specified, the value of the specified attribute will be retrieved from each data model and displayed. + * + * Also, if [[header]] is not specified, the label associated with the attribute will be displayed. + */ + public $attribute; + /** + * @var \Closure an anonymous function that returns the value to be displayed for every data model. + * If this is not set, `$model[$attribute]` will be used to obtain the value. + */ + public $value; + /** + * @var string in which format should the value of each data model be displayed as (e.g. "text", "html"). + * Supported formats are determined by the [[GridView::formatter|formatter]] used by the [[GridView]]. + */ + public $format; + /** + * @var boolean whether to allow sorting by this column. If true and [[attribute]] is found in + * the sort definition of [[GridView::dataProvider]], then the header cell of this column + * will contain a link that may trigger the sorting when being clicked. + */ + public $enableSorting = true; + /** + * @var string|array|boolean the HTML code representing a filter input (e.g. a text field, a dropdown list) + * that is used for this data column. This property is effective only when [[GridView::filterModel]] is set. + * + * - If this property is not set, a text field will be generated as the filter input; + * - If this property is an array, a dropdown list will be generated that uses this property value as + * the list options. + * - If you don't want a filter for this data column, set this value to be false. + */ + public $filter; + + + protected function renderHeaderCellContent() + { + if ($this->attribute !== null && $this->header === null) { + $provider = $this->grid->dataProvider; + if ($this->enableSorting && ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { + return $sort->link($this->attribute); + } + $models = $provider->getModels(); + if (($model = reset($models)) instanceof Model) { + /** @var Model $model */ + return $model->getAttributeLabel($this->attribute); + } elseif ($provider instanceof ActiveDataProvider) { + if ($provider->query instanceof ActiveQuery) { + /** @var Model $model */ + $model = new $provider->query->modelClass; + return $model->getAttributeLabel($this->attribute); + } + } + return Inflector::camel2words($this->attribute); + } else { + return parent::renderHeaderCellContent(); + } + } + + protected function renderFilterCellContent() + { + if (is_string($this->filter)) { + return $this->filter; + } elseif ($this->filter !== false && $this->grid->filterModel instanceof Model && $this->attribute !== null) { + if (is_array($this->filter)) { + return Html::activeDropDownList($this->grid->filterModel, $this->attribute, $this->filter, array('prompt' => '')); + } else { + return Html::activeTextInput($this->grid->filterModel, $this->attribute); + } + } else { + return parent::renderFilterCellContent(); + } + } + + protected function renderDataCellContent($model, $index) + { + if ($this->value !== null) { + $value = call_user_func($this->value, $model, $index, $this); + } elseif ($this->content === null && $this->attribute !== null) { + $value = ArrayHelper::getValue($model, $this->attribute); + } else { + return parent::renderDataCellContent($model, $index); + } + return $this->grid->formatter->format($value, $this->format); + } +} diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php new file mode 100644 index 0000000..9490f27 --- /dev/null +++ b/framework/yii/grid/GridView.php @@ -0,0 +1,406 @@ + + * @since 2.0 + */ +class GridView extends ListViewBase +{ + const FILTER_POS_HEADER = 'header'; + const FILTER_POS_FOOTER = 'footer'; + const FILTER_POS_BODY = 'body'; + + /** + * @var string the default data column class if the class name is not explicitly specified when configuring a data column. + * Defaults to 'yii\grid\DataColumn'. + */ + public $dataColumnClass; + /** + * @var string the caption of the grid table + * @see captionOptions + */ + public $caption; + /** + * @var array the HTML attributes for the caption element + * @see caption + */ + public $captionOptions = array(); + /** + * @var array the HTML attributes for the grid table element + */ + public $tableOptions = array('class' => 'table table-striped table-bordered'); + /** + * @var array the HTML attributes for the table header row + */ + public $headerRowOptions = array(); + /** + * @var array the HTML attributes for the table footer row + */ + public $footerRowOptions = array(); + /** + * @var array|Closure the HTML attributes for the table body rows. This can be either an array + * specifying the common HTML attributes for all body rows, or an anonymous function that + * returns an array of the HTML attributes. The anonymous function will be called once for every + * data model returned by [[dataProvider]]. It should have the following signature: + * + * ~~~php + * function ($model, $key, $index, $grid) + * ~~~ + * + * - `$model`: the current data model being rendered + * - `$key`: the key value associated with the current data model + * - `$index`: the zero-based index of the data model in the model array returned by [[dataProvider]] + * - `$grid`: the GridView object + */ + public $rowOptions = array(); + /** + * @var Closure an anonymous function that is called once BEFORE rendering each data model. + * It should have the similar signature as [[rowOptions]]. The return result of the function + * will be rendered directly. + */ + public $beforeRow; + /** + * @var Closure an anonymous function that is called once AFTER rendering each data model. + * It should have the similar signature as [[rowOptions]]. The return result of the function + * will be rendered directly. + */ + public $afterRow; + /** + * @var boolean whether to show the header section of the grid table. + */ + public $showHeader = true; + /** + * @var boolean whether to show the footer section of the grid table. + */ + public $showFooter = false; + /** + * @var array|Formatter the formatter used to format model attribute values into displayable texts. + * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]] + * instance. If this property is not set, the "formatter" application component will be used. + */ + public $formatter; + /** + * @var array grid column configuration. Each array element represents the configuration + * for one particular grid column. For example, + * + * ~~~php + * array( + * array( + * 'class' => SerialColumn::className(), + * ), + * array( + * 'class' => DataColumn::className(), + * 'attribute' => 'name', + * 'format' => 'text', + * 'header' => 'Name', + * ), + * array( + * 'class' => CheckboxColumn::className(), + * ), + * ) + * ~~~ + * + * If a column is of class [[DataColumn]], the "class" element can be omitted. + * + * As a shortcut format, a string may be used to specify the configuration of a data column + * which only contains "attribute", "format", and/or "header" options: `"attribute:format:header"`. + * For example, the above "name" column can also be specified as: `"name:text:Name"`. + * Both "format" and "header" are optional. They will take default values if absent. + */ + public $columns = array(); + /** + * @var string the layout that determines how different sections of the list view should be organized. + * The following tokens will be replaced with the corresponding section contents: + * + * - `{summary}`: the summary section. See [[renderSummary()]]. + * - `{items}`: the list items. See [[renderItems()]]. + * - `{sorter}`: the sorter. See [[renderSorter()]]. + * - `{pager}`: the pager. See [[renderPager()]]. + */ + public $layout = "{items}\n{summary}\n{pager}"; + public $emptyCell = ' '; + /** + * @var \yii\base\Model the model that keeps the user-entered filter data. When this property is set, + * the grid view will enable column-based filtering. Each data column by default will display a text field + * at the top that users can fill in to filter the data. + * + * Note that in order to show an input field for filtering, a column must have its [[DataColumn::attribute]] + * property set or have [[DataColumn::filter]] set as the HTML code for the input field. + * + * When this property is not set (null) the filtering feature is disabled. + */ + public $filterModel; + /** + * @var string whether the filters should be displayed in the grid view. Valid values include: + * + * - [[FILTER_POS_HEADER]]: the filters will be displayed on top of each column's header cell. + * - [[FILTER_POS_BODY]]: the filters will be displayed right below each column's header cell. + * - [[FILTER_POS_FOOTER]]: the filters will be displayed below each column's footer cell. + */ + public $filterPosition = self::FILTER_POS_BODY; + /** + * @var array the HTML attributes for the filter row element + */ + public $filterRowOptions = array('class' => 'filters'); + + /** + * Initializes the grid view. + * This method will initialize required property values and instantiate {@link columns} objects. + */ + public function init() + { + parent::init(); + if ($this->formatter == null) { + $this->formatter = Yii::$app->getFormatter(); + } elseif (is_array($this->formatter)) { + $this->formatter = Yii::createObject($this->formatter); + } + if (!$this->formatter instanceof Formatter) { + throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); + } + if (!isset($this->options['id'])) { + $this->options['id'] = $this->getId(); + } + + $this->initColumns(); + } + + /** + * Runs the widget. + */ + public function run() + { + $id = $this->options['id']; + $view = $this->getView(); + GridViewAsset::register($view); + $view->registerJs("jQuery('#$id').yiiGridView();"); + parent::run(); + } + + /** + * Renders the data models for the grid view. + */ + public function renderItems() + { + $content = array_filter(array( + $this->renderCaption(), + $this->renderColumnGroup(), + $this->showHeader ? $this->renderTableHeader() : false, + $this->showFooter ? $this->renderTableFooter() : false, + $this->renderTableBody(), + )); + return Html::tag('table', implode("\n", $content), $this->tableOptions); + } + + public function renderCaption() + { + if (!empty($this->caption)) { + return Html::tag('caption', $this->caption, $this->captionOptions); + } else { + return false; + } + } + + public function renderColumnGroup() + { + $requireColumnGroup = false; + foreach ($this->columns as $column) { + /** @var Column $column */ + if (!empty($column->options)) { + $requireColumnGroup = true; + break; + } + } + if ($requireColumnGroup) { + $cols = array(); + foreach ($this->columns as $column) { + $cols[] = Html::tag('col', '', $column->options); + } + return Html::tag('colgroup', implode("\n", $cols)); + } else { + return false; + } + } + + /** + * Renders the table header. + * @return string the rendering result + */ + public function renderTableHeader() + { + $cells = array(); + foreach ($this->columns as $column) { + /** @var Column $column */ + $cells[] = $column->renderHeaderCell(); + } + $content = implode('', $cells); + if ($this->filterPosition == self::FILTER_POS_HEADER) { + $content = $this->renderFilters() . $content; + } elseif ($this->filterPosition == self::FILTER_POS_BODY) { + $content .= $this->renderFilters(); + } + return "\n" . Html::tag('tr', $content, $this->headerRowOptions) . "\n"; + } + + /** + * Renders the table footer. + * @return string the rendering result + */ + public function renderTableFooter() + { + $cells = array(); + foreach ($this->columns as $column) { + /** @var Column $column */ + $cells[] = $column->renderFooterCell(); + } + $content = implode('', $cells); + if ($this->filterPosition == self::FILTER_POS_FOOTER) { + $content .= $this->renderFilters(); + } + return "\n" . Html::tag('tr', $content, $this->footerRowOptions) . "\n"; + } + + /** + * Renders the filter. + */ + public function renderFilters() + { + if ($this->filterModel !== null) { + $cells = array(); + foreach ($this->columns as $column) { + /** @var Column $column */ + $cells[] = $column->renderFilterCell(); + } + return Html::tag('tr', implode('', $cells), $this->filterRowOptions); + } else { + return ''; + } + } + + /** + * Renders the table body. + * @return string the rendering result + */ + public function renderTableBody() + { + $models = array_values($this->dataProvider->getModels()); + $keys = $this->dataProvider->getKeys(); + $rows = array(); + foreach ($models as $index => $model) { + $key = $keys[$index]; + if ($this->beforeRow !== null) { + $row = call_user_func($this->beforeRow, $model, $key, $index, $this); + if (!empty($row)) { + $rows[] = $row; + } + } + + $rows[] = $this->renderTableRow($model, $key, $index); + + if ($this->afterRow !== null) { + $row = call_user_func($this->afterRow, $model, $key, $index, $this); + if (!empty($row)) { + $rows[] = $row; + } + } + } + return "\n" . implode("\n", $rows) . "\n"; + } + + /** + * Renders a table row with the given data model and key. + * @param mixed $model the data model to be rendered + * @param mixed $key the key associated with the data model + * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]]. + * @return string the rendering result + */ + public function renderTableRow($model, $key, $index) + { + $cells = array(); + /** @var Column $column */ + foreach ($this->columns as $column) { + $cells[] = $column->renderDataCell($model, $index); + } + if ($this->rowOptions instanceof Closure) { + $options = call_user_func($this->rowOptions, $model, $key, $index, $this); + } else { + $options = $this->rowOptions; + } + $options['data-key'] = $key; + return Html::tag('tr', implode('', $cells), $options); + } + + /** + * Creates column objects and initializes them. + */ + protected function initColumns() + { + if (empty($this->columns)) { + $this->guessColumns(); + } + foreach ($this->columns as $i => $column) { + if (is_string($column)) { + $column = $this->createDataColumn($column); + } else { + $column = Yii::createObject(array_merge(array( + 'class' => $this->dataColumnClass ?: DataColumn::className(), + 'grid' => $this, + ), $column)); + } + if (!$column->visible) { + unset($this->columns[$i]); + continue; + } + $this->columns[$i] = $column; + } + } + + /** + * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:header". + * @param string $text the column specification string + * @return DataColumn the column instance + * @throws InvalidConfigException if the column specification is invalid + */ + protected function createDataColumn($text) + { + if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { + throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:header'); + } + return Yii::createObject(array( + 'class' => $this->dataColumnClass ?: DataColumn::className(), + 'grid' => $this, + 'attribute' => $matches[1], + 'format' => isset($matches[3]) ? $matches[3] : 'text', + 'header' => isset($matches[5]) ? $matches[5] : null, + )); + } + + protected function guessColumns() + { + $models = $this->dataProvider->getModels(); + $model = reset($models); + if (is_array($model) || is_object($model)) { + foreach ($model as $name => $value) { + $this->columns[] = $name; + } + } else { + throw new InvalidConfigException('Unable to generate columns from data.'); + } + } +} diff --git a/framework/yii/grid/GridViewAsset.php b/framework/yii/grid/GridViewAsset.php new file mode 100644 index 0000000..decf674 --- /dev/null +++ b/framework/yii/grid/GridViewAsset.php @@ -0,0 +1,26 @@ + + * @since 2.0 + */ +class GridViewAsset extends AssetBundle +{ + public $sourcePath = '@yii/assets'; + public $js = array( + 'yii.gridView.js', + ); + public $depends = array( + 'yii\web\YiiAsset', + ); +} diff --git a/framework/yii/grid/SerialColumn.php b/framework/yii/grid/SerialColumn.php new file mode 100644 index 0000000..3a5e21b --- /dev/null +++ b/framework/yii/grid/SerialColumn.php @@ -0,0 +1,33 @@ + + * @since 2.0 + */ +class SerialColumn extends Column +{ + /** + * Renders the data cell content. + * @param mixed $model the data model + * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. + * @return string the rendering result + */ + protected function renderDataCellContent($model, $index) + { + $pagination = $this->grid->dataProvider->getPagination(); + if ($pagination !== false) { + return $pagination->getOffset() + $index + 1; + } else { + return $index + 1; + } + } +} diff --git a/framework/yii/widgets/GridView.php b/framework/yii/widgets/GridView.php deleted file mode 100644 index a831ab8..0000000 --- a/framework/yii/widgets/GridView.php +++ /dev/null @@ -1,340 +0,0 @@ - - * @since 2.0 - */ -class GridView extends ListViewBase -{ - const FILTER_POS_HEADER = 'header'; - const FILTER_POS_FOOTER = 'footer'; - const FILTER_POS_BODY = 'body'; - - public $dataColumnClass; - public $caption; - public $captionOptions = array(); - public $tableOptions = array('class' => 'table table-striped table-bordered'); - public $headerRowOptions = array(); - public $footerRowOptions = array(); - public $beforeRow; - public $afterRow; - public $showHeader = true; - public $showFooter = false; - /** - * @var array|Closure - */ - public $rowOptions = array(); - /** - * @var array|Formatter the formatter used to format model attribute values into displayable texts. - * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]] - * instance. If this property is not set, the "formatter" application component will be used. - */ - public $formatter; - /** - * @var array grid column configuration. Each array element represents the configuration - * for one particular grid column which can be either a string or an array. - * - * When a column is specified as a string, it should be in the format of "name:type:header", - * where "type" and "header" are optional. A {@link CDataColumn} instance will be created in this case, - * whose {@link CDataColumn::name}, {@link CDataColumn::type} and {@link CDataColumn::header} - * properties will be initialized accordingly. - * - * When a column is specified as an array, it will be used to create a grid column instance, where - * the 'class' element specifies the column class name (defaults to {@link CDataColumn} if absent). - * Currently, these official column classes are provided: {@link CDataColumn}, - * {@link CLinkColumn}, {@link CButtonColumn} and {@link CCheckBoxColumn}. - */ - public $columns = array(); - /** - * @var string the layout that determines how different sections of the list view should be organized. - * The following tokens will be replaced with the corresponding section contents: - * - * - `{summary}`: the summary section. See [[renderSummary()]]. - * - `{items}`: the list items. See [[renderItems()]]. - * - `{sorter}`: the sorter. See [[renderSorter()]]. - * - `{pager}`: the pager. See [[renderPager()]]. - */ - public $layout = "{items}\n{summary}\n{pager}"; - public $emptyCell = ' '; - /** - * @var \yii\base\Model the model instance that keeps the user-entered filter data. When this property is set, - * the grid view will enable column-based filtering. Each data column by default will display a text field - * at the top that users can fill in to filter the data. - * Note that in order to show an input field for filtering, a column must have its {@link CDataColumn::name} - * property set or have {@link CDataColumn::filter} as the HTML code for the input field. - * When this property is not set (null) the filtering is disabled. - */ - public $filterModel; - /** - * @var string whether the filters should be displayed in the grid view. Valid values include: - * - */ - public $filterPosition = 'body'; - public $filterOptions = array('class' => 'filters'); - - /** - * Initializes the grid view. - * This method will initialize required property values and instantiate {@link columns} objects. - */ - public function init() - { - parent::init(); - if ($this->formatter == null) { - $this->formatter = Yii::$app->getFormatter(); - } elseif (is_array($this->formatter)) { - $this->formatter = Yii::createObject($this->formatter); - } - if (!$this->formatter instanceof Formatter) { - throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); - } - if (!isset($this->options['id'])) { - $this->options['id'] = $this->getId(); - } - - $this->initColumns(); - } - - /** - * Runs the widget. - */ - public function run() - { - $id = $this->options['id']; - $view = $this->getView(); - GridViewAsset::register($view); - $view->registerJs("jQuery('#$id').yiiGridView();"); - parent::run(); - } - - /** - * Renders the data models for the grid view. - */ - public function renderItems() - { - $content = array_filter(array( - $this->renderCaption(), - $this->renderColumnGroup(), - $this->showHeader ? $this->renderTableHeader() : false, - $this->showFooter ? $this->renderTableFooter() : false, - $this->renderTableBody(), - )); - return Html::tag('table', implode("\n", $content), $this->tableOptions); - } - - public function renderCaption() - { - if (!empty($this->caption)) { - return Html::tag('caption', $this->caption, $this->captionOptions); - } else { - return false; - } - } - - public function renderColumnGroup() - { - $requireColumnGroup = false; - foreach ($this->columns as $column) { - /** @var \yii\widgets\grid\Column $column */ - if (!empty($column->options)) { - $requireColumnGroup = true; - break; - } - } - if ($requireColumnGroup) { - $cols = array(); - foreach ($this->columns as $column) { - $cols[] = Html::tag('col', '', $column->options); - } - return Html::tag('colgroup', implode("\n", $cols)); - } else { - return false; - } - } - - /** - * Renders the table header. - * @return string the rendering result - */ - public function renderTableHeader() - { - $cells = array(); - foreach ($this->columns as $column) { - /** @var \yii\widgets\grid\Column $column */ - $cells[] = $column->renderHeaderCell(); - } - $content = implode('', $cells); - if ($this->filterPosition == self::FILTER_POS_HEADER) { - $content = $this->renderFilters() . $content; - } elseif ($this->filterPosition == self::FILTER_POS_BODY) { - $content .= $this->renderFilters(); - } - return "\n" . Html::tag('tr', $content, $this->headerRowOptions) . "\n"; - } - - /** - * Renders the table footer. - * @return string the rendering result - */ - public function renderTableFooter() - { - $cells = array(); - foreach ($this->columns as $column) { - /** @var \yii\widgets\grid\Column $column */ - $cells[] = $column->renderFooterCell(); - } - $content = implode('', $cells); - if ($this->filterPosition == self::FILTER_POS_FOOTER) { - $content .= $this->renderFilters(); - } - return "\n" . Html::tag('tr', $content, $this->footerRowOptions) . "\n"; - } - - /** - * Renders the filter. - */ - public function renderFilters() - { - if ($this->filterModel !== null) { - $cells = array(); - foreach ($this->columns as $column) { - /** @var \yii\widgets\grid\Column $column */ - $cells[] = $column->renderFilterCell(); - } - return Html::tag('tr', implode('', $cells), $this->filterOptions); - } else { - return ''; - } - } - - /** - * Renders the table body. - * @return string the rendering result - */ - public function renderTableBody() - { - $models = array_values($this->dataProvider->getModels()); - $keys = $this->dataProvider->getKeys(); - $rows = array(); - foreach ($models as $index => $model) { - $key = $keys[$index]; - if ($this->beforeRow !== null) { - $row = call_user_func($this->beforeRow, $model, $key, $index); - if (!empty($row)) { - $rows[] = $row; - } - } - - $rows[] = $this->renderTableRow($model, $key, $index); - - if ($this->afterRow !== null) { - $row = call_user_func($this->afterRow, $model, $key, $index); - if (!empty($row)) { - $rows[] = $row; - } - } - } - return "\n" . implode("\n", $rows) . "\n"; - } - - /** - * Renders a table row with the given data model and key. - * @param mixed $model the data model to be rendered - * @param mixed $key the key associated with the data model - * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]]. - * @return string the rendering result - */ - public function renderTableRow($model, $key, $index) - { - $cells = array(); - /** @var \yii\widgets\grid\Column $column */ - foreach ($this->columns as $column) { - $cells[] = $column->renderDataCell($model, $index); - } - if ($this->rowOptions instanceof Closure) { - $options = call_user_func($this->rowOptions, $model, $key, $index); - } else { - $options = $this->rowOptions; - } - $options['data-key'] = $key; - return Html::tag('tr', implode('', $cells), $options); - } - - /** - * Creates column objects and initializes them. - */ - protected function initColumns() - { - if (empty($this->columns)) { - $this->guessColumns(); - } - foreach ($this->columns as $i => $column) { - if (is_string($column)) { - $column = $this->createDataColumn($column); - } else { - $column = Yii::createObject(array_merge(array( - 'class' => $this->dataColumnClass ?: DataColumn::className(), - 'grid' => $this, - ), $column)); - } - if (!$column->visible) { - unset($this->columns[$i]); - continue; - } - $this->columns[$i] = $column; - } - } - - /** - * Creates a {@link CDataColumn} based on a shortcut column specification string. - * @param string $text the column specification string - * @return DataColumn the column instance - * @throws InvalidConfigException if the column specification is invalid - */ - protected function createDataColumn($text) - { - if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { - throw new InvalidConfigException('The column must be specified in the format of "Attribute", "Attribute:Format" or "Attribute:Format:Header'); - } - return Yii::createObject(array( - 'class' => $this->dataColumnClass ?: DataColumn::className(), - 'grid' => $this, - 'attribute' => $matches[1], - 'format' => isset($matches[3]) ? $matches[3] : 'text', - 'header' => isset($matches[5]) ? $matches[5] : null, - )); - } - - protected function guessColumns() - { - $models = $this->dataProvider->getModels(); - $model = reset($models); - if (is_array($model) || is_object($model)) { - foreach ($model as $name => $value) { - $this->columns[] = $name; - } - } else { - throw new InvalidConfigException('Unable to generate columns from data.'); - } - } -} diff --git a/framework/yii/widgets/grid/CheckboxColumn.php b/framework/yii/widgets/grid/CheckboxColumn.php deleted file mode 100644 index 5d1dc0c..0000000 --- a/framework/yii/widgets/grid/CheckboxColumn.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @since 2.0 - */ -class CheckboxColumn extends Column -{ - public $name; - public $checkboxOptions = array(); - public $multiple = true; - - - public function init() - { - parent::init(); - if (empty($this->name)) { - throw new InvalidConfigException('The "name" property must be set.'); - } - if (substr($this->name, -2) !== '[]') { - $this->name .= '[]'; - } - } - - /** - * Renders the header cell content. - * The default implementation simply renders {@link header}. - * This method may be overridden to customize the rendering of the header cell. - * @return string the rendering result - */ - protected function renderHeaderCellContent() - { - $name = rtrim($this->name, '[]') . '_all'; - $id = $this->grid->options['id']; - $options = json_encode(array( - 'name' => $this->name, - 'multiple' => $this->multiple, - 'checkAll' => $name, - )); - $this->grid->getView()->registerJs("jQuery('#$id').yiiGridView('setSelectionColumn', $options);"); - - if ($this->header !== null || !$this->multiple) { - return parent::renderHeaderCellContent(); - } else { - return Html::checkBox($name, false, array('class' => 'select-on-check-all')); - } - } - - /** - * Renders the data cell content. - * @param mixed $model the data model - * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. - * @return string the rendering result - */ - protected function renderDataCellContent($model, $index) - { - if ($this->checkboxOptions instanceof Closure) { - $options = call_user_func($this->checkboxOptions, $model, $index, $this); - } else { - $options = $this->checkboxOptions; - } - return Html::checkbox($this->name, !empty($options['checked']), $options); - } -} diff --git a/framework/yii/widgets/grid/Column.php b/framework/yii/widgets/grid/Column.php deleted file mode 100644 index 11e3d3d..0000000 --- a/framework/yii/widgets/grid/Column.php +++ /dev/null @@ -1,142 +0,0 @@ - - * @since 2.0 - */ -class Column extends Object -{ - /** - * @var GridView the grid view object that owns this column. - */ - public $grid; - /** - * @var string the header cell content. Note that it will not be HTML-encoded. - */ - public $header; - /** - * @var string the footer cell content. Note that it will not be HTML-encoded. - */ - public $footer; - /** - * @var callable - */ - public $content; - /** - * @var boolean whether this column is visible. Defaults to true. - */ - public $visible = true; - public $options = array(); - public $headerOptions = array(); - /** - * @var array|\Closure - */ - public $contentOptions = array(); - public $footerOptions = array(); - /** - * @var array the HTML attributes for the filter cell tag. - */ - public $filterOptions=array(); - - - /** - * Renders the header cell. - */ - public function renderHeaderCell() - { - return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions); - } - - /** - * Renders the footer cell. - */ - public function renderFooterCell() - { - return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions); - } - - /** - * Renders a data cell. - * @param mixed $model the data model being rendered - * @param integer $index the zero-based index of the data item among the item array returned by [[dataProvider]]. - * @return string the rendering result - */ - public function renderDataCell($model, $index) - { - if ($this->contentOptions instanceof Closure) { - $options = call_user_func($this->contentOptions, $model, $index, $this); - } else { - $options = $this->contentOptions; - } - return Html::tag('td', $this->renderDataCellContent($model, $index), $options); - } - - /** - * Renders the filter cell. - */ - public function renderFilterCell() - { - return Html::tag('td', $this->renderFilterCellContent(), $this->filterOptions); - } - - /** - * Renders the header cell content. - * The default implementation simply renders {@link header}. - * This method may be overridden to customize the rendering of the header cell. - * @return string the rendering result - */ - protected function renderHeaderCellContent() - { - return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell; - } - - /** - * Renders the footer cell content. - * The default implementation simply renders {@link footer}. - * This method may be overridden to customize the rendering of the footer cell. - * @return string the rendering result - */ - protected function renderFooterCellContent() - { - return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell; - } - - /** - * Renders the data cell content. - * @param mixed $model the data model - * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. - * @return string the rendering result - */ - protected function renderDataCellContent($model, $index) - { - if ($this->content !== null) { - return call_user_func($this->content, $model, $index, $this); - } else { - return $this->grid->emptyCell; - } - } - - /** - * Renders the filter cell content. - * The default implementation simply renders a space. - * This method may be overridden to customize the rendering of the filter cell (if any). - * @return string the rendering result - */ - protected function renderFilterCellContent() - { - return $this->grid->emptyCell; - } -} diff --git a/framework/yii/widgets/grid/DataColumn.php b/framework/yii/widgets/grid/DataColumn.php deleted file mode 100644 index ac65c4c..0000000 --- a/framework/yii/widgets/grid/DataColumn.php +++ /dev/null @@ -1,94 +0,0 @@ - - * @since 2.0 - */ -class DataColumn extends Column -{ - public $attribute; - public $value; - public $format; - /** - * @var boolean whether to allow sorting by this column. If true and [[attribute]] is found in - * the sort definition of [[GridView::dataProvider]], then the header cell of this column - * will contain a link that may trigger the sorting when being clicked. - */ - public $enableSorting = true; - /** - * @var string|array|boolean the HTML code representing a filter input (eg a text field, a dropdown list) - * that is used for this data column. This property is effective only when - * {@link CGridView::filter} is set. - * If this property is not set, a text field will be generated as the filter input; - * If this property is an array, a dropdown list will be generated that uses this property value as - * the list options. - * If you don't want a filter for this data column, set this value to false. - */ - public $filter; - - - protected function renderHeaderCellContent() - { - if ($this->attribute !== null && $this->header === null) { - $provider = $this->grid->dataProvider; - if ($this->enableSorting && ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { - return $sort->link($this->attribute); - } - $models = $provider->getModels(); - if (($model = reset($models)) instanceof Model) { - /** @var Model $model */ - return $model->getAttributeLabel($this->attribute); - } elseif ($provider instanceof ActiveDataProvider) { - if ($provider->query instanceof ActiveQuery) { - /** @var Model $model */ - $model = new $provider->query->modelClass; - return $model->getAttributeLabel($this->attribute); - } - } - return Inflector::camel2words($this->attribute); - } else { - return parent::renderHeaderCellContent(); - } - } - - protected function renderFilterCellContent() - { - if (is_string($this->filter)) { - return $this->filter; - } elseif ($this->filter !== false && $this->grid->filterModel instanceof Model && $this->attribute !== null) { - if (is_array($this->filter)) { - return Html::activeDropDownList($this->grid->filterModel, $this->attribute, $this->filter, array('prompt' => '')); - } else { - return Html::activeTextInput($this->grid->filterModel, $this->attribute); - } - } else { - return parent::renderFilterCellContent(); - } - } - - protected function renderDataCellContent($model, $index) - { - if ($this->value !== null) { - $value = call_user_func($this->value, $model, $index, $this); - } elseif ($this->content === null && $this->attribute !== null) { - $value = ArrayHelper::getValue($model, $this->attribute); - } else { - return parent::renderDataCellContent($model, $index); - } - return $this->grid->formatter->format($value, $this->format); - } -} diff --git a/framework/yii/widgets/grid/GridViewAsset.php b/framework/yii/widgets/grid/GridViewAsset.php deleted file mode 100644 index f0c2432..0000000 --- a/framework/yii/widgets/grid/GridViewAsset.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @since 2.0 - */ -class GridViewAsset extends AssetBundle -{ - public $sourcePath = '@yii/assets'; - public $js = array( - 'yii.gridView.js', - ); - public $depends = array( - 'yii\web\YiiAsset', - ); -} diff --git a/framework/yii/widgets/grid/SerialColumn.php b/framework/yii/widgets/grid/SerialColumn.php deleted file mode 100644 index b9b78a7..0000000 --- a/framework/yii/widgets/grid/SerialColumn.php +++ /dev/null @@ -1,32 +0,0 @@ - - * @since 2.0 - */ -class SerialColumn extends Column -{ - /** - * Renders the data cell content. - * @param mixed $model the data model - * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]]. - * @return string the rendering result - */ - protected function renderDataCellContent($model, $index) - { - $pagination = $this->grid->dataProvider->getPagination(); - if ($pagination !== false) { - return $pagination->getOffset() + $index + 1; - } else { - return $index + 1; - } - } -}