Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 16 |
CRAP | |
0.00% |
0 / 213 |
| GridView | |
0.00% |
0 / 1 |
|
0.00% |
0 / 16 |
3540.00 | |
0.00% |
0 / 213 |
| init | |
0.00% |
0 / 1 |
132.00 | |
0.00% |
0 / 37 |
|||
| run | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 5 |
|||
| renderErrors | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 7 |
|||
| renderSection | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 8 |
|||
| renderItems | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 5 |
|||
| renderCaption | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 7 |
|||
| renderColumnGroup | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 18 |
|||
| renderTableHeader | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 18 |
|||
| renderTableFooter | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 11 |
|||
| renderFilters | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 3 |
|||
| renderTableBody | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 21 |
|||
| renderTableRow | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 16 |
|||
| initColumns | |
0.00% |
0 / 1 |
90.00 | |
0.00% |
0 / 31 |
|||
| createDataColumn | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 12 |
|||
| guessColumns | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 9 |
|||
| createPage | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 5 |
|||
| <?php | |
| /** | |
| * @copyright Copyright (c) 2015 Maciej Klemarczyk | |
| * @license MIT License | |
| */ | |
| namespace common\pdf\grid; | |
| use HaruDoc; | |
| use HaruPage; | |
| use common\helpers\PdfHelper; | |
| use common\helpers\PdfPageHelper; | |
| use Yii; | |
| use Closure; | |
| use common\pdf\BaseListView; | |
| use yii\i18n\Formatter; | |
| use yii\base\InvalidConfigException; | |
| use yii\helpers\Url; | |
| use yii\helpers\Html; | |
| use yii\helpers\Json; | |
| use yii\base\Model; | |
| /** | |
| * The GridView widget is used to display data in a grid. | |
| * | |
| * | |
| * @author Qiang Xue <qiang.xue@gmail.com> | |
| * @since 2.0 | |
| */ | |
| class GridView extends BaseListView | |
| { | |
| 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 \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| * @see caption | |
| */ | |
| public $captionOptions = []; | |
| /** | |
| * @var array the HTML attributes for the grid table element. | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $tableOptions = ['class' => 'table table-striped table-bordered']; | |
| /** | |
| * @var array the HTML attributes for the container tag of the grid view. | |
| * The "tag" element specifies the tag name of the container element and defaults to "div". | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $options = ['class' => 'grid-view']; | |
| /** | |
| * @var array the HTML attributes for the table header row. | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $headerRowOptions = []; | |
| /** | |
| * @var array the HTML attributes for the table footer row. | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $footerRowOptions = []; | |
| /** | |
| * @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 | |
| * | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $rowOptions = []; | |
| /** | |
| * @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 boolean whether to show the grid view if [[dataProvider]] returns no data. | |
| */ | |
| public $showOnEmpty = true; | |
| /** | |
| * @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 | |
| * [ | |
| * ['class' => SerialColumn::className()], | |
| * [ | |
| * 'class' => DataColumn::className(), // this line is optional | |
| * 'attribute' => 'name', | |
| * 'format' => 'text', | |
| * 'label' => 'Name', | |
| * ], | |
| * ['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 [[DataColumn::attribute|attribute]], [[DataColumn::format|format]], | |
| * and/or [[DataColumn::label|label]] options: `"attribute:format:label"`. | |
| * For example, the above "name" column can also be specified as: `"name:text:Name"`. | |
| * Both "format" and "label" are optional. They will take default values if absent. | |
| * | |
| * Using the shortcut format the configuration for columns in simple cases would look like this: | |
| * | |
| * ```php | |
| * [ | |
| * 'id', | |
| * 'amount:currency:Total Amount', | |
| * 'created_at:datetime', | |
| * ] | |
| * ``` | |
| * | |
| * When using a [[dataProvider]] with active records, you can also display values from related records, | |
| * e.g. the `name` attribute of the `author` relation: | |
| * | |
| * ```php | |
| * // shortcut syntax | |
| * 'author.name', | |
| * // full syntax | |
| * [ | |
| * 'attribute' => 'author.name', | |
| * // ... | |
| * ] | |
| * ``` | |
| */ | |
| public $columns = []; | |
| /** | |
| * @var string the HTML display when the content of a cell is empty | |
| */ | |
| 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|array the URL for returning the filtering result. [[Url::to()]] will be called to | |
| * normalize the URL. If not set, the current controller action will be used. | |
| * When the user makes change to any filter input, the current filtering inputs will be appended | |
| * as GET parameters to this URL. | |
| */ | |
| public $filterUrl; | |
| /** | |
| * @var string additional jQuery selector for selecting filter input fields | |
| */ | |
| public $filterSelector; | |
| /** | |
| * @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. | |
| * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | |
| */ | |
| public $filterRowOptions = ['class' => 'filters']; | |
| /** | |
| * @var array the options for rendering the filter error summary. | |
| * Please refer to [[Html::errorSummary()]] for more details about how to specify the options. | |
| * @see renderErrors() | |
| */ | |
| public $filterErrorSummaryOptions = ['class' => 'error-summary']; | |
| /** | |
| * @var array the options for rendering every filter error message. | |
| * This is mainly used by [[Html::error()]] when rendering an error message next to every filter input field. | |
| */ | |
| public $filterErrorOptions = ['class' => 'help-block']; | |
| /** | |
| * @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()]]. | |
| * - `{errors}`: the filter model error summary. See [[renderErrors()]]. | |
| * - `{items}`: the list items. See [[renderItems()]]. | |
| * - `{sorter}`: the sorter. See [[renderSorter()]]. | |
| * - `{pager}`: the pager. See [[renderPager()]]. | |
| */ | |
| public $layout = "{items}"; | |
| public $document; | |
| protected $headerFont; | |
| protected $dataFont; | |
| protected $page; | |
| protected $textPos; | |
| protected $fontSize = 12; | |
| /** | |
| * Initializes the grid view. | |
| * This method will initialize required property values and instantiate [[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->filterRowOptions['id'])) { | |
| $this->filterRowOptions['id'] = $this->options['id'] . '-filters'; | |
| } | |
| if($this->document == null){ | |
| $this->document = new HaruDoc(); | |
| $this->document->setPageMode(HaruDoc::PAGE_MODE_USE_THUMBS); | |
| }else{ | |
| $this->page = $this->document->getCurrentPage(); | |
| $this->textPos = PdfPageHelper::saveTextPos(); | |
| $this->textPos['x'] = PdfPageHelper::$marginLeft; | |
| } | |
| $this->headerFont = $this->document->getFont("Helvetica-Bold", Yii::$app->params['pdf.encoding']); | |
| $this->dataFont = $this->document->getFont("Helvetica", Yii::$app->params['pdf.encoding']); | |
| if($this->page === null){ | |
| $this->createPage(); | |
| $this->page = $this->document->getCurrentPage(); | |
| } | |
| if(!isset($this->textPos['y'])){ | |
| $this->textPos['y'] = PdfPageHelper::getHeight($this->page); | |
| } | |
| if(empty($this->textPos) || $this->textPos['x'] == 0 && $this->textPos['y'] == 0){ | |
| $this->textPos = ['x' => PdfPageHelper::$marginLeft, 'y' => PdfPageHelper::getHeight($this->page)]; | |
| } | |
| $this->page->setRGBStroke(0, 0, 0); | |
| PdfPageHelper::horizontalLine($this->page, $this->textPos['y'] +10); | |
| $this->initColumns(); | |
| } | |
| /** | |
| * Runs the widget. | |
| */ | |
| public function run() | |
| { | |
| $id = $this->options['id']; | |
| $view = $this->getView(); | |
| parent::run(); | |
| } | |
| /** | |
| * Renders validator errors of filter model. | |
| * @return string the rendering result. | |
| */ | |
| public function renderErrors() | |
| { | |
| if ($this->filterModel instanceof Model && $this->filterModel->hasErrors()) { | |
| return Html::errorSummary($this->filterModel, $this->filterErrorSummaryOptions); | |
| } else { | |
| return ''; | |
| } | |
| } | |
| /** | |
| * @inheritdoc | |
| */ | |
| public function renderSection($name) | |
| { | |
| switch ($name) { | |
| case "{errors}": | |
| return $this->renderErrors(); | |
| default: | |
| return parent::renderSection($name); | |
| } | |
| } | |
| /** | |
| * Renders the data models for the grid view. | |
| */ | |
| public function renderItems() | |
| { | |
| if($this->showHeader) $this->renderTableHeader(); | |
| $this->renderTableBody(); | |
| if($this->showFooter) $this->renderTableFooter(); | |
| } | |
| /** | |
| * Renders the caption element. | |
| * @return bool|string the rendered caption element or `false` if no caption element should be rendered. | |
| */ | |
| public function renderCaption() | |
| { | |
| if (!empty($this->caption)) { | |
| return Html::tag('caption', $this->caption, $this->captionOptions); | |
| } else { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Renders the column group HTML. | |
| * @return bool|string the column group HTML or `false` if no column group should be rendered. | |
| */ | |
| public function renderColumnGroup() | |
| { | |
| $requireColumnGroup = false; | |
| foreach ($this->columns as $column) { | |
| /* @var $column Column */ | |
| if (!empty($column->options)) { | |
| $requireColumnGroup = true; | |
| break; | |
| } | |
| } | |
| if ($requireColumnGroup) { | |
| $cols = []; | |
| 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() | |
| { | |
| $page = &$this->page; | |
| $page->setFontandSize($this->headerFont, $this->fontSize); | |
| $width = PdfPageHelper::getWidth($page); | |
| $page->beginText(); | |
| $page->moveTextPos($page->getCurrentFontSize(), -$page->getCurrentFontSize() *1.1, true); | |
| $width = 0; | |
| foreach ($this->columns as $i => $column) { | |
| /* @var $column Column */ | |
| $cell = $column->renderHeaderCell(); | |
| $text = strip_tags($cell); | |
| $enc_text = PdfHelper::encode($text); | |
| PdfPageHelper::textOut($page, $width, $this->textPos['y'] -$page->getCurrentFontSize()-5, $enc_text); | |
| $width += $column->width; | |
| } | |
| $this->textPos = $page->getCurrentTextPos(); | |
| $page->endText(); | |
| PdfPageHelper::horizontalLine($page, $this->textPos['y'] -5); | |
| } | |
| /** | |
| * Renders the table footer. | |
| * @return string the rendering result. | |
| */ | |
| public function renderTableFooter() | |
| { | |
| $cells = []; | |
| foreach ($this->columns as $column) { | |
| /* @var $column Column */ | |
| $cells[] = $column->renderFooterCell(); | |
| } | |
| $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions); | |
| if ($this->filterPosition == self::FILTER_POS_FOOTER) { | |
| $content .= $this->renderFilters(); | |
| } | |
| return "<tfoot>\n" . $content . "\n</tfoot>"; | |
| } | |
| /** | |
| * Renders the filter. | |
| * @return string the rendering result. | |
| */ | |
| public function renderFilters() | |
| { | |
| return ''; | |
| } | |
| /** | |
| * Renders the table body. | |
| * @return string the rendering result. | |
| */ | |
| public function renderTableBody() | |
| { | |
| $this->page->setFontandSize($this->dataFont, $this->fontSize); | |
| $models = array_values($this->dataProvider->getModels()); | |
| $keys = $this->dataProvider->getKeys(); | |
| foreach ($models as $index => $model) { | |
| $key = $keys[$index]; | |
| if ($this->beforeRow !== null) { | |
| call_user_func($this->beforeRow, $model, $key, $index, $this); | |
| } | |
| if($this->textPos['y'] < PdfPageHelper::$marginBottom + $this->page->getCurrentFontSize()*1.1){ | |
| $this->page = $this->createPage(); | |
| $this->page->setFontandSize($this->dataFont, $this->fontSize); | |
| $this->textPos = ['x' => PdfPageHelper::$marginLeft, 'y' => PdfPageHelper::getHeight($this->page)]; | |
| PdfPageHelper::horizontalLine($this->page, $this->textPos['y'] -5); | |
| } | |
| $this->renderTableRow($model, $key, $index); | |
| if ($this->afterRow !== null) { | |
| call_user_func($this->afterRow, $model, $key, $index, $this); | |
| } | |
| } | |
| } | |
| /** | |
| * 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) | |
| { | |
| $page = &$this->page; | |
| $page->beginText(); | |
| $page->moveTextPos($page->getCurrentFontSize(), -$page->getCurrentFontSize()*1.1, true); | |
| $width = PdfPageHelper::$marginLeft; | |
| foreach ($this->columns as $i => $column) { | |
| /* @var $column Column */ | |
| $cell = $column->renderDataCell($model, $key, $index); | |
| $text = strip_tags($cell); | |
| $enc_text = PdfHelper::encode($text); | |
| $page->textOut($width, $this->textPos['y'] -$page->getCurrentFontSize()-5, $enc_text); | |
| $width += $column->width; | |
| } | |
| $this->textPos = $page->getCurrentTextPos(); | |
| $page->endText(); | |
| PdfPageHelper::horizontalLine($page, $this->textPos['y'] -5); | |
| } | |
| /** | |
| * 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([ | |
| 'class' => $this->dataColumnClass ? : DataColumn::className(), | |
| 'grid' => $this, | |
| ], $column)); | |
| } | |
| if (!$column->visible) { | |
| unset($this->columns[$i]); | |
| continue; | |
| } | |
| $this->columns[$i] = $column; | |
| } | |
| $guessWidth = 100.0 / count($this->columns); | |
| $totalWidth = 0; | |
| foreach ($this->columns as $column) { | |
| if(!($column->width > 0)) | |
| $column->width = $guessWidth; | |
| $totalWidth += $column->width; | |
| } | |
| $pageWidth = PdfPageHelper::getWidth($this->page) * 1.0; | |
| foreach ($this->columns as $column) { | |
| $column->width = $column->width * $pageWidth / $totalWidth; | |
| } | |
| } | |
| /** | |
| * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label". | |
| * @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*))?(:(.*))?$/', $text, $matches)) { | |
| throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); | |
| } | |
| return Yii::createObject([ | |
| 'class' => $this->dataColumnClass ? : DataColumn::className(), | |
| 'grid' => $this, | |
| 'attribute' => $matches[1], | |
| 'format' => isset($matches[3]) ? $matches[3] : 'text', | |
| 'label' => isset($matches[5]) ? $matches[5] : null, | |
| ]); | |
| } | |
| /** | |
| * This function tries to guess the columns to show from the given data | |
| * if [[columns]] are not explicitly specified. | |
| */ | |
| 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; | |
| } | |
| } | |
| } | |
| protected function createPage() | |
| { | |
| $page = $this->document->addPage(); | |
| $page->setSize(HaruPage::SIZE_A4, HaruPage::LANDSCAPE); | |
| return $page; | |
| } | |
| } |