# Custom sources

> Use an API, search service, or repository to retrieve table data.

Source: https://docs.inertiax.dev/table/guide/custom-sources/

Implement `TableSource::fetch()` when an API or repository should handle the table’s search, filters, sorting, and pagination. InertiaX passes the request as a `TableQuery`; your source returns a `TableResult` containing the requested rows and the total number of matches.

## Wrap a repository

This adapter wraps a `UserRepository` that returns a Laravel `LengthAwarePaginator`. Inside `fetch()`, forward the table request to the repository:

**Laravel**

RepositoryUsersSource::fetch() — excerpt

```php
return TableResult::fromPaginator($this->users->page(
    search: $query->search,
    sorting: $query->effectiveSorting(),
    page: $query->page,
    pageSize: $query->pageSize,
), $query);
```

Pass `page` and `pageSize` explicitly so Laravel does not read an unrelated `page` URL parameter. `effectiveSorting()` adds the row key as a tie-breaker when an active sort omits it. The [complete example](https://docs.inertiax.dev/table/guide/custom-sources/#complete-repository-example) includes the class, repository, and checks for operations the repository does not support.

## Define and render the table

Pass the source class to `data()`. Laravel resolves its constructor dependencies. Configure only the controls the repository supports: name search, ID/name sorting, and pagination. This repository has no filter support, so disable automatic filters.

**Laravel**

```php
use App\Sources\RepositoryUsersSource;
use Inertia\Inertia;
use InertiaX\Table\Columns\NumberColumn;
use InertiaX\Table\Columns\TextColumn;
use InertiaX\Table\Table;

return Inertia::render('users/index', [
    Table::make('repository_users')
        ->data(RepositoryUsersSource::class)
        ->addColumns([
            NumberColumn::make('id', 'ID')->sortable(),
            TextColumn::make('name', 'Name')->sortable()->searchable(),
            TextColumn::make('email', 'Email'),
        ])
        ->autoFilters(false),
]);
```

**React**

resources/js/pages/users/index.tsx

```tsx
import { AppTable } from '@/components/app-table';

export default function UsersIndex() {
    return <AppTable prop="repository_users" />;
}
```

Return the PHP example from a controller action with a route, as in [Your first table](https://docs.inertiax.dev/table/quick-start/). The `id` column identifies each row; use [`rowKey()`](https://docs.inertiax.dev/table/reference/table/#row-identity) for another key. Scope your repository’s query to records the current user may view. See [Authorization & security](https://docs.inertiax.dev/table/guide/security/).

Apply search, filters, and sorting across the matching dataset before selecting a page; return the total before pagination. InertiaX formats the returned cells without querying the rows again.

## Use an inline callback

For a small adapter, use `CallbackSource` instead of creating a source class:

**Laravel**

```php
use App\Sources\UserRepository;
use InertiaX\Table\Sources\CallbackSource;
use InertiaX\Table\Sources\TableQuery;
use InertiaX\Table\Sources\TableResult;

$users = app(UserRepository::class);
$source = CallbackSource::make(
    fn (TableQuery $query): TableResult => TableResult::fromPaginator(
        $users->page(
            search: $query->search,
            sorting: $query->effectiveSorting(),
            page: $query->page,
            pageSize: $query->pageSize,
        ),
        $query,
    ),
);
```

Use `data($source)` with the same columns and settings shown above. The callback runs after InertiaX validates the request. A bare `data(fn () => ...)` runs earlier to choose a source; it does not receive the completed query.

## Configure a source instance

Pass an instance when you already have a configured repository:

**Laravel**

```php
$source = new RepositoryUsersSource($repository);
```

Use `data($source)` inline or on a [reusable Table class](https://docs.inertiax.dev/table/guide/tables/#reuse-a-table-class). A fluent `data()` call overrides the class’s `data()` declaration for that use.

## Add filters or connect an API

Declare filters and clauses that your source can translate. The [API source example](https://docs.inertiax.dev/table/guide/api-source/) shows a reusable Table with select options, nested filters, and field mappings for an HTTP service. It includes a local fake service so you can run the example without API credentials.

See [Source API](https://docs.inertiax.dev/table/reference/source/) for query fields, options callbacks, and result requirements. For data that InertiaX should query automatically, use [Eloquent or collections](https://docs.inertiax.dev/table/guide/data-sources/).

## Complete repository example

Use these classes with the table configuration above. The source checks its provider’s limits; InertiaX validates the table request but cannot determine which operations your repository supports.

RepositoryUsersSource.php — complete source

app/Sources/RepositoryUsersSource.php

```php
<?php

namespace App\Sources;

use InertiaX\Table\Sources\Contracts\TableSource;
use InertiaX\Table\Sources\TableQuery;
use InertiaX\Table\Sources\TableResult;
use InvalidArgumentException;

final class RepositoryUsersSource implements TableSource
{
    public function __construct(private UserRepository $users) {}

    public function fetch(TableQuery $query): TableResult
    {
        if (! $query->paginationEnabled || $query->filters['conditions'] !== []) {
            throw new InvalidArgumentException('User repository requires pagination and does not support filters.');
        }
        if (array_diff($query->searchableColumns, ['name']) !== []) {
            throw new InvalidArgumentException('User repository searches only names.');
        }
        foreach ($query->effectiveSorting() as $sort) {
            if (! in_array($sort['key'], ['id', 'name'], true)) {
                throw new InvalidArgumentException("User repository cannot sort [{$sort['key']}].");
            }
        }

        return TableResult::fromPaginator($this->users->page(
            search: $query->search,
            sorting: $query->effectiveSorting(),
            page: $query->page,
            pageSize: $query->pageSize,
        ), $query);
    }
}
```

UserRepository.php — example repository

app/Sources/UserRepository.php

```php
<?php

namespace App\Sources;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

/** A pre-existing repository can keep its own query and pagination implementation. */
final class UserRepository
{
    public function page(string $search, array $sorting, int $page, int $pageSize): LengthAwarePaginator
    {
        $query = DB::table('users')->select(['id', 'name', 'email']);
        if ($search !== '') {
            $query->where('name', 'like', '%'.$search.'%');
        }
        foreach ($sorting as $sort) {
            $query->orderBy($sort['key'], $sort['direction']);
        }

        return $query->paginate($pageSize, page: $page);
    }
}
```
