Table Resource
Table resources map custom DB tables through WordPressTableRepository/WpdbAdapter.
Deny-by-default (v0.3.0)
Table resources reject every action — including list and get — until a policy() is configured. This is the most important behavioral change in v0.3.0 for table-backed endpoints. Use a ResourcePolicy preset or supply an explicit permissions array.
Minimal example
use BetterRoute\Resource\Resource;
use BetterRoute\Resource\ResourcePolicy;
Resource::make('raw-articles')
->restNamespace('better-route/v1')
->sourceTable('ai_raw_articles', 'id')
->allow(['list', 'get', 'create', 'update', 'delete'])
->fields(['id', 'source', 'title', 'lang', 'published', 'created_at', 'updated_at'])
->filters(['source', 'lang', 'published'])
->filterSchema([
'source' => 'string',
'lang' => 'string',
'published' => 'bool',
])
->sort(['created_at', 'id'])
->maxPerPage(100)
->maxOffset(5000)
->policy(ResourcePolicy::adminOnly('manage_options'))
->register();
SQL safety model
WpdbAdapter enforces:
- identifier validation for table/field/sort names
- prepared statements for values
- prefixed table resolution via
$wpdb->prefix - (v0.3.0) cross-database table names (containing
.) are rejected - (v0.3.0) structured (array/object) write payloads are rejected at the storage boundary
Scenario: ingestion rows with typed filters
- Declare
source_idasint,publishedasbool - Requests like
?source_id=12&published=trueare type-coerced before repository call
Common mistakes
- Missing
fields()(required for table resources) - Using invalid SQL identifiers in field names
- Assuming unknown query params are ignored
- (v0.3.0) skipping
policy()— every action returns403without an explicit policy - (v0.3.0) attempting to reference a foreign database via
other_db.table— rejected at the adapter layer
Validation checklist
- list queries use LIMIT/OFFSET with prepared args
- invalid identifiers fail early
- create/update payload keys outside allowlist return
400 validation_failed