Skip to content
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

Add multi column decorator support in CardTable #1959

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions demos/init-db.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,15 @@ protected function init(): void
'type' => 'string',
'ui' => [
'form' => [Form\Control\Line::class],
'table' => [Table\Column\CountryFlag::class],
'table' => [Table\Column\CountryFlag::class, 'nameField' => $this->fieldName()->client_country],
],
])
->addField($this->fieldName()->client_country, Country::hinting()->fieldName()->name);
->addField($this->fieldName()->client_country, Country::hinting()->fieldName()->name, [
// caption needs to be set explicitly because HasOneSql::addField() uses the value from referenced field
// https://github.com/atk4/data/blob/4.0.0/src/Reference/HasOneSql.php#L87
// https://github.com/atk4/data/pull/447/files#r1063408315
'caption' => 'Client Country Name',
]);

$this->addField($this->fieldName()->is_commercial, ['type' => 'boolean']);
$this->addField($this->fieldName()->currency, ['values' => ['EUR' => 'Euro', 'USD' => 'US Dollar', 'GBP' => 'Pound Sterling']]);
Expand Down
9 changes: 8 additions & 1 deletion demos/interactive/cardtable.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@

Header::addTo($app, ['Card displays read-only data of a single record']);

CardTable::addTo($app)->setModel((new Stat($app->db))->loadAny());
$entity = (new Stat($app->db))->loadAny();
$entity->project_code .= ' <b>no reload</b>';

CardTable::addTo($app)->setModel($entity);

// CardTable uses internally atk4_local_object type which uses weak references,
// force GC to test the data are kept referenced correctly
gc_collect_cycles();
19 changes: 8 additions & 11 deletions src/CardTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Atk4\Ui;

use Atk4\Data\Field;
use Atk4\Data\Model;

/**
Expand Down Expand Up @@ -34,12 +35,12 @@ public function setModel(Model $entity, array $columns = null): void
}

$data = [];
foreach ($entity->get() as $key => $value) {
foreach (array_keys($entity->get()) as $key) {
if (in_array($key, $columns, true)) {
$data[] = [
'id' => $key,
'field' => $entity->getField($key)->getCaption(),
'value' => $this->getApp()->uiPersistence->typecastSaveField($entity->getField($key), $value),
'value' => new Model\EntityFieldPair($entity, $key),
];
}
}
Expand All @@ -51,17 +52,13 @@ public function setModel(Model $entity, array $columns = null): void
$this->_bypass = false;
}

$this->addDecorator('value', [Table\Column\Multiformat::class, function (Model $row) use ($entity) {
$field = $entity->getField($row->getId());
$ret = $this->decoratorFactory(
$field,
$field->type === 'boolean' ? [Table\Column\Status::class, ['positive' => [true, 'Yes'], 'negative' => [false, 'No']]] : []
);
if ($ret instanceof Table\Column\Money) {
$ret->attr['all']['class'] = ['single line'];
$this->addDecorator('value', [Table\Column\Multiformat::class, function (Model $row, Field $field) {
$c = $this->decoratorFactory($field);
if ($c instanceof Table\Column\Money) {
$c->attr['all']['class'] = ['single line'];
}

return [$ret];
return [$c];
}]);
}
}
40 changes: 38 additions & 2 deletions src/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Atk4\Core\Factory;
use Atk4\Data\Field;
use Atk4\Data\Model;
use Atk4\Data\Model\EntityFieldPair;
use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsChain;
use Atk4\Ui\Js\JsExpression;
Expand Down Expand Up @@ -389,6 +390,40 @@ public function setModel(Model $model, array $columns = null): void
}
}

public function setSource(array $data, $fields = null): Model
{
// mainly for CardTable to support different field type for different row
// related with https://github.com/atk4/data/blob/4.0.0/tests/Persistence/StaticTest.php#L142
$dataWithObjects = [];
if (is_array(reset($data))) {
foreach ($data as $rowKey => $row) {
foreach ($row as $k => $v) {
if ($v instanceof EntityFieldPair) {
$dataWithObjects[$row['id']][$k] = $v;
$data[$rowKey][$k] = null;
}
}
}
}

$model = parent::setSource($data, $fields);

foreach ($dataWithObjects as $id => $row) {
$entity = $model->load($id);
foreach ($row as $k => $v) {
$model->getField($k)->type = 'atk4_local_object';
$entity->set($k, $v);
}
$entity->save();
}
$model->onHook(Model::HOOK_BEFORE_LOAD, function () use ($dataWithObjects) {
// useless hook, but make sure the $dataWithObjects is kept referenced from $model
$count = count($dataWithObjects);
});

return $model;
}

protected function renderView(): void
{
if (!$this->columns) {
Expand Down Expand Up @@ -420,8 +455,9 @@ protected function renderView(): void
// the same in Lister class
$modelBackup = $this->model;
try {
foreach ($this->model as $this->model) {
$this->currentRow = $this->model;
foreach ($this->model as $entity) {
$this->model = $entity;
$this->currentRow = $entity; // TODO we should either drop currentRow property or never update model property
if ($this->hook(self::HOOK_BEFORE_ROW) === false) {
continue;
}
Expand Down
7 changes: 7 additions & 0 deletions src/Table/Column/Multiformat.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public function getDataCellHtml(Field $field = null, array $attr = []): string

public function getHtmlTags(Model $row, ?Field $field): array
{
// mainly for CardTable to support different field type for different row
$fieldValue = $field->get($row);
if ($fieldValue instanceof Model\EntityFieldPair) {
$row = $fieldValue->getEntity();
$field = $fieldValue->getField();
}

$decorators = ($this->callback)($row, $field);
// we need to smartly wrap things up
$name = $field->shortName;
Expand Down