# Controllers Controllers handle the business logic for your REST API endpoints. Extend the base controller to get access to request handling, validation, and response helpers. ## Base Controller ```php request — the Request object // $this->response — the Response object return $this->sendSuccess(['items' => []]); } } ``` The base controller (`FluentCrm\App\Http\Controllers\Controller`) provides: | Property / Method | Description | |-------------------|-------------| | `$this->request` | The [`Request`](#request-object) instance | | `$this->response` | The `Response` instance | | `$this->validate($data, $rules, $messages)` | [Validate](#validation) input data | | `$this->send($data, $code)` | Send a response with status code (default `200`) | | `$this->sendSuccess($data, $code)` | Send a success response (default `200`) | | `$this->sendError($data, $code)` | Send an error response (default `422`) | ## Request Object The `$this->request` object wraps the incoming HTTP request and provides methods to retrieve and check input data. ### Retrieving Input ```php public function store() { // Get all input $all = $this->request->all(); // Get a single value with optional default $name = $this->request->get('name', 'Unknown'); // Get only specific keys $data = $this->request->only('email', 'first_name', 'last_name'); // Get everything except specific keys $data = $this->request->except('password', 'token'); } ``` | Method | Description | |--------|-------------| | `get($key, $default)` | Get a single input value, or all input if no key provided | | `all()` | Get all input data as an array | | `input($key, $default)` | Alias for `get()` | | `only(...$keys)` | Get only the specified keys | | `except(...$keys)` | Get all input except the specified keys | | `query($key, $default)` | Get from query string (`$_GET`) only | | `post($key, $default)` | Get from POST body (`$_POST`) only | | `json($key, $default)` | Get from JSON request body | ### Safe Input Retrieval Use `getSafe()` to retrieve and sanitize input in one call: ```php // getSafe($key, $sanitizeCallback, $default) $name = $this->request->getSafe('name', 'sanitize_text_field', ''); $email = $this->request->getSafe('email', 'sanitize_email'); $orderBy = $this->request->getSafe('sort_by', 'sanitize_sql_orderby', 'id'); ``` ### Checking Input ```php if ($this->request->has('email')) { // 'email' exists and has a truthy value } if ($this->request->exists('status')) { // 'status' exists (even if empty/falsy) } if ($this->request->missing('notes')) { // 'notes' is not present in the request } ``` | Method | Description | |--------|-------------| | `has($key)` | Key exists and has a truthy value | | `exists($key)` | Key exists (including falsy values) | | `missing($key)` | Key is not present | | `hasAny(...$keys)` | At least one key has a truthy value | ### Request Info ```php $method = $this->request->method(); // GET, POST, etc. $url = $this->request->url(); // Request URL without query string $user = $this->request->user(); // Current WordPress user (WPUserProxy) ``` ### File Uploads ```php $file = $this->request->file('attachment'); ``` ## Validation The controller provides a `validate()` method that throws a `ValidationException` (HTTP 422) if validation fails. ### Basic Usage ```php public function store() { $this->validate($this->request->all(), [ 'email' => 'required|email', 'name' => 'required|string|max:255', 'status' => 'required|in:subscribed,unsubscribed,pending', ]); // If we reach here, validation passed $email = sanitize_email($this->request->get('email')); // ... } ``` ### Request-Level Validation You can also validate directly on the request object: ```php public function store() { $this->request->validate([ 'email' => 'required|email', 'name' => 'required|string', ]); // Get only validated data $data = $this->request->safe()->all(); } ``` ### Custom Error Messages ```php $this->validate($this->request->all(), [ 'email' => 'required|email|unique:fc_subscribers,email', ], [ 'email.required' => __('Email address is required.', 'my-plugin'), 'email.unique' => __('This email is already subscribed.', 'my-plugin'), ]); ``` ### Validation Error Response When validation fails, the framework automatically returns a `422` response: ```json { "message": "Unprocessable Entity!", "errors": { "email": ["The email field is required."], "status": ["The selected status is invalid."] } } ``` ### Available Rules | Rule | Description | |------|-------------| | `required` | Must be present and non-empty | | `nullable` | Can be null or empty | | `string` | Must be a string | | `numeric` | Must be numeric | | `integer` | Must be an integer | | `email` | Must be a valid email | | `url` | Must be a valid URL | | `array` | Must be an array | | `date` | Must be a valid date | | `date_format:Y-m-d` | Must match the date format | | `in:val1,val2,...` | Must be one of the listed values | | `not_in:val1,val2,...` | Must not be one of the listed values | | `min:n` | Minimum length (string) or value (number) | | `max:n` | Maximum length (string) or value (number) | | `size:n` | Exact length (string), count (array), or value (number) | | `regex:pattern` | Must match the regex pattern | | `alpha` | Only alphabetic characters | | `alphanum` | Only alphanumeric characters | | `alphadash` | Alphanumeric, dashes, and underscores | | `unique:table,column` | Value must be unique in database table | | `exists:table,column` | Value must exist in database table | | `required_if:field,value` | Required when another field equals a value | | `required_with:field` | Required when another field is present | | `required_without:field` | Required when another field is absent | | `same:field` | Must match another field's value | | `accepted` | Must be `yes`, `on`, `1`, or `true` | | `filled` | If present, must not be empty | | `present` | Must exist in request (even if empty) | | `digits:n` | Must be exactly n digits | | `mimes:jpg,png,...` | File must have one of the listed extensions | Rules are separated by `|` and can be combined: `'email' => 'required|email|max:255'`. ## Response Methods ### Returning Data Controller methods should return data using the response helpers: ```php public function index() { $items = MyModel::paginate(); return $this->sendSuccess(['items' => $items]); } public function store() { // ... create item return $this->sendSuccess([ 'message' => __('Item created', 'my-plugin'), 'item' => $item, ], 201); } public function destroy($id) { // ... delete item return $this->sendSuccess([ 'message' => __('Item deleted', 'my-plugin'), ]); } ``` ### Error Responses ```php public function show($id) { $item = MyModel::find($id); if (!$item) { return $this->sendError([ 'message' => __('Item not found', 'my-plugin'), ], 404); } return $this->sendSuccess(['item' => $item]); } ``` ### Response Methods Reference | Method | Default Code | Description | |--------|-------------|-------------| | `send($data, $code)` | `200` | Send response with data and status code | | `sendSuccess($data, $code)` | `200` | Send success response | | `sendError($data, $code)` | `422` | Send error response (code is forced to 400+) | ::: tip You can also return a plain array from any controller method — the framework wraps it in a `WP_REST_Response` automatically. ::: ## Complete Example ```php notes() ->orderBy('id', 'DESC') ->paginate(); return $this->sendSuccess([ 'notes' => $notes, ]); } public function store($contactId) { $this->validate($this->request->all(), [ 'title' => 'required|string|max:255', 'content' => 'required|string', ]); $subscriber = Subscriber::findOrFail($contactId); $note = $subscriber->notes()->create([ 'title' => sanitize_text_field($this->request->get('title')), 'description' => wp_kses_post($this->request->get('content')), 'type' => 'note', 'created_by' => get_current_user_id(), ]); return $this->sendSuccess([ 'message' => __('Note added', 'my-plugin'), 'note' => $note, ], 201); } public function destroy($contactId, $noteId) { $subscriber = Subscriber::findOrFail($contactId); $subscriber->notes()->where('id', $noteId)->delete(); return $this->sendSuccess([ 'message' => __('Note deleted', 'my-plugin'), ]); } } ``` **Source:** `app/Http/Controllers/Controller.php`, `vendor/wpfluent/framework/src/WPFluent/Http/Request/Request.php`, `vendor/wpfluent/framework/src/WPFluent/Validator/Validator.php`