Yii: Sorting and formatting dynamic columns - sql-server

I am showing data in CGridView from a dynamic SQL Query using CSqlDataProvider. There are some static and some dynamic column. Now I want to do some special formatting like currency in the dynamic columns. But how do I do that when I don't know the number/name of the columns till the query is executed.
Also i want to be able to sort the dynamic columns and again I have the same problem that I don't have all the column names.
Anyone before who worked with dynamic queries and gridview. Could please point me to the right direction or give some ideas how to do it.
In short I am able to successfully show the data in gridview(also dynamic rows) and sort all the static columns. Just need to sort dynamic rows and format dynamic & static columns
Code for GridView:
$tdata=$dataProvider->getData();
//Calculation to get column names
$grid_columns = array_keys($tdata[0]);
foreach($grid_columns as $i=>$ii)
{
//Applying Formula to get Total Row
$grid_final[$i] = array('name'=>$ii,'class'=>'bootstrap.widgets.TbTotalSumColumn');
}
//Grid View
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'sortableRows'=>true,
'afterSortableUpdate' => 'js:function(id, position){ console.log("id: "+id+", position:"+position);}',
'dataProvider'=>$dataProvider,
'type'=>'striped bordered',
'template' => "{items}\n{extendedSummary}",
'columns'=> $grid_final,
));
Controller Code:
public function actionIndex()
{
if(isset($_GET['month']))
{
$month=$_GET['month'];
}
else
{
$month= 7;
}
//SQL Query with Dynamic Columns
$sql = "SELECt ABC,X,Y,Z, #Column_Names
FROM some_table
WHERE [month] = :month";
$connection=Yii::app()->db;
$command=$connection->createCommand($sql);
$command->bindParam(':month',$month,PDO::PARAM_STR);
$dataProvider=new CSqlDataProvider($sql,array('keyField' => 'ABC','params' => array(
':month' => $month,
),'sort' => array(
//Here how do i put column names which i don't know yet for sorting
'attributes' => array(
'ABC','X','Y','Z' )),'pagination'=>false));
$this->render('index',array('dataProvider' => $dataProvider, 'month' => $month));
}

I create dynamic columns in Yii like this:
In some_table model, let's name it SomeTable, I declare a maximum number of column names like this:
public $column1, $column2, $column3, $column4;
I create a function in that model, let's name it 'search()' that builds the dataProvider, like your logic states, but I make sure that #Column_Names looks like something like this:
var_column1 as column1, var_column2 as column2, etc.
in validation(), declare all those columns as safe on 'search'
construct a columns array formed from merging model->attributes and all declared columns with their options and assign it to the view, exactly like you do with $grid_final variable
The only drawback here is that you need to know the maximum number of columns, and of course, the big problem of declaring allot of variables if you have tables with allot of columns.

If you are able to get the columns before the grid is rendered, you can also alter the sorting conditions of the dataprovider.
Something like this:
$tdata=$dataProvider->getData();
//Calculation to get column names
$grid_columns = array_keys($tdata[0]);
$dataProvider->setSort(array('attributes'=> $grid_columns));
Or you can of course prepare you own array of attributes with specific settings, or specific formatting according to any logic you have. The thing is - once you have the columns in $grid_columns you can alter the dataProvider sorting, or the gridColumn setting as you need.

Related

Can an aliased query use a contain clause?

I use a union to join two datasets and then the following query to setup for pagination correctly
$paginationQuery = $this->find('all')
->contain(['EmailAddresses' => [
'foreignKey' => false,
'queryBuilder' => function($q) {
return $q->where(['Members__id' => 'EmailAddresses.member_id']);
}
]])
->select( $selectMainUnion )
->from([$this->getAlias() => $query])
->order(['Members__last_name' => 'ASC', 'Members__first_name' => 'ASC']);
I have also tried
$paginationQuery = $this->find('all')
->contain(['EmailAddresses'])
->select( $selectMainUnion )
->from([$this->getAlias() => $query])
->order(['Members__last_name' => 'ASC', 'Members__first_name' => 'ASC']);
and tried
$query->loadInto($query, ['EmailAddresses']); where $query is the result of the union.
Neither of these result in email addresses added to $paginationQuery.
Is there a way to do this?
Adding to clarify the code
$selectMain =['Members.id',
'Members.member_type',
'Members.first_name',
'Members.middle_name',
'Members.last_name',
'Members.suffix',
'Members.date_joined'];
foreach($selectMain as $select) {
$selectMainUnion[] = str_replace('.', '__', $select);
}
$this->hasMany('EmailAddresses', [
'foreignKey' => 'member_id',
'dependent' => true,
]);
Looking at the SQL in DebugKit SQL Log, there is no reference to the EmailAddresses table.
Generally containments do work fine irrespective of the queries FROM clause, whether that's a table or a subquery should be irrelevant. The requirement for this to work however is that the required primary and/or foreign key fields are being selected, and that they are in the correct format.
By default CakePHP's ORM queries automatically alias selected fields, ie they are being selected like Alias.field AS Alias__field. So when Alias is a subquery, then Alias.field doesn't exist, you'd have to select Alias.Alias__field instead. So with the automatic aliases, your select of Members__id would be transformed to Members.Members__id AS Members__Members__id, and Members__Members__id is not something the ORM understands, it would end up as Members__id in your entities, where the eager loader would expect id instead, ie the name of the primary key which is used to inject the results of the queried hasMany associated records (this happens in a separate query), your custom queryBuilder won't help with that, as the injecting happens afterwards on PHP level.
Long story short, to fix the problem, you can either change how the fields of the union queries are selected, ie ensure that they are not selected with aliases, that way the pagination query fields do not need to be changed at all:
$fields = $table->getSchema()->columns();
$fields = array_combine($fields, $fields);
$query->select($fields);
This will create a list of fields in the format of ['id' => 'id', ...], looks a bit whacky, but it works (as long as there's no ambiguity because of joined tables for example), the SQL would be like id AS id, so your pagination query can then simply reference the fields like Members.id.
Another way would be to select the aliases of the subquery, ie not just select Member__id, which the ORM turns into Member__Member__id when it applies automatic aliasing, but use Members.Member__id, like:
[
'Member__id' => 'Members.Member__id',
// ...
]
That way no automatic aliasing takes place, on SQL level it would select the field like Members.Member__id AS Member__id, and the field would end up as id in your entities, which the eager loader would find and could use for injecting the associated records.

using WPDB to display external database-data inside a WP-shortcode

I'm trying to figure out a way to use WPDB to load a whole row or single cells/fields from another table (not the Wordpress-DB) and displaying them in a shortcode. I have a bunch of weatherdata-values, I need the latest row (each column is another data-type (temp, wind, humidity, etc) of the database for a start.
Sadly, the plugin that would do everything that I need, SQL Shortcode, doesn't work anymore. I found this now:
https://de.wordpress.org/plugins/shortcode-variables/
Though I still need to use some PHP/PDO-foo to get the data from the database.
By heavy copy&pasting I came up with this:
<?php
$hostname='localhost';
$username='root';
$password='';
$dbname='sensordata';
$result = $db->prepare(SELECT * FROM `daten` WHERE id=(SELECT MAX(id) FROM `daten`););
$result->execute();
while ($row = $result->fetch(PDO::FETCH_ASSOC))
{
$data = $row['*'];
}
echo $data;
?>
But obviously it's not working. What I need to get it done with WPDB?
kind regards :)
Just in case anyone else needs this in the future. I used this now:
//connect to the database
<?php
$dbh = new PDO('mysql:host=localhost;dbname=databasename', 'dbuser',
'dbpasswort');
//query the database "databasename", selecting "columnname" from table "tablename", checking that said column has no NULL entry, sort it by column "id" (autoincrementing numeric ID), newest first and just fetch the last one
$sth = $dbh->query("SELECT `columnname` FROM `tablename` WHERE `columnname` IS NOT NULL order by id desc limit 1")->fetchColumn(0);
//print the value/number
print_r($sth);
?>
By using "SELECT colum1, colum2,... FROM" You should get all the columns, could be that fetchColumn needs to be replaced with something different though.

PostgreSQL - Check column value and update after removing any symbols

I have large amount of data in a table 'Users'. The 'username' field contains string values but users has input symbols in it e.g. im-a-user, you_are_user, etc.
How can I clean that columns data using SQL query ?
Users:
I want to clean the values in Username column so that they should look like, imauser, imanotheruser and andmoreuser, etc.
Use regexp_replace()
select id,
regexp_replace(lower(username), '[^a-z]', '', 'gi') as clean_user_name
from users;

Trying to make a filter to retrieve data from related models

I have a Post model which hasMany PostField
every post can have several fields stored in the post_fields table..
post_fields has this structure: (id, post_id, name, value)
posts table has some common fields for all posts, but any additional fields should be stored in post_fields table..
I created a search form that is used to filter the posts
when specifying filters for the fields in the posts table, it works fine..
but I want to make the filter to work even on the other fields found in post_fields ..
I can retrieve the posts first then filter them manually, but i want something more efficient !
EXAMPLE: let's suppose that posts are describing some products..
post (id, title, created, price)
post_fields (id, post_id, name, value)
in this case, all posts have title, created and price..
but if a post (id=3) wants to have a weight field, we should do that by creating a record in post_fields, the record should be :
{ id: .. , post_id: 3, name: weight, value: .. }
it's easy now to filter posts according to price (e.g. price between min & max)..
but, what if i want to filter posts according to weight ??
e.g. i want all posts that have weight greater than 10 !!
I would like to achieve this preferably in one query, using joins maybe or subqueries ..
I don't know how to do that in cakePHP, so if any one has an idea, plz HELP !!
even if someone just has an idea but doesn't have details, that could help ...
thanx in advance !
There is no way to search against the children of a hasMany relationship. You will need to run your query against the PostFields model. ie: $this->PostField->find('all', array('conditions'=>array('PostField.name' => 'weight', 'PostField.value' > 10)));
If you want to do a query against both the PostField and Post models at the same time (ie: price < $1.00 and weight > 10, you will need to do a custom query, as CakePHP has no built-in solution for doing so TMK. Should look something like this:
$query = "SELECT ... FROM posts as Post, post_fields as PostField WHERE PostField.name = 'weight' AND PostField.value > 10 AND POST.price < 1.0 AND PostField.post_id = Post.id;"
$posts = $this->Post->query($query);
EDIT:
I would do this. You're not going to get away with doing a single call, but this is still a clean solution.
$postIds = null;
if(/*we need to run query against PostFields*/) {
$conditions = array(
'OR' => array(
array(
'AND' => array(
'PostField.name' => 'weight',
'PostField.value' > 10
)
),
array(
'AND' => array(
'PostField.name' => 'height',
'PostField.value' < 10
)
)
)
);
$fields = array('PostField.id', 'PostField.post_id');
$postIds = $this->Post->PostField->find('list', array('conditions'=>$conditions, 'fields'=>$fields));
}
$conditions = array('Post.price' < 1.0);
if($postIds) {
$conditions['Post.id'] = $postIds;
}
$posts = $this->Post->find('all', array('conditions'=>$conditions));
You should look into using the Containable behavior for your models. This way, you can filter the returned columns as you like. (I think this is the type of filtering you want to do)

cakephp xml parser

I am parsing cakephp using xml parser. It parses it just fine. Its a huge xml. I now need to enter that into a database. Any easy way to do it without going into too much trouble with all those arrays and sub arrays
Thanks
This all depends on what the array looks like and how you want to store the data. If you just need to capture the array, you can use serialize:
$data = serialize($xml_array)
And store that in a text field.
If you need to store each item in the array, you can do that easy enough as long as there are not sub-arrays within the array. If it is for example and array like this:
array(
[MyArray] => (
[Field1] => 'data',
[field2] => 'data',
)
)
and the Field1 and field2 match the columns of the table, just change [MyArray] to the model name and pass the array to the model->save() function and it will save the data.
However, if you have sub-array information:
array(
[MyArray] => (
[Field1] => array([sub_array] => 'more_data'),
[field2] => 'data',
)
)
Your only option is to parse the data into an array that can be saved and then save it.

Resources