Skip to content

feat(views): allow views without a table - #13

Open
remko48 wants to merge 3 commits into
mainfrom
stack/g1-views-without-table
Open

remko48 wants to merge 3 commits into
mainfrom
stack/g1-views-without-table

Conversation

@remko48

@remko48 remko48 commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Views can now exist without a table.
With this PR we prepare for the use of pages inside an application.
Views also get a type (table or grid).
Every existing view becomes table, so nothing changes for current users.

What changes

  • Migration: tables_views gets a type column with the default table. table_id was already nullable (for federated views), so a view without a table needs no other schema change.
  • Ownership: a view normally inherits its ownership from its table. A view without a table is owned by the user who created it.
  • Creating: ViewService::create() accepts no table when the type is not table. A table view still needs a table, and a view without a table cannot be changed to table later.
  • Technical name: a view without a table is always named by its id (view_<id>). Setting a technical name on it is rejected, and an import drops it. The unique index on (table_id, technical_name) treats a missing table differently per database.
  • Guards: places that assume every view has a table now handle the missing table:
    • rows: listing returns nothing and the row count is 0; creating rows, importing or adding columns returns a not-found error
    • columns, relations and permission inheritance: nothing to inherit or show
    • activity: activities belong to a table, so a view without a table creates none
  • Listing: the user's own views without a table are now shown in their shared views, because no table shows them.
  • User deletion: When a user is deleted, views without a table are also deleted if the user owns them.
  • API: tableId is now nullable and type is added to the view response (OpenAPI and TS types regenerated). type can already be changed through the existing view update endpoints. There is no new endpoint yet; creating a view without a table through the API comes in a later PR of this stack.

🖼️ Screenshots

Backend only, no UI changes.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

A view can now exist without a table and carries a type (table or grid),
so an application can hold pages that are not tied to one table.

- tables_views gets a type column and table_id becomes nullable
- a view without a table is owned by its creator and listed for them
- row, column, import, relation, permission and activity code skips
  views without a table instead of failing on the missing table
- views without a table are removed with their owner

Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
Comment thread lib/Db/ViewMapper.php
@remko48
remko48 added this pull request to stack #15 October 12, 2026 08:26
Comment thread lib/Db/View.php Outdated
Comment thread lib/Service/RowService.php
Comment thread lib/Model/ViewUpdateInput.php Outdated
Comment thread lib/Migration/Version2400Date20261012000000.php Outdated
Federated views have no table_id either, so a missing table alone does not
mean a view is standalone. View::isStandalone() checks for no table and no
external id, and the guards, the owner fallback and the query for a user's
standalone views now use it.

- a row update, delete or presence check against a view without a table
  no longer ends in a TypeError: the view holds no rows
- the view type is validated with the rest of the input and is typed as
  table or grid in the API
- the migration no longer makes table_id nullable, it already is

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
The unique index on (table_id, technical_name) does not hold for a missing
table on MySQL, PostgreSQL and SQLite, and on Oracle it makes a name unique
across the whole instance. A view without a table is therefore always named
view_<id>: create and update refuse a chosen name, and an import ignores the
name from its source. The page a user names is the application's menu item,
whose technical name is unique within the application.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants