yii - create nested cgridview - yii

Is it possible to create nested table using cgridview?
I'd like to have the final output as follows
| Transaction | Total |
| T-001 | $100 |
| Item | Price | // here is the nested table
| I-1 | $50 |
| I-2 | $50 |
| T-002 | $90 |
| Item | Price | // here is the nested table
| I-3 | $90 |
I know you can do this using a custom template, but i'd like a neater solution using a widget like CGRidView.
Thanks

If nested table is inside cell then you can, just create function in your model that will render table and return content. You can set third parameter of widget function to true to return content. If you want to enable pagination for nested table then be sure to manually set widget id and allow ajax update.
In model:
function getNestedTable() {
return Yii::app()->controller->widget(..., ..., true);
}
In columns definition use:
'columns' => array(
array(
'name' => 'nestedTable',
'type' => 'raw'
)
)

I think the best way to achieve what you want is to use custom functions in your CActiveRecord model (if you've a CActiveDataprovider for the grid), and put that 'nested table' as normal column:
| Transaction | Item | Price | Total |
------------------------------------------
| T-001 | I-1 | $50 | $100 |
| I-2 | $50 |
------------------------------------------
| T-002 | I-3 | $90 | $90 |
------------------------------------------
In your model you've to define get functions that return data in HTML with line breaks (for example with a br:
class Item extends CActiveRecord {
...
public function getIdItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->textId; // 'I-3', 'I-2'...
}
return $string;
}
public function getPriceItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->price; // $50, $90...
}
return $string;
}
...
}
And to show the new columns in your grid:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'anotacionesGrid',
'dataProvider'=>$dataProvider,
'columns'=>array(
'transaction',
'idItems:html:Item',
'priceItems:html:Price',
'total'
)
);

Related

How can I extract a json column into new columns automatically in Snowflake SQL?

This is as example taken from another thread, but essentially I would like to achieve this:
Sample data
ID Name Value
1 TV1 {"URL": "www.url.com", "Icon": "some_icon"}
2 TV2 {"URL": "www.url.com", "Icon": "some_icon", "Facebook": "Facebook_URL"}
3 TV3 {"URL": "www.url.com", "Icon": "some_icon", "Twitter": "Twitter_URL"}
..........
Expected output
ID Name URL Icon Facebook Twitter
1 TV1 www.url.com some_icon NULL NULL
2 TV2 www.url.com some_icon Facebook_URL NULL
3 TV3 www.url.com some_icon NULL Twitter_URL
I'm totally new to Snowflake so I'm shaking my head on how to do this easily (and hopefully automatically, in the case where some rows might have more elements in the json than other rows, which would be tedious to assign manually). Some lines might have sub-categories too.
I found the parse_json function for Snowflake, but it's only giving me the same json column in a new column, still in json format.
TIA!
You can create a view over your table with the following SELECT:
SELECT ID,
Name,
Value:URL::varchar as URL,
Value:Icon::varchar as Icon,
Value:Facebook::varchar as Facebook,
Value:Twitter::varchar as Twitter
FROM tablename;
Additional attributes will be ignored unless you add them to the view. There is no way to "automatically" include them into the view, but you could create a stored procedure that dynamically generates the view based on all the attributes that are in the full variant content of a table.
You can create a SP to automatically build the CREATE VIEW for you based on the JSON data in the VARIANT.
I have some simple example below:
-- prepare the table and data
create or replace table test (
col1 int, col2 string,
data1 variant, data2 variant
);
insert into test select 1,2, parse_json(
'{"URL": "test", "Icon": "test1", "Facebook": "http://www.facebook.com"}'
), parse_json(
'{"k1": "test", "k2": "test1", "k3": "http://www.facebook.com"}'
);
insert into test select 3,4,parse_json(
'{"URL": "test", "Icon": "test1", "Twitter": "http://www.twitter.com"}'
), parse_json(
'{"k4": "v4", "k3": "http://www.ericlin.me"}'
);
-- create the SP, we need to know which table and
-- column has the variant data
create or replace procedure create_view(
table_name varchar
)
returns string
language javascript
as
$$
var final_columns = [];
// first, find out the columns
var query = `SHOW COLUMNS IN TABLE ${TABLE_NAME}`;
var stmt = snowflake.createStatement({sqlText: query});
var result = stmt.execute();
var variant_columns = [];
while (result.next()) {
var col_name = result.getColumnValue(3);
var data_type = JSON.parse(result.getColumnValue(4));
// just use it if it is not a VARIANT type
// if it is variant type, we need to remember this column
// and then run query against it later
if (data_type["type"] != "VARIANT") {
final_columns.push(col_name);
} else {
variant_columns.push(col_name);
}
}
var columns = {};
query = `SELECT ` + variant_columns.join(', ') + ` FROM ${TABLE_NAME}`;
stmt = snowflake.createStatement({sqlText: query});
result = stmt.execute();
while (result.next()) {
for(i=1; i<=variant_columns.length; i++) {
var sub_result = result.getColumnValue(i);
if(!sub_result) {
continue;
}
var keys = Object.keys(sub_result);
for(j=0; j<keys.length; j++) {
columns[variant_columns[i-1] + ":" + keys[j]] = keys[j];
}
}
}
for(path in columns) {
final_columns.push(path + "::STRING AS " + columns[path]);
}
var create_view_sql = "CREATE OR REPLACE VIEW " +
TABLE_NAME + "_VIEW\n" +
"AS SELECT " + "\n" +
" " + final_columns.join(",\n ") + "\n" +
"FROM " + TABLE_NAME + ";";
snowflake.execute({sqlText: create_view_sql});
return create_view_sql + "\n\nVIEW created successfully.";
$$;
Execute the SP will return below string:
call create_view('TEST');
+---------------------------------------+
| CREATE_VIEW |
|---------------------------------------|
| CREATE OR REPLACE VIEW TEST_VIEW |
| AS SELECT |
| COL1, |
| COL2, |
| DATA1:Facebook::STRING AS Facebook, |
| DATA1:Icon::STRING AS Icon, |
| DATA1:URL::STRING AS URL, |
| DATA2:k1::STRING AS k1, |
| DATA2:k2::STRING AS k2, |
| DATA2:k3::STRING AS k3, |
| DATA1:Twitter::STRING AS Twitter, |
| DATA2:k4::STRING AS k4 |
| FROM TEST; |
| |
| VIEW created successfully. |
+---------------------------------------+
Then query the VIEW:
SELECT * FROM TEST_VIEW;
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
| COL1 | COL2 | FACEBOOK | ICON | URL | K1 | K2 | K3 | TWITTER | K4 |
|------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------|
| 1 | 2 | http://www.facebook.com | test1 | test | test | test1 | http://www.facebook.com | NULL | NULL |
| 3 | 4 | NULL | test1 | test | NULL | NULL | http://www.ericlin.me | http://www.twitter.com | v4 |
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
Query the source table:
SELECT * FROM TEST;
+------+------+------------------------------------------+-----------------------------------+
| COL1 | COL2 | DATA1 | DATA2 |
|------+------+------------------------------------------+-----------------------------------|
| 1 | 2 | { | { |
| | | "Facebook": "http://www.facebook.com", | "k1": "test", |
| | | "Icon": "test1", | "k2": "test1", |
| | | "URL": "test" | "k3": "http://www.facebook.com" |
| | | } | } |
| 3 | 4 | { | { |
| | | "Icon": "test1", | "k3": "http://www.ericlin.me", |
| | | "Twitter": "http://www.twitter.com", | "k4": "v4" |
| | | "URL": "test" | } |
| | | } | |
+------+------+------------------------------------------+-----------------------------------+
You can refine this SP to detect nested data and have them added to the columns list as well.

Yii: CGridView Columns Totals

Please help!
I need to total three columns (Budget, Release and Expenditure) under header in CGridView as shown below
| Fund | Budget | Release | Expenditure|
| | Total:30 | Total:15 | Total: 8 |
| A | 10 | 5 | 3 |
| B | 20 | 10 | 5 |
You can achieve that by inheriting CGridView and overriding the renderTableHeader function.
1st. Make your grid:
<?php
Yii::import('zii.widgets.grid.CGridView');
class CGridViewWithTotals extends CGridView
{
public function renderTableHeader()
{
if(!$this->hideHeader)
{
echo "<thead>\n";
if($this->filterPosition===self::FILTER_POS_HEADER)
$this->renderFilter();
echo "<tr>\n";
foreach($this->columns as $column)
$column->renderHeaderCell();
echo "</tr>\n";
if($this->filterPosition===self::FILTER_POS_BODY)
$this->renderFilter();
if($this->getHasFooter())
{
echo "<tr>\n";
foreach($this->columns as $column)
$column->renderFooterCell();
echo "</tr>\n";
}
echo "</thead>\n";
}
else if($this->filter!==null && ($this->filterPosition===self::FILTER_POS_HEADER || $this->filterPosition===self::FILTER_POS_BODY))
{
echo "<thead>\n";
$this->renderFilter();
echo "</thead>\n";
}
}
}
To prevent footer from rendering(if you dont need it), override CGridView::renderTableFooter()
2nd. Use your new grid as always:
<?php
$this->widget('CGridViewWithTotals', array(
'id'=>'my-grid',
'dataProvider'=>$model->search(),
'emptyText'=>'No items found.',
'ajaxUpdate'=>false,
'columns'=>array(
array(
'name'=>'field1',
'footer'=>$total,
),
),
));
?>

phql changed when executing using modelsManager

I'm new to Phalcon and would like to create a PHP web-service on my WAMP server. I have a table called "coreswings" in MySQL database and it represents the cores and wings of a large building. There are five fields: abbr, name, type, busyFrom, busyTo.
Following the tutorial on http://docs.phalconphp.com/en/latest/reference/tutorial-rest.html , I can route my requests to my desired functions, but the phql, "SELECT * FROM coreswings", doesn't work and returns me fatal errors.
index.php
<?php
/*###########################################################################
########## Set up connection to be used by model CoresWings, start ##########
###########################################################################*/
// use Loader() to autoload the model
$loader = new \Phalcon\Loader();
$loader->registerDirs(array(__DIR__.'/models/'))->register();
$di = new \Phalcon\DI\FactoryDefault();
// set up the database service
$di->set('db', function(){
return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => "localhost",
"username" => "user",
"password" => "user_pw",
"dbname" => "map"
));
});
// create and bind the DI to the application
$app = new \Phalcon\Mvc\Micro($di);
/*#########################################################################
########## Set up connection to be used by model CoresWings, end ##########
#########################################################################*/
/*#########################################################
########## create routes according to api, start ##########
#########################################################*/
// get all cores and wings
$app->get('/coresWings', function() use ($app){
$phql = "SELECT * FROM coreswings";
$coresWings = $app->modelsManager->executeQuery($phql);
$data = array();
foreach($coresWings as $coreWing){
$data[] = array(
'abbr' => $coreWing->abbr,
'name' => $coreWing->name,
'type' => $coreWing->type,
'busyFrom' => $coreWing->busyFrom,
'busyTo' => $coreWing->busyTo,
);
}
echo json_encode($data);
});
// testing purpose
$app->get('/testing', function(){
$data = array(
'function' => 'tesing',
'data' => '001'
);
echo json_encode($data);
});
/*#######################################################
########## create routes according to api, end ##########
#######################################################*/
$app->handle();
?>
When I access the URL http://localhost/FYP/001/api/coresWings, the following errors are shown:
( ! ) Fatal error: Uncaught exception 'Phalcon\Mvc\Model\Exception' with message 'Table "cores_wings"
doesn't exist on database when dumping meta-data for CoresWings' in
D:\Program Files\wamp\www\FYP\001\api\index.php on line 39
( ! ) Phalcon\Mvc\Model\Exception: Table "cores_wings" doesn't exist on database when dumping meta-data for CoresWings in D:\Program Files\wamp\www\FYP\001\api\index.php on line 39
Of course I don't have a table called "cores_wings", but my phql is "SELECT * FROM coreswings". Please tell me if I have done anything wrong. Thanks so much.
#
mysql> describe coreswings;
+----------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------------------+------+-----+---------+-------+
| abbr | varchar(63) | NO | PRI | NULL | |
| name | varchar(1024) | YES | | NULL | |
| type | enum('core','wing') | NO | | NULL | |
| busyFrom | time | YES | | NULL | |
| busyTo | time | YES | | NULL | |
+----------+---------------------+------+-----+---------+-------+
Model: CoresWings.php
use Phalcon\Mvc\Model,
Phalcon\Mvc\Model\Message,
Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class CoresWings extends Model{
public function validation(){
// building type must be "core" or "wing"
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("core", "wing")
)
));
// building abbreviation must be unique
$this->validate(new Uniqueness(
array(
"field" => "abbr",
"message" => "Abbreviation of a building must be unique"
)
));
// check if any messages have been produced
if($this->validationHasFailed()==true){
return false;
}
}
}
?>
If you want minimal configuration then you should follow some PhalconPHP's conventions.
One of them is that it for CamelCaseModel PhalconPHP uses camel_case table.
If you want to set different tabel for model then you should set it's source:
class CoresWings extends Model{
public function initialize() {
$this->setSource('coreswings');
}
}
You can use Phalcon\Mvc\Model::getSource() method. It is table name map with manual.

Simple relation in YII, can't get it done

This is the first time I use relations in Yii, the question is very simple.
MODULE TABLE
name - PK
status - FK status_id
STATUS TABLE
id PK
name
So, each Module HAS one status.
But I can not seem to get it working.
Module.php (Model)
public function relations()
{
return array(
'status'=>array(self::BELONGS_TO, 'ModuleStatus', 'status'),
);
}
I access them this way:
$modulesAR = Module::model()->with('status')->findAll();
if( $modulesAR )
{
foreach( $modulesAR as $moduleAR )
{
$this->modules[ $moduleAR->name ] = array(
'sessionLimit' => isset($moduleAR->sessionLimit) ? $moduleAR->sessionLimit : 0,
'status' => isset($moduleAR->status) ? $moduleAR->status : 'disabled',
);
}
}
Var_dump(Yii::app()->module->modules;
array(3) {
["digidoc"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
["docusearch"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "1" // Should say "Enabled"
}
["printbox"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
}
I will appreciate any help.
Thanks!
EDIT:
query executed by Yii:
SELECT `t`.`name` AS `t0_c0`, `t`.`status_id` AS `t0_c1`, `t`.`session_limit` AS `t0_c2`, `status`.`id` AS `t1_c0`, `status`.`name` AS `t1_c1` FROM `ss_module` `t` LEFT OUTER JOIN `ss_module_status` `status` ON (`status`.`id`=`t`.`name`)
+------------+-------+-------+-------+-------+
| t0_c0 | t0_c1 | t0_c2 | t1_c0 | t1_c1 |
+------------+-------+-------+-------+-------+
| digidoc | 2 | 0 | NULL | NULL |
| docusearch | 1 | 2 | NULL | NULL |
| printbox | 2 | 0 | NULL | NULL |
+------------+-------+-------+-------+-------+
EDIT 2:
Changing the last
ON (`status`.`id`=`t`.`name`);
to
ON (`status`.`id`=`t`.`status_id`);
works as expected, by I dont know how to fix it in Yii.
When you peek into $moduleAR->status, you get the whole Status model instance, not it's id. So just go on and peek into that instance:
$moduleAR->status->name.
That's what ActiveRecord magic is intended for ;)
And inversely, if you have Status model, you should have a relation there saying something like this:
'modules' => array(self::HAS_MANY, 'Module', 'status_id')
Which could make the following possible:
$status = Status::model()->find(/*somehow*/);
/* now that $status is an Status instance */
foreach ($status->modules as $module) {
// here you are! looping over all modules connected to this status
// each $module is a full-fledged Module instance
}

Associate table cell with header

I want to associate a cell value in table with it's header. The header is not known, as it was
generated by SQL query.
Sizes as header came from SQL return result. So then put it into an array,
#sizes = qw(S36 S37 S38 S39 S40 S41 S42);
Now, if James has size S38.
I want to print them as HTML table with sizes header:
+--------+--------+--------+-------+-------+-------+-------+
| S36 | S37 | S38 | S39 | S40 | S41 | S42 |
+--------+--------+--------+-------+-------+-------+-------+
| | | James | | | | |
+--------+--------+--------+-------+-------+-------+-------+
I know how to do this if sizes is part of row or result, but as table header?
How to manipulate this with Perl?
EDITED:
I try to summarize the code i tried...
SQL query:
select size from articles where order_number = "3";
Get into an array:
while(my $ref = $sth->fetchrow_hashref()) {
$size = "$ref->{'size'}";
push #sizes, $size;
}
Say, #sizes is:
#sizes = qw(S36 S37 S38 S39 S40 S41 S42);
Create HTML header based on sizes:
+--------+--------+--------+-------+-------+-------+-------+
| S36 | S37 | S38 | S39 | S40 | S41 | S42 |
+--------+--------+--------+-------+-------+-------+-------+
Now, say from another SQL query, i know that James has S38.
How to put into the right row cell of the above table. It would be:
+--------+--------+--------+-------+-------+-------+-------+
| S36 | S37 | S38 | S39 | S40 | S41 | S42 |
+--------+--------+--------+-------+-------+-------+-------+
| | | James | | | | |
+--------+--------+--------+-------+-------+-------+-------+
Here is a way of doing it using CGI.pm HTML generation methods:
#!/usr/bin/perl
use strict;
use warnings;
use CGI qw(:html);
use List::AllUtils qw( first_index );
my #sizes = qw(S36 S37 S38 S39 S40 S41 S42);
my %person = ( name => 'James', size => 'S38');
my #row = ('') x #sizes;
$row[ first_index { $_ eq $person{size} } #sizes ] = $person{name};
print start_html,
table( { border => 1 },
Tr(td({width => sprintf('%.0f%%', 100/#sizes)}, \#sizes)),
Tr(td(\#row) ) ),
end_html;
On the other hand, I do love HTML::Template:
#!/usr/bin/perl
use strict; use warnings;
use HTML::Template;
use List::AllUtils qw( first_index );
my #sizes = qw(S36 S37 S38 S39 S40 S41 S42);
my %person = ( name => 'James', size => 'S38');
my #row = (' ') x #sizes;
$row[ first_index { $_ eq $person{size} } #sizes ] = $person{name};
my $tmpl_txt = <<EO_TMPL;
<html><head><style type="text/css">
#size_chart { margin: 0 auto; }
#size_chart td { width: <TMPL_VAR CELL_WIDTH>; border: 2px inset #ddd }
</style></head>
<body><table id="size_chart">
<TMPL_LOOP ROWS><tr>
<TMPL_LOOP CELLS><td><TMPL_VAR CELL></td></TMPL_LOOP>
</tr></TMPL_LOOP>
</table></body></html>
EO_TMPL
my $tmpl = HTML::Template->new(scalarref => \$tmpl_txt);
$tmpl->param(
CELL_WIDTH => sprintf('%.0f%%', 100/#sizes),
ROWS => [ { CELLS => [ map { { CELL => $_ } } #sizes ] },
{ CELLS => [ map { { CELL => $_ } } #row ] },
]);
print $tmpl->output;