├── .gitignore ├── AreaChart.php ├── BarChart.php ├── BubbleChart.php ├── Chart.php ├── ColumnChart.php ├── GeoChart.php ├── LICENSE ├── LineChart.php ├── PieChart.php ├── README.md ├── ScatterChart.php └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | assets/* 2 | !assets/.gitignore 3 | protected/runtime/* 4 | !protected/runtime/.gitignore 5 | protected/data/*.db 6 | themes/classic/views/ 7 | -------------------------------------------------------------------------------- /AreaChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | $this->loadPackages('corechart'); 26 | 27 | $this->drawChart("var $id=new google.visualization.AreaChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BarChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | if ($this->mode != 'classic') $this->options['bars'] = 'horizontal'; 22 | $jOpts = self::encode($this->options); 23 | 24 | $id = $this->getId(); 25 | 26 | if ($this->mode == 'classic') { 27 | $package = 'corechart'; 28 | $call = "var $id=new google.visualization.BarChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 29 | } 30 | else { 31 | $package = 'bar'; 32 | if ($this->mode == 'transition') $jOpts = "google.charts.Bar.convertOptions($jOpts)"; 33 | $call = "var $id=new google.charts.Bar(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 34 | } 35 | 36 | $this->loadPackages($package); 37 | 38 | $this->drawChart($call); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BubbleChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | $this->loadPackages('corechart'); 26 | 27 | $this->drawChart("var $id=new google.visualization.BubbleChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"); 28 | } 29 | } -------------------------------------------------------------------------------- /Chart.php: -------------------------------------------------------------------------------- 1 | Javascript 62 | * @link https://developers.google.com/chart/interactive/docs/events 63 | */ 64 | public $events = []; 65 | 66 | /** 67 | * @var array 68 | * HTML options of the chart container. 69 | * Use this if you want to explicitly set the ID. 70 | */ 71 | public $htmlOptions = []; 72 | 73 | protected static $loadOptions = [ 74 | 'packages' => [], 75 | ]; 76 | 77 | protected static $jsLoaded = ''; 78 | 79 | public function init() { 80 | if (is_null($this->dataProvider)) { 81 | throw new InvalidConfigException('The "dataProvider" property must be set.'); 82 | } 83 | if (isset($this->htmlOptions['id'])) { 84 | $this->setId($this->htmlOptions['id']); 85 | } 86 | else $this->htmlOptions['id'] = $this->getId(); 87 | 88 | $style = "height:$this->height;"; 89 | if (! empty($this->width)) $style .= "width:$this->width;"; 90 | $this->htmlOptions['style'] = $style; 91 | 92 | $this->columns = array_map(function($v) { 93 | if (is_string($v)) { 94 | if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $v, $matches)) { 95 | throw new InvalidConfigException('Column must be specified in the format of "attribute", "attribute:type" or "attribute:type:label"'); 96 | } 97 | $v = [ 98 | 'attribute' => $matches[1], 99 | 'type' => isset($matches[3]) ? $matches[3] : 'number', 100 | 'label' => isset($matches[5]) ? $matches[5] : null, 101 | ]; 102 | } 103 | return $v; 104 | }, $this->columns); 105 | 106 | $view = $this->getView(); 107 | 108 | $view->registerJsFile('//www.gstatic.com/charts/loader.js'); 109 | } 110 | 111 | public function run() { 112 | $view = $this->getView(); 113 | 114 | $lo = array_merge(self::$loadOptions, [ 115 | 'callback' => new JsExpression('drawChart'), 116 | 'language' => Yii::$app->language 117 | ]); 118 | 119 | $loadOpts = Json::encode($lo); 120 | $allJs = self::$jsLoaded; 121 | 122 | $view->registerJs("google.charts.load('{$this->version}',$loadOpts);function drawChart(){{$allJs}};", 123 | View::POS_END, __NAMESPACE__); 124 | 125 | echo Html::tag('div', '', $this->htmlOptions); 126 | } 127 | 128 | protected function loadPackages($packs) { 129 | if (is_string($packs)) $packs = [$packs]; 130 | $loaded = &self::$loadOptions['packages']; 131 | foreach ($packs as $pack) { 132 | if (! in_array($pack, $loaded)) $loaded[] = $pack; 133 | } 134 | } 135 | 136 | protected function drawChart($js) { 137 | foreach ($this->events as $evt => $code) { 138 | $js .= "google.visualization.events.addListener($this->id,'$evt',$code);"; 139 | } 140 | 141 | self::$jsLoaded .= $js; 142 | } 143 | 144 | protected function dataTable() { 145 | $provider = $this->dataProvider; 146 | 147 | $models = $provider->getModels(); 148 | $model = reset($models); 149 | 150 | $cols = array_map(function($v) use ($model) { 151 | $r = [ 152 | 'label' => $this->colLabel($model, $v), 153 | 'type' => $this->colType($v), 154 | ]; 155 | if (isset($v['pattern'])) $r['pattern'] = $v['pattern']; 156 | if (isset($v['role'])) $r['p'] = [ 'role' => $v['role']]; 157 | return $r; 158 | }, $this->columns); 159 | 160 | $index = 0; 161 | $rows = array_map(function($model) use(&$index) { 162 | $cells = array_map(function($column) use($model, &$index) { 163 | $r = $this->cell($model, $column, $index); 164 | return $r; 165 | }, $this->columns); 166 | $index++; 167 | return [ 168 | 'c' => $cells 169 | ]; 170 | }, $models); 171 | 172 | $jTable = self::encode([ 173 | 'cols' => $cols, 174 | 'rows' => $rows 175 | ]); 176 | return "new google.visualization.DataTable($jTable)"; 177 | } 178 | 179 | protected function colLabel($model, $col) { 180 | $label = ArrayHelper::getValue($col, 'label'); 181 | if (is_null($label)) { // label may be explicitly set to empty text 182 | $attr = $this->colAttribute($col); 183 | if ($model instanceof Model) $label = $model->getAttributeLabel($attr); 184 | else $label = Inflector::camel2words($attr); 185 | } 186 | return $label; 187 | } 188 | 189 | protected function cell($model, $col, $index) { 190 | $attr = $this->colAttribute($col); 191 | $value = ArrayHelper::getValue($col, 'value'); 192 | if (is_callable($value)) $value = call_user_func($value, $model, $attr, $index, $this); 193 | 194 | $formatted = ArrayHelper::getValue($col, 'formatted'); 195 | if (is_callable($formatted)) $formatted = call_user_func($formatted, $model, $attr, $index, $this); 196 | if (is_null($value)) $value = ArrayHelper::getValue($model, $attr); 197 | switch ($this->colType($col)) { 198 | case 'string': 199 | break; 200 | case 'number': 201 | $value = (float) $value; 202 | break; 203 | case 'boolean': 204 | $value = (boolean) $value; 205 | break; 206 | default: // 'date', 'dattime', 'timeofday' 207 | if (! is_numeric($value)) $value = "'$value'"; 208 | $value = new JsExpression("new Date($value)"); 209 | break; 210 | } 211 | $r = [ 'v' => $value ]; 212 | if ($formatted) $r['f'] = $formatted; 213 | return $r; 214 | } 215 | 216 | protected function colAttribute($col) { 217 | return ArrayHelper::getValue($col, 'attribute', ''); 218 | } 219 | 220 | protected function colType($col) { 221 | return ArrayHelper::getValue($col, 'type', 'number'); 222 | } 223 | 224 | protected static function encode($php) { 225 | return empty($php) ? '{}': preg_replace('/\"(\w+)\":/', '$1:', Json::htmlEncode($php)); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /ColumnChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | if ($this->mode == 'classic') { 26 | $package = 'corechart'; 27 | $call = "var $id=new google.visualization.ColumnChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 28 | } 29 | else { 30 | $package = 'bar'; 31 | if ($this->mode == 'transition') $jOpts = "google.charts.Bar.convertOptions($jOpts)"; 32 | $call = "var $id=new google.charts.Bar(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 33 | } 34 | 35 | $this->loadPackages($package); 36 | 37 | $this->drawChart($call); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GeoChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 27 | 28 | $jOpts = self::encode($this->options); 29 | 30 | $id = $this->getId(); 31 | 32 | if (!empty($this->mapsApiKey)) self::$loadOptions['mapsApiKey'] = $this->mapsApiKey; 33 | 34 | $this->loadPackages('geochart'); 35 | 36 | $this->drawChart("var $id=new google.visualization.GeoChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sjaak Priester 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /LineChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | if ($this->mode == 'classic') { 26 | $package = 'corechart'; 27 | $call = "var $id=new google.visualization.LineChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 28 | } 29 | else { 30 | $package = 'line'; 31 | if ($this->mode == 'transition') $jOpts = "google.charts.Line.convertOptions($jOpts)"; 32 | $call = "var $id=new google.charts.Line(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 33 | } 34 | 35 | $this->loadPackages($package); 36 | 37 | $this->drawChart($call); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PieChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | $this->loadPackages('corechart'); 26 | 27 | $this->drawChart("var $id=new google.visualization.PieChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yii2-gcharts 2 | ============ 3 | 4 | #### Google Charts the Yii 2.0 way #### 5 | 6 | [![Latest Stable Version](https://poser.pugx.org/sjaakp/yii2-gcharts/v/stable)](https://packagist.org/packages/sjaakp/yii2-gcharts) 7 | [![Total Downloads](https://poser.pugx.org/sjaakp/yii2-gcharts/downloads)](https://packagist.org/packages/sjaakp/yii2-gcharts) 8 | [![License](https://poser.pugx.org/sjaakp/yii2-gcharts/license)](https://packagist.org/packages/sjaakp/yii2-gcharts) 9 | 10 | **yii2-gcharts** is a collection of widgets to render selected [Google Charts](https://developers.google.com/chart/ "Google Developers") in the [Yii 2.0](http://www.yiiframework.com/ "Yii") PHP Framework, like you would use a [GridView](http://www.yiiframework.com/doc-2.0/yii-grid-gridview.html "Yii"). 11 | 12 | Currently, **yii2-gcharts** consists of the following widgets: 13 | 14 | - [AreaChart](https://developers.google.com/chart/interactive/docs/gallery/areachart "Google Developers") 15 | - [BarChart](https://developers.google.com/chart/interactive/docs/gallery/barchart "Google Developers") 16 | - [BubbleChart](https://developers.google.com/chart/interactive/docs/gallery/bubblechart "Google Developers") 17 | - [ColumnChart](https://developers.google.com/chart/interactive/docs/gallery/columnchart "Google Developers") 18 | - [GeoChart](https://developers.google.com/chart/interactive/docs/gallery/geochart "Google Developers") 19 | - [LineChart](https://developers.google.com/chart/interactive/docs/gallery/linechart "Google Developers") 20 | - [PieChart](https://developers.google.com/chart/interactive/docs/gallery/piechart "Google Developers") 21 | - [ScatterChart](https://developers.google.com/chart/interactive/docs/gallery/scatterchart "Google Developers") 22 | 23 | A demonstration of **Yii2-gcharts** is [here](http://www.sjaakpriester.nl/software/yii2-gcharts). 24 | 25 | ## Installation ## 26 | 27 | Install **yii2-gcharts** with [Composer](https://getcomposer.org/). Either add the following to the require section of your `composer.json` file: 28 | 29 | `"sjaakp/yii2-gcharts": "*"` 30 | 31 | Or run: 32 | 33 | `composer require sjaakp/yii2-gcharts "*"` 34 | 35 | You can manually install **yii2-gcharts** by [downloading the source in ZIP-format](https://github.com/sjaakp/yii2-gcharts/archive/master.zip). 36 | 37 | ## Using yii2-gcharts ## 38 | 39 | Use the **yii2 charts** widgets just like you would use a [GridView](http://www.yiiframework.com/doc-2.0/yii-grid-gridview.html "Yii Framework"). For instance, in the Controller you might have something like: 40 | 41 | Country::find(), 46 | 'pagination' => false 47 | ]); 48 | 49 | return $this->render('pie', [ 50 | 'dataProvider' => $dataProvider 51 | ]); 52 | } 53 | // ... 54 | ?> 55 | 56 | To render a PieChart in the View we could use: 57 | 58 | 61 | ... 62 | '400px', 64 | 'dataProvider' => $dataProvider, 65 | 'columns' => [ 66 | 'name:string', 67 | 'population' 68 | ], 69 | 'options' => [ 70 | 'title' => 'Countries by Population' 71 | ], 72 | ]) ?> 73 | ... 74 | 75 | Each of the chart types has slight variations in the column interpretation, and its own set of options. Consult the [Google Charts documentation](https://developers.google.com/chart/?hl=nl "Google Developers"). 76 | 77 | ## Options ## 78 | 79 | All the **yii2-gcharts** widgets share the same options: 80 | 81 | ### dataProvider ### 82 | 83 | The data provider for the chart. This property is required. In most cases, it will be an `ActiveDataProvider` or an `ArrayDataProvider`. 84 | 85 | ### columns ### 86 | 87 | Chart column configuration array. Each array element configures one chart column. Each column configuration is an `array` or a `string` shortcut. 88 | 89 | An `array` column configuration can have the following members (all are optional, but at least one must be given): 90 | 91 | - **attribute** The attribute name associated with this column. When `value` is not specified, the value of the attribute will be retrieved from each data model. 92 | 93 | - **formatted** The Google Charts formatted version of the data. Can be a callable of the form `function($model, $attribute, $index, $widget)`. 94 | 95 | - **label** The label assigned to the data. If not given, it is either retrieved from the model or derived from `attribute`. 96 | 97 | - **pattern** The Google Charts [pattern](https://developers.google.com/chart/interactive/docs/querylanguage#Format "Google Developers"). 98 | 99 | - **role** The Google Charts [role](https://developers.google.com/chart/interactive/docs/roles "Google Developers"). Can be one of: 100 | 101 | - `"annotation"` 102 | - `"annotationText"` 103 | - `"certainty"` 104 | - `"emphasis"` 105 | - `"interval"` 106 | - `"scope"` 107 | - `"style"` 108 | - `"tooltip"` 109 | 110 | - **type** The Google Charts [data type](https://developers.google.com/chart/interactive/docs/reference#DataTable "Google Developers"). Can be one of: 111 | 112 | - `"number"` (default) 113 | - `"string"` 114 | - `"boolean"` 115 | - `"date"` 116 | - `"datetime"` 117 | - `"timeofday"` 118 | 119 | 120 | - **value** The data value. This can be a callable of the form `function($model, $attribute, $index, $widget)`. If not given, the value of the model's `attribute` is taken. 121 | 122 | The `string` shortcut configuration specifies the attribute, type, and label in the format `"attribute:type:label"`. Both type and label are optional; they take their default values if omitted. 123 | 124 | ### mode ### 125 | 126 | `string` This determines which variant of the chart is drawn. Must be one of: 127 | 128 | - `"classic"` (default) Draws the 'ordinary' version of the chart, 129 | - `"material"` Draws the new, Material version of the chart, if available, 130 | - `"transition"` Draws the Material version, if available, and also applies `convertOptions()` to the options 131 | 132 | **Notice** that only a few of the charts are currently available in Material version and that they're in early beta, lacking lots of the 'classic' options. 133 | 134 | **Notice also** that currently, the Material options are undocumented, so the only practical way to work with Material charts is using the `"transition"` mode. 135 | 136 | ### version ### 137 | 138 | `string` The version of the **gcharts** library used. Must be one of: 139 | 140 | - `"current"` (default), 141 | - `"upcoming"`, 142 | - `number` 143 | 144 | [More](https://developers.google.com/chart/interactive/docs/basic_load_libs#load-version-name-or-number) information. 145 | 146 | ### mapsApiKey ### 147 | 148 | `string` Applies to **GeoChart** only. It is advised to provide **GeoChart** with an [API-key](https://developers.google.com/chart/interactive/docs/gallery/geochart#loading) of Google Maps. 149 | -------------------------------------------------------------------------------- /ScatterChart.php: -------------------------------------------------------------------------------- 1 | dataTable(); 20 | 21 | $jOpts = self::encode($this->options); 22 | 23 | $id = $this->getId(); 24 | 25 | if ($this->mode == 'classic') { 26 | $package = 'corechart'; 27 | $call = "var $id=new google.visualization.ScatterChart(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 28 | } 29 | else { 30 | $package = 'scatter'; 31 | if ($this->mode == 'transition') $jOpts = "google.charts.Scatter.convertOptions($jOpts)"; 32 | $call = "var $id=new google.charts.Scatter(document.getElementById('$id'));$id.draw($dataTable,$jOpts);"; 33 | } 34 | 35 | $this->loadPackages($package); 36 | 37 | $this->drawChart($call); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sjaakp/yii2-gcharts", 3 | "description": "Google Charts the Yii 2.0 way", 4 | "keywords": ["yii", "yii2", "extension", "widget", "charts", "visualization"], 5 | "type": "yii2-extension", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Sjaak Priester", 10 | "email": "sjaak@sjaakpriester.nl", 11 | "homepage": "http://sjaakpriester.nl" 12 | } 13 | ], 14 | "require": { 15 | "yiisoft/yii2": "*" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "sjaakp\\gcharts\\": "" 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------