Skip to content

Commit b7c2346

Browse files
committed
refactor: restructure column config to support additional settings
Signed-off-by: ailkiv <a.ilkiv.ye@gmail.com>
1 parent cdd0208 commit b7c2346

File tree

11 files changed

+174
-31
lines changed

11 files changed

+174
-31
lines changed

lib/Db/LegacyRowMapper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ public function findAllByView(View $view, string $userId, ?int $limit = null, ?i
301301
$rows = $this->findEntities($qb);
302302
foreach ($rows as &$row) {
303303
$row->setDataArray(array_filter($row->getDataArray(), function ($item) use ($view) {
304-
return in_array($item['columnId'], $view->getColumnsArray());
304+
return array_key_exists($item['columnId'], $view->getColumnsSettingsArray());
305305
}));
306306
}
307307
return $rows;
@@ -411,7 +411,7 @@ public function findByView(int $id, View $view): LegacyRow {
411411
$row = $this->findEntity($qb);
412412

413413
$row->setDataArray(array_filter($row->getDataArray(), function ($item) use ($view) {
414-
return in_array($item['columnId'], $view->getColumnsArray());
414+
return array_key_exists($item['columnId'], $view->getColumnsSettingsArray());
415415
}));
416416

417417
return $row;

lib/Db/View.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
* @method setTableId(int $tableId)
2626
* @method getColumns(): string
2727
* @method setColumns(string $columns)
28+
* @method getColumnSettings(): string
29+
* @method setColumnSettings(string $columnSettings)
2830
* @method getCreatedBy(): string
2931
* @method setCreatedBy(string $createdBy)
3032
* @method getCreatedAt(): string
@@ -64,6 +66,7 @@ class View extends Entity implements JsonSerializable {
6466
protected ?string $emoji = null;
6567
protected ?string $description = null;
6668
protected ?string $columns = null; // json
69+
protected ?string $columnSettings = null; // json
6770
protected ?string $sort = null; // json
6871
protected ?string $filter = null; // json
6972
protected ?bool $isShared = null;
@@ -87,6 +90,13 @@ public function getColumnsArray(): array {
8790
return $this->getArray($this->getColumns());
8891
}
8992

93+
/**
94+
* @return array<string, array{order: int}>
95+
*/
96+
public function getColumnsSettingsArray(): array {
97+
return $this->getArray($this->getColumnSettings());
98+
}
99+
90100
/**
91101
* @psalm-suppress MismatchingDocblockReturnType
92102
* @return list<array{columnId: int, mode: 'ASC'|'DESC'}>
@@ -125,6 +135,10 @@ public function setColumnsArray(array $array):void {
125135
$this->setColumns(\json_encode($array));
126136
}
127137

138+
public function setColumnsSettingsArray(array $array):void {
139+
$this->setColumnSettings(\json_encode($array));
140+
}
141+
128142
public function setSortArray(array $array):void {
129143
$this->setSort(\json_encode($array));
130144
}
@@ -160,7 +174,7 @@ public function jsonSerialize(): array {
160174
'createdAt' => $this->createdAt ?: '',
161175
'lastEditBy' => $this->lastEditBy ?: '',
162176
'lastEditAt' => $this->lastEditAt ?: '',
163-
'columns' => $this->getColumnsArray(),
177+
'columnSettings' => $this->getColumnsSettingsArray(),
164178
'sort' => $this->getSortArray(),
165179
'isShared' => (bool)$this->isShared,
166180
'favorite' => $this->favorite,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Tables\Migration;
11+
12+
use Closure;
13+
use Doctrine\DBAL\Exception;
14+
use OCP\DB\ISchemaWrapper;
15+
use OCP\DB\Types;
16+
use OCP\Migration\IOutput;
17+
use OCP\Migration\SimpleMigrationStep;
18+
19+
class Version001000Date20240328000000 extends SimpleMigrationStep {
20+
/**
21+
* @param IOutput $output
22+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
23+
* @param array $options
24+
* @return null|ISchemaWrapper
25+
* @throws Exception
26+
*/
27+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
28+
/** @var ISchemaWrapper $schema */
29+
$schema = $schemaClosure();
30+
31+
if ($schema->hasTable('tables_views')) {
32+
$table = $schema->getTable('tables_views');
33+
34+
// Add new column_settings field
35+
if (!$table->hasColumn('column_settings')) {
36+
$table->addColumn('column_settings', Types::JSON, [
37+
'notnull' => false,
38+
'comment' => 'JSON structure for column-specific settings like order, visibility, etc.',
39+
]);
40+
}
41+
42+
return $schema;
43+
}
44+
45+
return null;
46+
}
47+
48+
/**
49+
* @param IOutput $output
50+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
51+
* @param array $options
52+
*/
53+
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
54+
/** @var ISchemaWrapper $schema */
55+
$schema = $schemaClosure();
56+
57+
if ($schema->hasTable('tables_views')) {
58+
// Get database connection
59+
$connection = \OC::$server->get(\OCP\IDBConnection::class);
60+
61+
// Get all views
62+
$qb = $connection->getQueryBuilder();
63+
$qb->select('id', 'columns')
64+
->from('tables_views')
65+
->where($qb->expr()->isNotNull('columns'));
66+
67+
$result = $qb->executeQuery();
68+
$views = $result->fetchAll();
69+
70+
// Update each view
71+
foreach ($views as $view) {
72+
if (empty($view['columns'])) {
73+
continue;
74+
}
75+
76+
// Parse existing columns JSON
77+
$columns = json_decode($view['columns'], true);
78+
if (!is_array($columns)) {
79+
continue;
80+
}
81+
82+
// Create new column_settings structure
83+
$columnSettings = [];
84+
foreach ($columns as $order => $columnId) {
85+
$columnSettings[$columnId] = [
86+
'order' => $order,
87+
];
88+
}
89+
90+
// Update the view with new column_settings
91+
$updateQb = $connection->getQueryBuilder();
92+
$updateQb->update('tables_views')
93+
->set('column_settings', $updateQb->createNamedParameter(json_encode($columnSettings)))
94+
->where($updateQb->expr()->eq('id', $updateQb->createNamedParameter($view['id'], \PDO::PARAM_INT)));
95+
96+
$updateQb->executeStatement();
97+
}
98+
99+
$result->closeCursor();
100+
}
101+
}
102+
}

lib/ResponseDefinitions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* lastEditBy: string,
2626
* lastEditAt: string,
2727
* description: string|null,
28-
* columns: int[],
28+
* column_settings: array<int, array{order: int}>,
2929
* sort: list<array{columnId: int, mode: 'ASC'|'DESC'}>,
3030
* filter: list<list<array{columnId: int, operator: 'begins-with'|'ends-with'|'contains'|'is-equal'|'is-greater-than'|'is-greater-than-or-equal'|'is-lower-than'|'is-lower-than-or-equal'|'is-empty', value: string|int|float}>>,
3131
* isShared: bool,

lib/Service/ColumnService.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ public function findAllByView(int $viewId, ?string $userId = null): array {
9999
$this->logger->error($e->getMessage(), ['exception' => $e]);
100100
throw new NotFoundError(get_class($this) . ' - ' . __FUNCTION__ . ': ' . $e->getMessage());
101101
}
102-
$viewColumnIds = $view->getColumnsArray();
103-
$viewColumns = $this->mapper->findAll($viewColumnIds);
102+
$columnsSettings = $view->getColumnsSettingsArray();
103+
$viewColumns = $this->mapper->findAll(array_keys($columnsSettings));
104+
104105
return $this->enhanceColumns($viewColumns);
105106
}
106107

@@ -230,7 +231,10 @@ public function create(
230231
}
231232
if (isset($view) && $view) {
232233
// Add columns to view(s)
233-
$this->viewService->update($view->getId(), ['columns' => json_encode(array_merge($view->getColumnsArray(), [$entity->getId()]))], $userId, true);
234+
$columnsSettings = $view->getColumnsSettingsArray();
235+
$nextOrder = empty($columnsSettings) ? 0 : max(array_column($columnsSettings, 'order')) + 1;
236+
$columnsSettings[$entity->getId()] = ['order' => $nextOrder];
237+
$this->viewService->update($view->getId(), ['column_settings' => json_encode($columnsSettings)], $userId, true);
234238
}
235239
foreach ($selectedViewIds as $viewId) {
236240
try {
@@ -244,7 +248,10 @@ public function create(
244248
$this->logger->error($e->getMessage(), ['exception' => $e]);
245249
throw new NotFoundError(get_class($this) . ' - ' . __FUNCTION__ . ': ' . $e->getMessage());
246250
}
247-
$this->viewService->update($viewId, ['columns' => json_encode(array_merge($view->getColumnsArray(), [$entity->getId()]))], $userId, true);
251+
$columnsSettings = $view->getColumnsSettingsArray();
252+
$nextOrder = empty($columnsSettings) ? 0 : max(array_column($columnsSettings, 'order')) + 1;
253+
$columnsSettings[$entity->getId()] = ['order' => $nextOrder];
254+
$this->viewService->update($viewId, ['column_settings' => json_encode($columnsSettings)], $userId, true);
248255
}
249256
return $this->enhanceColumn($entity);
250257
}

lib/Service/RowService.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ public function findAllByView(int $viewId, string $userId, ?int $limit = null, ?
100100
try {
101101
if ($this->permissionsService->canReadRowsByElementId($viewId, 'view', $userId)) {
102102
$view = $this->viewMapper->find($viewId);
103-
$columnsArray = $view->getColumnsArray();
104-
$filterColumns = $this->columnMapper->findAll($columnsArray);
103+
$columnsSettings = $view->getColumnsSettingsArray();
104+
$filterColumns = $this->columnMapper->findAll(array_keys($columnsSettings));
105105
$tableColumns = $this->columnMapper->findAllByTable($view->getTableId());
106106

107107
return $this->row2Mapper->findAll($tableColumns, $filterColumns, $view->getTableId(), $limit, $offset, $view->getFilterArray(), $view->getSortArray(), $userId);
@@ -184,7 +184,7 @@ public function create(?int $tableId, ?int $viewId, RowDataInput|array $data): R
184184
throw new PermissionError('create row at the view id = ' . $viewId . ' is not allowed.');
185185
}
186186

187-
$columns = $this->columnMapper->findMultiple($view->getColumnsArray());
187+
$columns = $this->columnMapper->findMultiple(array_keys($view->getColumnsSettingsArray()));
188188
}
189189
if ($tableId) {
190190
try {
@@ -257,7 +257,7 @@ private function enhanceWithViewDefaults(?View $view, RowDataInput $data): RowDa
257257
}
258258

259259
// Skip if the column is already visible in the view
260-
if (in_array($filter['columnId'], $view->getColumnsArray())) {
260+
if (in_array($filter['columnId'], array_keys($view->getColumnsSettingsArray()))) {
261261
continue;
262262
}
263263

@@ -427,7 +427,7 @@ public function updateSet(
427427

428428
// fetch all needed columns
429429
try {
430-
$columns = $this->columnMapper->findMultiple($view->getColumnsArray());
430+
$columns = $this->columnMapper->findMultiple(array_keys($view->getColumnsSettingsArray()));
431431
} catch (Exception $e) {
432432
$this->logger->error($e->getMessage(), ['exception' => $e]);
433433
throw new InternalError(get_class($this) . ' - ' . __FUNCTION__ . ': ' . $e->getMessage());
@@ -620,7 +620,7 @@ private function filterRowResult(?View $view, Row2 $row): Row2 {
620620
return $row;
621621
}
622622

623-
$row->filterDataByColumns($view->getColumnsArray());
623+
$row->filterDataByColumns(array_keys($view->getColumnsSettingsArray()));
624624

625625
return $row;
626626
}

lib/Service/ViewService.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public function update(int $id, array $data, ?string $userId = null, bool $skipT
257257
throw new PermissionError('PermissionError: can not update view with id ' . $id);
258258
}
259259

260-
$updatableParameter = ['title', 'emoji', 'description', 'columns', 'sort', 'filter'];
260+
$updatableParameter = ['title', 'emoji', 'description', 'sort', 'filter', 'columnSettings'];
261261

262262
foreach ($data as $key => $value) {
263263
if (!in_array($key, $updatableParameter)) {
@@ -450,11 +450,11 @@ private function enhanceView(View $view, string $userId): void {
450450
if ($rawSortArray) {
451451
$view->setSortArray(
452452
array_map(static function (array $sortRule) use ($view): array {
453-
$columnsArray = $view->getColumnsArray();
453+
$columnsSettings = $view->getColumnsSettingsArray();
454454
if (isset($sortRule['columnId'])
455455
&& (
456456
Column::isValidMetaTypeId($sortRule['columnId'])
457-
|| in_array($sortRule['columnId'], $columnsArray, true)
457+
|| array_key_exists($sortRule['columnId'], $columnsSettings)
458458
)
459459
) {
460460
return $sortRule;
@@ -525,10 +525,12 @@ function (array $filter) use ($columnId) {
525525

526526
);
527527

528+
$columnSettings = $view->getColumnsSettingsArray();
529+
unset($columnSettings[$columnId]);
528530
$data = [
529-
'columns' => json_encode(array_values(array_diff($view->getColumnsArray(), [$columnId]))),
530531
'sort' => json_encode($filteredSortingRules),
531532
'filter' => json_encode($filteredFilters),
533+
'column_settings' => json_encode($columnSettings),
532534
];
533535

534536
$this->update($view->getId(), $data);

src/modules/main/sections/Dashboard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
{{ view.rowsCount }}
4040
</td>
4141
<td class="link-to-view number-column" @click="openView(view)">
42-
{{ view.columns.length }}
42+
{{ view.columnSettings ? Object.keys(view.columnSettings).length : 0 }}
4343
</td>
4444
<td class="link-to-view" @click="openView(view)">
4545
{{ view.lastEditAt | niceDateTime }}

src/modules/modals/ViewSettings.vue

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<SelectedViewColumns
3030
:columns="allColumns"
3131
:selected-columns="selectedColumns"
32-
:view-column-ids="viewSetting ? (view.columns ?? columns.map(col => col.id)) : null"
32+
:view-column-ids="viewSetting ? (view.columnSettings ? Object.keys(view.columnSettings) : columns.map(col => col.id)) : null"
3333
:generated-column-ids="viewSetting ? (generatedView.columns ?? [...selectedColumns]) : null"
3434
:disable-hide="!canManageTable(view)" />
3535
</NcAppSettingsSection>
@@ -151,11 +151,14 @@ export default {
151151
generateViewConfigData() {
152152
if (!this.viewSetting) return this.view
153153
const mergedViewSettings = JSON.parse(JSON.stringify(this.view))
154-
if (this.view.columns) {
154+
if (this.view.columnSettings) {
155155
if (this.viewSetting.hiddenColumns && this.viewSetting.hiddenColumns.length !== 0) {
156-
mergedViewSettings.columns = this.view.columns.filter(id => !this.viewSetting.hiddenColumns.includes(id))
156+
mergedViewSettings.columnSettings = Object.fromEntries(
157+
Object.entries(this.view.columnSettings)
158+
.filter(([id]) => !this.viewSetting.hiddenColumns.includes(parseInt(id)))
159+
)
157160
} else {
158-
mergedViewSettings.columns = this.view.columns
161+
mergedViewSettings.columnSettings = this.view.columnSettings
159162
}
160163
}
161164
if (this.viewSetting.sorting) {
@@ -225,8 +228,12 @@ export default {
225228
// Show columns of view first
226229
if (this.canManageTable(this.view)) this.allColumns = this.columns.concat(MetaColumns)
227230
else this.allColumns = this.columns
228-
if (this.view.columns) {
229-
this.allColumns = this.view.columns.map(id => this.allColumns.find(col => col.id === id)).concat(this.allColumns.filter(col => !this.view.columns.includes(col.id)))
231+
if (this.view.columnSettings) {
232+
this.allColumns.sort((a, b) => {
233+
const orderA = this.view.columnSettings[a.id]?.order ?? Number.MAX_SAFE_INTEGER
234+
const orderB = this.view.columnSettings[b.id]?.order ?? Number.MAX_SAFE_INTEGER
235+
return orderA - orderB
236+
})
230237
}
231238
},
232239
async saveView() {
@@ -278,11 +285,15 @@ export default {
278285
},
279286
async updateViewToBE(id) {
280287
const newSelectedColumnIds = this.allColumns.map(col => col.id).filter(id => this.selectedColumns.includes(id))
288+
const columnSettings = {}
289+
newSelectedColumnIds.forEach((id, index) => {
290+
columnSettings[id] = { order: index }
291+
})
281292
const data = {
282293
data: {
283294
title: this.title,
284295
emoji: this.icon,
285-
columns: JSON.stringify(newSelectedColumnIds),
296+
columnSettings: JSON.stringify(columnSettings),
286297
},
287298
}
288299
// Update sorting rules if they don't contain hidden rules (= rules regarding rows the user can not see) that were not overwritten
@@ -310,7 +321,7 @@ export default {
310321
this.title = this.mutableView.title ?? ''
311322
this.icon = this.mutableView.emoji ?? this.loadEmoji()
312323
this.errorTitle = false
313-
this.selectedColumns = this.mutableView.columns ? [...this.mutableView.columns] : null
324+
this.selectedColumns = this.mutableView.columnSettings ? Object.keys(this.mutableView.columnSettings).map(id => parseInt(id)) : null
314325
this.allColumns = []
315326
this.localLoading = false
316327
this.columns = null

src/modules/navigation/partials/NavigationViewItem.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export default {
206206
if (newViewId) {
207207
data = {
208208
data: {
209-
columns: JSON.stringify(this.view.columns),
209+
columnSettings: JSON.stringify(columnSettings),
210210
filter: JSON.stringify(this.view.filter),
211211
sort: JSON.stringify(this.view.sort),
212212
},

0 commit comments

Comments
 (0)