Essentials

Requests & Responses

Handle HTTP input and output

Learn how to work with HTTP requests and return properly formatted JSON responses.

Request Basics

Access the current Symfony Request instance in controllers (injected as $this->request):

public function store()
{
    // All JSON/form body input
    $data = $this->request->request->all();

    // Specific fields (null if missing)
    $email = $this->request->request->get('email');

    // With default value
    $name = $this->request->request->get('name', 'Guest');
}

Getting Input Data

JSON Body

// POST /api/users
// Body: {"name": "John", "email": "[email protected]"}

public function store()
{
    $name = $this->request->request->get('name');
    $email = $this->request->request->get('email');

    // Or get all (JSON auto parsed)
    $data = $this->request->request->all();
    // ['name' => 'John', 'email' => '[email protected]']
}

Query Parameters

// GET /api/users?page=2&limit=10

public function index()
{
    $page = (int) $this->request->query->get('page', 1);
    $limit = (int) $this->request->query->get('limit', 20);
}

Route Parameters

// Route: /users/{id}/posts/{postId}

public function show(string $id, string $postId)
{
    // Parameters injected automatically
}

Headers

$token = $this->request->headers->get('Authorization');
$contentType = $this->request->headers->get('Content-Type');

Files

public function upload()
{
    $file = $this->request->files->get('avatar');

    if ($file) {
        $path = $file->move('/uploads', $file->getClientOriginalName());
    }
}

Response Helpers

Glueful provides consistent JSON response helpers:

Success Responses

use Glueful\Http\Response;

// Basic success (default message = "Success")
return Response::success($data);
// {"success": true, "message": "Success", "data": {...}}

// Custom message
return Response::success($users, 'Users retrieved');
// {"success": true, "message": "Users retrieved", "data": [...]} 

// Created (201)
return Response::created($user);
// {"success": true, "message": "Created successfully", "data": {...}}

// No content (204)
return Response::noContent();
// HTTP 204 empty body

Error Responses

// Not found (404)
return Response::notFound('User not found');
// {
//   "success": false,
//   "message": "User not found",
//   "error": {
//     "code": 404,
//     "timestamp": "2024-01-15T10:30:00Z",
//     "request_id": "req_ab12cd34ef",
//     "details": { ... } // only when provided
//   }
// }

// Unauthorized (401)
return Response::unauthorized('Invalid token');

// Forbidden (403)
return Response::forbidden('Access denied');

// Validation error (422) - details show field errors
return Response::validation(['email' => 'Email is required']);

// Custom error with extra context
return Response::error('Something went wrong', 500, ['operation' => 'import']);

Pagination

public function index()
{
    $page = (int) $this->request->query->get('page', 1);
    $perPage = 20;

    $users = $this->db->table('users')
        ->limit($perPage)
        ->offset(($page - 1) * $perPage)
        ->get();

    $total = $this->db->table('users')->count();

    return Response::paginated($users, $total, $page, $perPage);
}

// Returns (flattened pagination + default message):
// {
//   "success": true,
//   "message": "Data retrieved successfully",
//   "data": [...],
//   "current_page": 1,
//   "per_page": 20,
//   "total": 100,
//   "total_pages": 5,
//   "has_next_page": true,
//   "has_previous_page": false
// }

Complete Examples

Simple CRUD

class TaskController
{
    public function index()
    {
        $tasks = $this->db->table('tasks')->get();
        return Response::success($tasks);
    }

    public function store()
    {
    $data = $this->request->request->all();

        $task = $this->db->table('tasks')->insert([
            'title' => $data['title'],
            'description' => $data['description'] ?? null,
            'completed' => false
        ]);

        return Response::created($task);
    }

    public function show(string $id)
    {
        $task = $this->db->table('tasks')
            ->where('uuid', $id)
            ->first();

        if (!$task) {
            return Response::notFound('Task not found');
        }

        return Response::success($task);
    }

    public function update(string $id)
    {
    $data = $this->request->request->all();

        $updated = $this->db->table('tasks')
            ->where('uuid', $id)
            ->update($data);

        if (!$updated) {
            return Response::notFound('Task not found');
        }

        return Response::success(null, 'Task updated');
    }

    public function destroy(string $id)
    {
        $deleted = $this->db->table('tasks')
            ->where('uuid', $id)
            ->delete();

        if (!$deleted) {
            return Response::notFound('Task not found');
        }

        return Response::noContent();
    }
}

With Validation

public function store()
{
    $data = $this->request->request->all();

    // Validate
    $validated = $this->validate($data, [
        'title' => 'required|min:3',
        'email' => 'required|email'
    ]);

    // Validation passed
    $task = $this->db->table('tasks')->insert($validated);

    return Response::created($task);
}

Search & Filter

public function index()
{
    $query = $this->db->table('products');

    // Search
    if ($search = $this->request->query->get('search')) {
        $query->where('name', 'LIKE', "%{$search}%");
    }

    // Filter by category
    if ($category = $this->request->query->get('category')) {
        $query->where('category', $category);
    }

    // Sort
    $sortBy = $this->request->query->get('sort', 'created_at');
    $sortOrder = $this->request->query->get('order', 'desc');
    $query->orderBy($sortBy, $sortOrder);

    // Paginate
    $page = (int) $this->request->query->get('page', 1);
    $perPage = 20;

    $products = $query
        ->limit($perPage)
        ->offset(($page - 1) * $perPage)
        ->get();

    $total = $query->count();

    return Response::paginated($products, $total, $page, $perPage);
}

Request Methods

Checking Request Method

if ($this->request->isMethod('POST')) {
    // Handle POST
}

if ($this->request->isMethod('GET')) {
    // Handle GET
}

Getting All Headers

$headers = $this->request->headers->all();

Getting Client IP

$ip = $this->request->getClientIp();

Checking for AJAX

if ($this->request->isXmlHttpRequest()) {
    // AJAX request
}

Response Customization

Custom Status Code

return Response::success($data)->setStatusCode(202);

Custom Headers

return Response::success($data)
    ->header('X-Custom-Header', 'value')
    ->header('X-Rate-Limit', '100');

Download Response

// Provide your own file bytes then mark as download
$contents = file_get_contents('/path/to/file.pdf');
return Response::success(['file' => base64_encode($contents)])
    ->asDownload('file.pdf', 'application/pdf');

Or serve a real file (stream) with the helper:

return response()->file('/path/to/file.pdf'); // inline disposition
// For attachment: response()->file('/path/to/file.pdf', 'file.pdf', [], 'attachment');

File Response

return response()->file('/path/to/image.jpg');

Common Patterns

API with Meta Data

return Response::successWithMeta($users, [
    'filters_applied' => ['status' => 'active'],
    'generated_at' => date('c')
]);

// Returns:
// {
//   "success": true,
//   "message": "Data retrieved successfully",
//   "data": [...],
//   "filters_applied": {"status":"active"},
//   "generated_at": "2024-01-15T10:30:00Z"
// }

Error with Details

return Response::error('Quota exceeded', 403, [
    'plan' => 'free',
    'limit' => 1000,
    'current' => 1000,
    'upgrade_url' => '/pricing'
]);

Conditional Response

public function show(string $id)
{
    $user = $this->db->table('users')
        ->where('uuid', $id)
        ->first();

    return $user
        ? Response::success($user)
        : Response::notFound('User not found');
}

Best Practices

Always Return JSON

// ✅ Good
return Response::success($data);

// ❌ Avoid
return $data; // Might not be properly formatted

Use Appropriate Status Codes

// Create → 201
return Response::created($user);

// Delete → 204
return Response::noContent();

// Not found → 404
return Response::notFound();

// Validation error → 422
return Response::validation($errors);

Include Error Context

// ✅ Helpful
return Response::notFound('Product #' . $id . ' not found');

// ❌ Less helpful
return Response::notFound();

Troubleshooting

Empty request body?

  • Check Content-Type: application/json header is set
  • Verify JSON is valid

Parameters not available?

  • Route parameters must match method signature
  • Query parameters use $this->request->query->get()
  • Body parameters use $this->request->request->all() / ->get()

Response not JSON?

  • Always use Response:: helpers
  • Check you're returning the Response object
  • Ensure JSON body has valid syntax

Need raw request data?

  • Use RequestHelper::getRequestData() or controller's protected getRequestData()

Correlate errors?

  • Use the request_id in error responses for tracing (logged automatically)

Next Steps