Structured input objects from HTTP.
Ray.InputQuery transforms flat HTTP data into structured PHP objects through explicit type declarations. Using the #[Input] attribute, you declare which parameters come from query data, while other parameters are resolved via dependency injection.
Core Mechanism:
- Attribute-Based Control -
#[Input]explicitly marks query-sourced parameters - Prefix-Based Nesting -
assigneeId,assigneeNamefields automatically composeUserInputobjects - Type-Safe Conversion - Leverages PHP's type system for automatic scalar conversion
- DI Integration - Parameters without
#[Input]are resolved from dependency injection
The Problem:
// Manual parameter extraction and object construction
$data = $request->getParsedBody(); // or $_POST
$title = $data['title'] ?? '';
$assigneeId = $data['assigneeId'] ?? '';
$assigneeName = $data['assigneeName'] ?? '';
$assigneeEmail = $data['assigneeEmail'] ?? '';
**Ray.InputQuery Solution:**
```php
// Declarative structure definition
final class TodoInput {
public function __construct(
#[Input] public readonly string $title,
#[Input] public readonly UserInput $assignee, // Auto-composed from assigneeId, assigneeName, assigneeEmail
private LoggerInterface $logger // From DI container
) {}
}
public function createTodo(TodoInput $input) {
// $input automatically structured from request data
}composer require ray/input-queryComprehensive documentation including design philosophy, AI prompts for development assistance, and sample data examples can be found in the docs/ directory.
Ray.InputQuery converts flat query data into typed PHP objects automatically.
Define your input class with the #[Input] attribute on parameters that come from query data:
use Ray\InputQuery\Attribute\Input;
final class UserInput
{
public function __construct(
#[Input] public readonly string $name,
#[Input] public readonly string $email
) {}
}Create input objects from query data:
use Ray\InputQuery\InputQuery;
use Ray\Di\Injector;
$injector = new Injector();
$inputQuery = new InputQuery($injector);
// Create object directly from array
$user = $inputQuery->create(UserInput::class, [
'name' => 'John Doe',
'email' => 'john@example.com'
]);
echo $user->name; // John Doe
echo $user->email; // john@example.com
// Method argument resolution from $_POST
$method = new ReflectionMethod(UserController::class, 'register');
$args = $inputQuery->getArguments($method, $_POST);
$controller->register(...$args);
// Or with PSR-7 Request
$args = $inputQuery->getArguments($method, $request->getParsedBody());
$controller->register(...$args);Ray.InputQuery automatically creates nested objects from flat query data:
final class TodoInput
{
public function __construct(
#[Input] public readonly string $title,
#[Input] public readonly UserInput $assignee // Nested input
) {}
}
$todo = $inputQuery->create(TodoInput::class, [
'title' => 'Buy milk',
'assigneeId' => '123',
'assigneeName' => 'John',
'assigneeEmail' => 'john@example.com'
]);
echo $todo->title; // Buy milk
echo $todo->assignee->name; // JohnRay.InputQuery supports arrays and ArrayObject collections with the item parameter:
use Ray\InputQuery\Attribute\Input;
final class UserInput
{
public function __construct(
#[Input] public readonly string $id,
#[Input] public readonly string $name
) {}
}
final class UserListController
{
public function updateUsers(
#[Input(item: UserInput::class)] array $users // Array of UserInput objects
) {
foreach ($users as $user) {
echo $user->name; // Each element is a UserInput instance
}
}
public function processUsers(
#[Input(item: UserInput::class)] ArrayObject $users // ArrayObject collection
) {
// $users is an ArrayObject containing UserInput instances
}
}Query data format for arrays:
$data = [
'users' => [
['id' => '1', 'name' => 'John'],
['id' => '2', 'name' => 'Jane'],
['id' => '3', 'name' => 'Bob']
]
];
$args = $inputQuery->getArguments($method, $data);
// $args[0] will be an array of UserInput objectsArrayObject Inheritance Support:
Custom ArrayObject subclasses are also supported:
final class UserCollection extends ArrayObject
{
public function getFirst(): ?UserInput
{
return $this[0] ?? null;
}
}
public function handleUsers(
#[Input(item: UserInput::class)] UserCollection $users
) {
$firstUser = $users->getFirst(); // Custom method available
}Parameters without the #[Input] attribute are resolved via dependency injection:
use Ray\Di\Di\Named;
final class OrderInput
{
public function __construct(
#[Input] public readonly string $orderId, // From query
#[Input] public readonly CustomerInput $customer, // From query
#[Named('tax.rate')] private float $taxRate, // From DI
private LoggerInterface $logger // From DI
) {}
}All query keys are normalized to camelCase:
user_name→userNameuser-name→userNameUserName→userName
Ray.InputQuery expects flat key-value pairs as input. Nested array structures are not supported:
// ✅ Correct - Flat structure
$data = [
'customerName' => 'John Doe',
'customerEmail' => 'john@example.com',
'shippingCity' => 'Tokyo'
];
// ❌ Wrong - Nested arrays (e.g., from customer[name] form fields)
$data = [
'customer' => [
'name' => 'John Doe',
'email' => 'john@example.com'
]
];Why this restriction? When nested objects are flattened for database operations, all property names must be globally unique to avoid conflicts. This design ensures predictable parameter binding and prevents naming collisions. For HTML forms, use flat naming:
<!-- ✅ Correct -->
<input name="customerName">
<input name="customerEmail">
<!-- ❌ Avoid -->
<input name="customer[name]">
<input name="customer[email]">Ray.InputQuery is designed as a foundation library to be used by:
- Ray.MediaQuery - For database query integration
- BEAR.Resource - For REST resource integration
- PHP 8.1+
- ray/di ^2.0
MIT