# Data Sources

A View reads its rows from one data source, chosen in **Data source** under
**View settings**. Every source works with every layout type, and the
server runs search, filters, sorting, paging and export for all of them, so
the browser never holds more than it shows.

## Choosing a source

The **Data source** field groups its choices:

| Group                | Sources                                                          |
| -------------------- | ---------------------------------------------------------------- |
| WordPress            | **Post type**, **Custom table**                                  |
| Remote               | **Custom REST URL**, **HTTP CSV**, **HTTP JSON**, **HTTP JSONC** |
| Filesystem           | **Path CSV**, **Path JSON**, **Path JSONC**, **Path SQLite**     |
| Media Library        | **Media CSV**, **Media JSON**, **Media JSONC**                   |
| Integrations, Custom | Presets that site code or WooCommerce registers                  |

A new View reads the Posts post type.

## Post type

**Post type** is the default and the only source SleekView can write to.
Inline table editing, moving Kanban cards, adding, editing and deleting cards
and feedback items, upvotes and comments all change real posts, each checked
against the user's WordPress permissions for that post. Choose the post type
in **Post type**.

## Every other source is read-only

The other sources render the same layouts without write actions, and the
inspector hides the settings that would need them: inline editing; Kanban
sorting, add buttons, edit buttons, upvotes, the uncategorized column and
comments; Feedback upvotes, edit button, sort options, submission and
comments.

## Custom table

**Custom table** queries a table of the WordPress database directly. Pick it
in the **Custom table** field, which lists every table in the database, then
map its columns in [Field Mapping](#field-mapping). The users and user meta
tables are listed but never queried; a View of either shows no rows.

## Remote sources

### Custom REST URL

**REST URL template** holds an endpoint that should do the searching, sorting
and paging itself. SleekView fetches it on each query and first replaces
these tags with the layout's current state:

| Tag            | Replaced with                                                                                |
| -------------- | -------------------------------------------------------------------------------------------- |
| `{pageNumber}` | The requested page                                                                           |
| `{perPage}`    | Rows per page, at most 500; Kanban, Feedback and Charts layouts read every row 500 at a time |
| `{search}`     | The search text                                                                              |
| `{orderBy}`    | The sort column                                                                              |
| `{order}`      | `ASC` or `DESC`                                                                              |

```text
https://example.com/wp-json/custom/v1/items?page={pageNumber}&per_page={perPage}&search={search}&orderby={orderBy}&order={order}
```

SleekView requests only URLs WordPress considers safe for a server-side
request, which excludes private and local network addresses other than the
site's own host. A URL may name a port only when it is 80, 443 or 8080, or
the site's own port on the site's own host, and every redirect passes the
same check. The response is JSON, comments allowed, in one of the
[shapes below](#json-and-jsonc). When it
also carries any of `totalPosts`, `totalPages`, `postsPerPage` or
`currentPage`, SleekView treats the rows as already searched, sorted and
paged by the endpoint and uses those numbers for the pagination:

```json
{
  "items": [{ "id": 21, "title": "Fix checkout copy", "status": "Review" }],
  "totalPosts": 87,
  "totalPages": 5,
  "postsPerPage": 20,
  "currentPage": 2
}
```

Without them SleekView searches, sorts and pages the rows it received. The
layout's filters are applied to the returned rows either way.

### HTTP CSV, HTTP JSON and HTTP JSONC

These fetch a file from a URL (**CSV URL**, **JSON URL** or **JSONC URL**) on
each query, with the same URL check, and parse it on the server. A request
waits up to 15 seconds and follows up to three redirects; a preset can change
both. A failed request or a status outside 200-299 shows no rows.

## Filesystem sources

**Path CSV**, **Path JSON**, **Path JSONC** and **Path SQLite** read a file
on the server. The path is relative to the WordPress root, such as
`/wp-content/uploads/imports/data.csv`. SleekView reads only files inside the
WordPress root with the extension `csv`, `json`, `jsonc`, `txt`, `tsv`,
`sqlite`, `sqlite3` or `db`.

### Path SQLite

**Path SQLite** queries a SQLite database with PHP's PDO and its `pdo_sqlite`
driver, which the server must have. **SQLite table** names the table to read;
when the file holds exactly one table you can leave it empty. Search,
filters, sorting and paging run as SQL against that table.

## Media library sources

**Media CSV**, **Media JSON** and **Media JSONC** read a file from the media
library, chosen in the file field. The View stores the attachment's ID and
reads its file on each query. Use them when editors should manage the file
themselves.

## File formats

### CSV

The first row holds the column headers and each later row becomes one row of
the View. The delimiter is detected from the header row: comma, semicolon, tab
or pipe.

```text
id,title,status,owner
1,Redesign homepage,Backlog,Anna
2,Write launch post,In Progress,Ben
```

### JSON and JSONC

A JSON source is either an array of row objects, or an object that holds the
rows under `rows`, `data`, `posts` or `items`, checked in that order. JSONC is
JSON that may also contain comments and trailing commas, which SleekView
removes before decoding, so it suits files people edit by hand.

```javascript
{
  // Roadmap items maintained by the product team
  "items": [
    { "id": 1, "title": "Redesign homepage", "status": "Backlog", },
  ],
}
```

## Field mapping

Sources other than a post type have a **Field mapping** section, which tells
the layouts which source column plays each role:

| Field       | Used for                          |
| ----------- | --------------------------------- |
| **ID**      | The row's unique identifier       |
| **Title**   | The title of a card or item       |
| **Content** | The body or description of a card |
| **Date**    | The date a feedback item shows    |

A layout still shows the raw columns without a mapping, but cards and
feedback items need a title to read well.

## Column labels

**Column labels** renames columns for display without changing the source.
Each column shows its machine name and a **Display name** field; clear the
field to go back to the source's own label. The names apply to table
headers, column choices, sorting, filters and export in every layout of the
View, and a different View of the same source can use different names. Post
type sources have it too.

## Presets

A preset is a ready-made data source: choosing it in the **Data source**
field fills in the View's data source in one step. Like any change of source,
it clears the layouts' column settings, as
[View Settings](/docs/view/views/general.md#general) describes; nothing else in
the View changes. A preset that fixes the field mapping or the column labels
hides that section from the editor.

### Built-in WooCommerce presets

While WooCommerce is active, **Integrations** offers three Custom Table
presets, each only when its table exists:

- **WooCommerce: Order Stats**, the `wc_order_stats` table
- **WooCommerce: Product Lookup**, the `wc_product_meta_lookup` table
- **WooCommerce: Customer Lookup**, the `wc_customer_lookup` table

Each comes with column labels, and Order Stats and Customer Lookup with a
field mapping.

### Registering a preset

Site code registers presets with the `sleekView/dataSourcePresets` filter;
its [reference](/docs/view/reference/hooks.md) lists the fields a preset takes.
`dataSource` is stored with the View like a data source configured by hand.
`fetch` stays on the server: it carries request headers, a timeout and a
redirect limit for a remote source, and never reaches the browser, which makes
it the place for credentials.

```php
add_filter('sleekView/dataSourcePresets', function ($presets) {
    $presets[] = [
        'key' => 'product-api',
        'group' => 'custom',
        'label' => 'Product API',
        'type' => 'customRest',
        'dataSource' => [
            'url' => 'https://example.com/api/products?page={pageNumber}&per_page={perPage}&search={search}',
            'fieldMapping' => [
                'id' => 'id',
                'title' => 'title',
                'content' => 'description',
                'date' => 'createdAt',
            ],
        ],
        'fetch' => [
            'headers' => ['Authorization' => 'Bearer secret-token'],
            'timeout' => 20,
        ],
    ];

    return $presets;
});
```

`type` is one of `customTable`, `customRest`, `httpCsv`, `httpJson`,
`httpJsonc`, `mediaCsv`, `mediaJson`, `mediaJsonc`, `pathCsv`, `pathJson`,
`pathJsonc` or `pathSqlite`; a post type cannot be a preset. A Custom Table
preset names its table in `dataSource.tableName`:

```php
add_filter('sleekView/dataSourcePresets', function ($presets) {
    global $wpdb;

    $presets[] = [
        'key' => 'support-tickets',
        'label' => 'Support Tickets',
        'type' => 'customTable',
        'dataSource' => [
            'tableName' => $wpdb->prefix . 'support_tickets',
            'columnLabels' => [
                'ticket_id' => 'Ticket ID',
                'created_at' => 'Created',
            ],
        ],
    ];

    return $presets;
});
```
