-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generating a dynamic table from a data set #11
Comments
Hi, |
Do the machine names reflect the hierarchy always, in your use case? |
Ok, here is my solution. /**
* @var bool[] $colNames
* Format: $[$colName] = $isLeaf
*/
$colNames = [
'F1' => false,
'F1.F4' => true,
'F1.F5' => true,
'F2' => false,
'F2.F6' => true,
'F2.F8' => true,
'F3' => false,
'F3.F10' => false,
'F3.F10.F7' => true,
'F3.F10.F9' => false,
'F3.F10.F9.F12' => true,
'F3.F10.F9.F13' => true,
'F3.F11' => true,
];
$maxdepth = 0;
foreach ($colNames as $colName => $isLeaf) {
$depth = substr_count($colName, '.');
$maxdepth = max($maxdepth, $depth);
}
$table = Table::create();
$thead = $table->thead();
$rowGroupName = 'g';
$rowGroupNames = [$rowGroupName];
for ($i = 0; $i < $maxdepth; ++$i) {
$thead->addRow($rowGroupName . '.caption');
$rowGroupName .= '.g';
$rowGroupNames[] = $rowGroupName;
}
$thead->addRow($rowGroupName);
foreach ($colNames as $colName => $isLeaf) {
if (FALSE !== $pos = strrpos($colName, '.')) {
$caption = substr($colName, $pos + 1);
}
else {
$caption = $colName;
}
$table->addColName($colName);
$depth = substr_count($colName, '.');
$rowGroupName = $rowGroupNames[$depth];
$rowName = $isLeaf
? $rowGroupName
: $rowGroupName . '.caption';
$thead->th($rowName, $colName, $caption);
}
$dataset = [
'F1.F4' => 'F4 data',
'F1.F5' => 'F5 data',
'F2.F6' => 'F6 data',
'F2.F8' => 'F8 data',
'F3.F10.F7' => 'F7 data',
'F3.F10.F9.F12' => 'F12 data',
'F3.F10.F9.F13' => 'F13 data',
'F3.F11' => 'F11 data',
];
$table->addRow(0);
foreach ($dataset as $colName => $cellContent) {
$table->td(0, $colName, $cellContent);
}
return $table->render(); Some things to note:
|
For examples like this, maybe it would be nice if one didn't need the long hierarchic machine names. |
I did not immediately notice the error in building the hierarchy, I did a little wrong recursion... In the end, I got a set of data like yours
Alas, I have not found another way as adjacency tables or material path, if there are proposals I will gladly accept and test
This is what I strive for, your library is a huge work, thank you for this, I would not have managed without you...
I want to do some refinement on the data set, for example, not one line but 10, how would it look? How is the enclosed array?
Once again, thank you very much! |
I am not sure I understand all of the questions, if something is missing please ask again.
The things you need to know for adding multiple data rows:
If you want, you can use the shortcut syntax with row handles, but I want to keep it simple for now. If you want to split up a cell within a data set: |
Sorry I am a bit confused, do you mean it is a lot of work to use it or it was a lot of work to write it? |
It's all my bad English ... No, I wanted to say that you did a great job, the result came out very worthy! |
ok :) thank you! |
Hello! It's me again 😃 Last time there were a lot of problems because of my poor spoken English, I'm studying it, but so far everything has become very bad :(, so I will adjust my thoughts from Google Translate 😋 You really help me, but I still do not understand how to add extra rows of data... How it all looks at the moment:
The UFO came and published this code here and initialize all $this->_tree = $this->getArray()->createTree(); and get the sorted array (fragment) print_r($this->_tree)
public function create($data)
{
if(!is_array($data)){
return;
}
foreach ($data as $el => $els) {
$this->_array[$els['path']] = is_array($els['childs']) ? false : true;
if (isset($els['childs'])) {
$this->create($els['childs']);
}
}
$this->_table = Table::create();
$colNames = ($this->_array);
$maxdepth = 0;
foreach ($colNames as $colName => $isLeaf) {
$depth = substr_count($colName, '.');
$maxdepth = max($maxdepth, $depth);
}
$thead = $this->_table->thead();
$rowGroupName = 'g';
$rowGroupNames = [$rowGroupName];
//if(!array_key_exists($this->_table->thead->rows->parents[$rowGroupName], $rowGroupNames)) {
for ($i = 0; $i < $maxdepth; ++$i) {
$thead->addRow($rowGroupName . '.caption');
$rowGroupName .= '.g';
$rowGroupNames[] = $rowGroupName;
}
$thead->addRow($rowGroupName);
//}
//$this->p($this->_table->thead->rows->parents);
foreach ($colNames as $colName => $isLeaf) {
//if (!array_key_exists($this->_table->columns->parents[$colName], $colNames)) {
if (false !== $pos = strrpos($colName, '.')) {
$caption = substr($colName, $pos + 1);
} else {
$caption = $colName;
}
$this->_table->addColName($colName);
$depth = substr_count($colName, '.');
$rowGroupName = $rowGroupNames[$depth];
$rowName = $isLeaf
? $rowGroupName
: $rowGroupName . '.caption';
$thead->th($rowName, $colName, $caption);
}
/*
* ToDo create automatic data output from the database
*/
$dataset = [];
foreach ($this->_lastLevelColumns as $key => $val){
$dataset[$val['path']] = $val['values'][0]['value'];
}
//$this->p($dataset);
/*
$dataset = [
'F1.F4' => 'F4 Data',
'F1.F6' => 'F5 Data',
'F2.F5' => 'F6 Data',
'F2.F8' => 'F8 Data',
'F3.F10.F7' => 'F7 Data',
'F3.F10.F9.F12' => 'F12 Data',
'F3.F10.F9.F13' => 'F13 Data',
'F3.F11' => 'F11 Data',
];
*/
$this->_table->addRow(1);
foreach ($dataset as $colName => $colValue) {
$this->_table->td(1, $colName, $colValue);
}
return $this->_table;
} call <?= $mapper->create($mapper->_tree)->render(); ?>
For example, there is a method for obtaining the last branches of a tree together with the corresponding values The UFO came and published this code here get such an array (fragment) print_r($this->_lastLevelColumns) [4] => Array
(
[id] => 4
[internal_key] => 1
[name] => F4
[values] => Array
(
[0] => Array
(
[id] => 3
[value] => this field 4 value
)
[1] => Array
(
[id] => 13
[value] => value 4
)
)
[path] => F1.F4
) The problem is /*
* ToDo create automatic data output from the database
*/
$dataset = [];
foreach ($this->_lastLevelColumns as $key => $val){
$dataset[$val['path']] = $val['values'][0]['value'];
}
//$this->p($dataset);
/*
$dataset = [
'F1.F4' => 'F4 Data',
'F1.F6' => 'F5 Data',
'F2.F5' => 'F6 Data',
'F2.F8' => 'F8 Data',
'F3.F10.F7' => 'F7 Data',
'F3.F10.F9.F12' => 'F12 Data',
'F3.F10.F9.F13' => 'F13 Data',
'F3.F11' => 'F11 Data',
];
*/
$this->_table->addRow(1);
foreach ($dataset as $colName => $colValue) {
$this->_table->td(1, $colName, $colValue);
} Full code listing and dump of the database, you can look at the links
If you have free time, help me, please, I can not do this myself... :( |
Wow this is a lot of detail, which is not directly related to Cellbrush library. Some general feedback about your codeI see in your code that you are writing a lot on object properties of I would recommend to instead treat the object as immutable, and make Also I think none of the object properties that you write on need to be object properties. Everything can be local variables, parameters and return values. This way, some of your functions / methods can become "pure", see https://en.wikipedia.org/wiki/Pure_function Then, add doc comments everywhere, and document the type of parameters and return values. E.g. /**
* @param int $colId
*
* @return array[]
* Format: $[] = ['id' => .., 'value' => ..]
*/
public function getAllValuesForColumn($colId) {.. Also rename your variables to be more meaningful. The entire example seems quite artificial. E.g. why are fields labeled "F1", "F2", "F3"? Feedback about the uploaded pictureIn the picture it looks like for each data row, you display the values for F6, F4, F5, F8, F11, F7, F12, F13. Feedback about Cellbrush usageI admit I do not fully understand the rest of your code, and I don't have time to study all of it. But from only looking at the parts that deal with Cellbrush: $this->_table->addRow(1);
foreach ($dataset as $colName => $colValue) {
$this->_table->td(1, $colName, $colValue);
} Here you add the row named "1". |
Here is something that might help. Before this code can run, the table already needs to have all the columns. Notes:
// Visible (leaf) columns
// @todo This should be filled from the description table.
$columnIdsByFieldId = [
6 => 'F1.F6',
4 => 'F1.F4',
5 => 'F2.F5',
8 => 'F2.F8',
11 => 'F3.F11',
7 => 'F3.F10.F7',
12 => 'F3.F10.F9.F12',
13 => 'F3.F10.F9.F13',
];
$q = $this->db->query('SELECT * FROM `values` ORDER BY `id`');
$tbody = $table->tbody();
while ($row = $q->fetch_assoc()) {
$fieldId = $row['field_id'];
if (!isset($columnIdsByFieldId[$fieldId])) {
// Ignore this entry.
continue;
}
$rowId = $row['id'];
$columnId = $columnIdsByFieldId[$fieldId];
$valueUnsafe = $row['value'];
$valueSafe = htmlspecialchars($valueUnsafe, ENT_QUOTES, 'UTF-8');
$tbody->addRowIfNotExists($rowId);
$tbody->td($rowId, $columnId, $valueSafe);
} |
You can use a shortcut for this: $tbody->addRowIfNotExists($rowId);
$tbody->td($rowId, $columnId, $valueSafe); Shortcut: $tbody->addRowIfNotExists($rowId)->td($columnId, $valueSafe); I am only writing this for other people who might be interested. You have other problems than using shortcuts at this stage :) |
@donquixote Many thanks! :) But there is one more question, I think that this will even facilitate the work. You add data in this way: while ($row = $q->fetch_assoc()) {
$fieldId = $row['field_id'];
if (!isset($columnIdsByFieldId[$fieldId])) {
// Ignore this entry.
continue;
}
$rowId = $row['id'];
$columnId = $columnIdsByFieldId[$fieldId];
$valueUnsafe = $row['value'];
$valueSafe = htmlspecialchars($valueUnsafe, ENT_QUOTES, 'UTF-8');
$tbody->addRowIfNotExists($rowId);
$tbody->td($rowId, $columnId, $valueSafe);
} namely a row and columns $tbody->td($rowId, $columnId, $valueSafe); and it works fine, but I think it will be better if all this is stored in a matrix form $dataset = [
['F16.F18.F20', 'F1.F6', 'x - 20; y - 6'],
['F16.F18.F21', 'F1.F6', 'x - 21; y - 6'],
['F16.F19', 'F1.F6', 'x - 19; y - 6'],
['F16.F18.F20', 'F1.F4', 'x - 20; y - 4'],
['F16.F18.F21', 'F1.F4', 'x - 21; y - 4'],
['F16.F19', 'F1.F4', 'x - 19; y - 4'],
['F16.F18.F20', 'F2.F5', 'x - 20; y - 5'],
['F16.F18.F21', 'F2.F5', 'x - 21; y - 5'],
['F16.F19', 'F2.F5', 'x - 19; y - 5'],
['F16.F18.F20', 'F2.F8', 'x - 20; y - 8'],
['F16.F18.F21', 'F2.F8', 'x - 21; y - 8'],
['F16.F19', 'F2.F8', 'x - 19; y - 8'],
['F16.F18.F20', 'F3.F10.F9.F12.F14', 'x - 20; y - 14'],
['F16.F18.F21', 'F3.F10.F9.F12.F14', 'x - 21; y - 14'],
['F16.F19', 'F3.F10.F9.F12.F14', 'x - 19; y - 14'],
['F16.F18.F20', 'F3.F10.F9.F13', 'x - 20; y - 13'],
['F16.F18.F21', 'F3.F10.F9.F13', 'x - 21; y - 13'],
['F16.F19', 'F3.F10.F9.F13', 'x - 19; y - 13'],
]; In the database, we also store the matrix
But I do not quite understand how to properly organize this using your library... So far the simplest and not quite the right way: /*
* [PATH, LEVEL(column name, should be deleted)]
*/
$rowsVerticalHierarhy = [
['F16', 0],
['F16.F18', 1],
['F16.F18.F20', 2],
['F16.F18.F21', 2],
['F16.F19', 1],
];
$table->addColNames([0, 1, 2]); // example columns as levels, how to do without levels?
foreach ($rowsVerticalHierarhy as $row){
$table->addRow($row[0]);
$table->th($row[0], $row[1], $row[0]);
}
// ...
// and
foreach ($dataset as $data){
$tbody->td($data[0], $data[1], $data[2]);
} how to do it and how do I delete these additional rows in the left corner of the table, those. rowspan and colspan? :) |
it would be awesome... |
Hello, |
Well, I'll try to put the essence, in general, there are two arrays, one of which contains a description of the horizontal hierarchy (from above), and the other - the vertical one (on the left). And also the third array with data, where the first two elements are the keys of the last columns of the table (ie, full paths, ends of the hierarchy), and the third one - values for intersection of cells, vertical and hierarchical hierarchy, ie, a matrix. This is the question of how to implement this decision qualitatively and beautifully? I have it was poorly implemented, there is no grouping, many extra lines, and you have it elegantly and practical... In the picture you can see by the elements of F16 (left) a lot of lines that should not be... |
Those. the following scheme is obtained:
The first moment is perfect, the second one was made by me, it was very bad. Well, the third seems more normal, it remains only to bind to the database (but somehow I'll do it myself) |
Do you have more intuitively meaningful example data? Something more appealing to my imagination? Something with animals and flowers and food perhaps? Or rivers and mountains? |
@donquixote Well, I'll try to put the essence. I made a more intuitive hierarchy, for this I slightly reduced the depth of the hierarchy. Look at the picture. I mentioned some cells in red - these are extra cells, and they should not exist in theory, that is, Weight row have attr rowspan = 2 Common informationThe top and left hierarchies are stored in the same table with the orientation attribute:
and the data itself is stored in the form of records with coordinates (that is, with long paths) in the form:
Now about the very implementation1. Left Hierarchy: /* Array for LEFT TH
* [PATH, LEVEL(COLUMN, should be deleted)]
* LEVEL is columns from left to right. How do you do without specifying these columns?
*/
$rowsVerticalHierarhy = [
['F16', 0, 'Characteristics'],
['F16.F18', 1, 'Motor'],
['F16.F18.F20', 2, 'Speed'],
['F16.F18.F21', 2, 'HorsePower'],
['F16.F19', 1, 'Weight'],
];
$table->addColNames([0, 1, 2]); //artificial columns
foreach ($rowsVerticalHierarhy as $row){
$table->addRow($row[0]);
$table->th($row[0], $row[1], $row[2]);
} Data(array) for Left Hierarchy + JSON: https://codeshare.io/a31Jv4 2. Up Hierarchy:Data(array) for Up Hierarchy + JSON: https://codeshare.io/ay79rl //generate array(https://codeshare.io/ay79rl)
foreach ($data as $els => $el) {
$colNames[$el['path']] = is_array($el['childs']) ? ['leaf' => false, 'name' => $el['field_name']] : ['leaf' => true, 'name' => $el['field_name']];
if (isset($el['childs'])) {
$this->create($el['childs']);
}
}
// out function create gereate array
$colNames = [
'F1' => ['leaf' => false, 'name' => 'Mazda']
'F1.F6' => ['leaf' => true, 'name' => 'Mazda CX-9'],
'F1.F4' => ['leaf' => true, 'name' => 'Mazda'],
'F2' => ['leaf' => false, 'name' => 'BMW'],
'F2.F8' => ['leaf' => true, 'name' => 'BMW 750Li xDrive'],
'F2.F5' => ['leaf' => true, 'name' => 'BMW M5 xDrive'],
'F3' => ['leaf' => false, 'name' => 'Honda'],
'F3.F11' => ['leaf' => true, 'name' => 'CR-V 2.4 PRESTIGE CVT'],
'F3.F10' => ['leaf' => false, 'name' => 'Motorcycle'],
'F3.F10.F7' => ['leaf' => true, 'name' => 'CBR1000SP1 FIREBLADE'],
'F3.F10.F9' => ['leaf' => false, 'name' => 'Africa Twin Adventure Sports 2018'],
];
//and add
foreach ($colNames as $colName => $isLeaf) {
/*
if (false !== $pos = strrpos($colName, '.')) {
$caption = substr($colName, $pos + 1);
} else {
$caption = $isLeaf['name'];
}
*/
$this->_table->addColName($colName);
$depth = substr_count($colName, '.');
$rowGroupName = $rowGroupNames[$depth];
$rowName = $isLeaf['leaf']
? $rowGroupName
: $rowGroupName . '.caption';
$thead->th($rowName, $colName, $isLeaf['name']);
} 3. Matrix Values:Data(array) for Matrix Values + JSON: https://codeshare.io/GLgY6K // Here the data set is an artificial imitation of data from the database
$dataset = [
['F16.F18.F20', 'F1.F6', '192 km/h'],
['F16.F18.F21', 'F1.F6', '267 h.p'],
['F16.F19', 'F1.F6', '2100'],
['F16.F18.F20', 'F1.F4', '200 km/h'],
['F16.F18.F21', 'F1.F4', '260 h.p'],
['F16.F19', 'F1.F4', '1650'],
['F16.F18.F20', 'F2.F5', '305 km/h'],
['F16.F18.F21', 'F2.F5', '600 h.p'],
['F16.F19', 'F2.F5', '1700'],
['F16.F18.F20', 'F2.F8', '240 km/h'],
['F16.F18.F21', 'F2.F8', '444 h.p'],
['F16.F19', 'F2.F8', '2200'],
['F16.F18.F20', 'F3.F11', '... km/h'],
['F16.F18.F21', 'F3.F11', '... h.p'],
['F16.F19', 'F3.F11', '....'],
['F16.F18.F20', 'F3.F10.F7', '... km/h'],
['F16.F18.F21', 'F3.F10.F7', '... h.p'],
['F16.F19', 'F3.F10.F7', '...'],
['F16.F18.F20', 'F3.F10.F9', '... km/h'],
['F16.F18.F21', 'F3.F10.F9', '... h.p'],
['F16.F19', 'F3.F10.F9', '...'],
];
foreach ($dataset as $data){
$tbody->td($data[0], $data[1], $data[2]);
} |
Ok, this example is more intuitive. In this case, our job is a bit easier, we can use ->tdOpenEnd() and ->thOpenEnd() to let the cells continue to the right. 3x3 red cells in top left corner: $thead->thOpenEnd('g', 0, ''); Explanation:
1x1 red cell next to "Weight": DIsclaimer If this does not help, you need to define a hierarchy for the columns on the left. |
Hello! I would like to know if it is possible to generate hierarchical tables from a data set in a database?
Is it possible to make one of the following patterns?
I have a table with a description of the hierarchical structure. The result is to output the following table: https://ibb.co/jg9Wjb
Tell me please, or advise how to implement it with the help of your library?
I can get an array of ancestors and children, like this:
I do not understand how to manipulate such data, namely, to use methods addColName(), addRowName() and th().
The text was updated successfully, but these errors were encountered: