Skip to content
View as Markdown

Controllers ​

FluentCRM Core Intermediate

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
<?php

namespace MyPlugin\Controllers;

use FluentCrm\App\Http\Controllers\Controller;

class ItemController extends Controller
{
    public function index()
    {
        // $this->request — the Request object
        // $this->response — the Response object
        return $this->sendSuccess(['items' => []]);
    }
}

The base controller (FluentCrm\App\Http\Controllers\Controller) provides:

Property / MethodDescription
$this->requestThe Request instance
$this->responseThe Response instance
$this->validate($data, $rules, $messages)Validate 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');
}
MethodDescription
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
}
MethodDescription
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 ​

RuleDescription
requiredMust be present and non-empty
nullableCan be null or empty
stringMust be a string
numericMust be numeric
integerMust be an integer
emailMust be a valid email
urlMust be a valid URL
arrayMust be an array
dateMust be a valid date
date_format:Y-m-dMust 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:nMinimum length (string) or value (number)
max:nMaximum length (string) or value (number)
size:nExact length (string), count (array), or value (number)
regex:patternMust match the regex pattern
alphaOnly alphabetic characters
alphanumOnly alphanumeric characters
alphadashAlphanumeric, dashes, and underscores
unique:table,columnValue must be unique in database table
exists:table,columnValue must exist in database table
required_if:field,valueRequired when another field equals a value
required_with:fieldRequired when another field is present
required_without:fieldRequired when another field is absent
same:fieldMust match another field's value
acceptedMust be yes, on, 1, or true
filledIf present, must not be empty
presentMust exist in request (even if empty)
digits:nMust 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 ​

MethodDefault CodeDescription
send($data, $code)200Send response with data and status code
sendSuccess($data, $code)200Send success response
sendError($data, $code)422Send 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
<?php

namespace MyPlugin\Controllers;

use FluentCrm\App\Http\Controllers\Controller;
use FluentCrm\App\Models\Subscriber;

class ContactNotesController extends Controller
{
    public function index($contactId)
    {
        $subscriber = Subscriber::findOrFail($contactId);

        $notes = $subscriber->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