Controllers

Organize your application logic with powerful, feature-rich controllers

Clean Architecture
Built-in Security
Dependency Injection

Overview

Controllers are the heart of your Yabasi application, handling HTTP requests and coordinating your application's response. They provide a clean, organized way to group related request handling logic.

Automatic DI

Dependency injection out of the box for clean, testable code.

Built-in Security

Secure request handling with middleware and validation.

Clean Code

Modern PHP practices with expressive syntax.

Basic Controller Structure

Let's start with a basic controller example that demonstrates the core concepts and best practices in Yabasi:

app/Controllers/UserController.php
namespace Yabasi\Controllers;

use Yabasi\Controller\Controller;
use Yabasi\Http\Request;
use Yabasi\Http\Response;
use Yabasi\Models\User;

class UserController extends Controller
{
    public function index(Request $request): Response
    {
        $users = User::all();
        return $this->view('users.index', ['users' => $users]);
    }

    public function show(Request $request, int $id): Response
    {
        $user = User::find($id);
        
        if (!$user) {
            return $this->json(['error' => 'User not found'], 404);
        }

        return $this->view('users.show', ['user' => $user]);
    }

    public function store(Request $request): Response
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8'
        ]);

        $user = User::create($validated);

        return $this->json([
            'message' => 'User created successfully',
            'user' => $user
        ], 201);
    }
}

Key Features

  • Type-hinted method parameters
  • Built-in response methods
  • Request validation
  • Model integration

Best Practices

  • Keep controllers slim and focused
  • Use type declarations
  • Validate input data
  • Return appropriate status codes

Controller Methods

Yabasi controllers follow RESTful conventions with standardized method names that map to specific HTTP operations. Here's a complete overview of common controller methods and their purposes:

GET Request Methods

index()

Lists all resources

index() method
public function index(Request $request): Response
{
    $users = User::all();
    
    return $this->view('users.index', [
        'users' => $users
    ]);
}

show($id)

Displays a specific resource

show() method
public function show(Request $request, int $id): Response
{
    $user = User::find($id);

    return $this->view('users.show', [
        'user' => $user
    ]);
}

POST Modification Methods

store()

Creates a new resource

store() method
public function store(Request $request): Response
{
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:8'
    ]);

    $user = User::create($validated);

    return $this->json([
        'message' => 'User created successfully',
        'user' => $user
    ], 201);
}

update($id)

Updates an existing resource

update() method
public function update(Request $request, int $id): Response
{
    $user = User::find($id);
    $user->update($request->validated());

    return $this->json($user);
}

Method Quick Reference

GET index()

List all resources

GET show($id)

Show single resource

POST store()

Create new resource

PUT update($id)

Update resource

DELETE destroy($id)

Delete resource

GET create()

Show create form

Controller Middleware

Protecting Your Controllers

Middleware provides a convenient way to filter and transform HTTP requests entering your application. Yabasi makes it easy to attach middleware to your controller actions.

Basic Usage

UserController.php
class UserController extends Controller
{
    public function __construct()
    {
        // Apply middleware to specific methods
        $this->middleware('auth')->only(['store', 'update', 'destroy']);
        
        // Apply middleware to all methods except specified ones
        $this->middleware('log')->except(['index', 'show']);
        
        // Apply middleware to all methods
        $this->middleware('throttle:60,1');
    }
}

only() Method

only() example
$this->middleware('auth')->only([
    'store',
    'update',
    'destroy'
]);

Applies middleware only to specified methods, leaving others unprotected.

except() Method

except() example
$this->middleware('auth')->except([
    'index',
    'show'
]);

Applies middleware to all methods except those specified.

Available Middleware

auth

Ensures the user is authenticated.

$this->middleware('auth')

csrf

Protects against CSRF attacks.

$this->middleware('csrf')

throttle

Rate limiting for API routes.

$this->middleware('throttle:60,1')

Best Practices

Keep It Simple

Apply middleware only where necessary. Too many middleware layers can impact performance.

Order Matters

Middleware executes in the order they are defined. Plan your middleware chain carefully.

Resource Controllers

RESTful Resource Controllers

Resource controllers provide a quick way to generate all the typical CRUD routes and methods for a resource. This follows REST conventions and keeps your application consistent.

Generating a Resource Controller

php yabasi make:controller UserController --resource

This command generates a controller with all the necessary resource methods pre-configured.

Generated Methods

UserController.php
namespace Yabasi\Controllers;

use Yabasi\Controller\Controller;
use Yabasi\Http\Request;
use Yabasi\Http\Response;

class UserController extends Controller
{
    /**
        * Display a listing of users
        */
    public function index(): Response
    {
        $users = User::paginate(20);
        return $this->view('users.index', ['users' => $users]);
    }

    /**
        * Show the form for creating a new user
        */
    public function create(): Response
    {
        return $this->view('users.create');
    }

    /**
        * Store a newly created user
        */
    public function store(Request $request): Response
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users'
        ]);

        $user = User::create($validated);
        
        return $this->redirect('users.show', ['id' => $user->id])
                    ->with('success', 'User created successfully');
    }

    /**
        * Display the specified user
        */
    public function show(int $id): Response
    {
        $user = User::findOrFail($id);
        return $this->view('users.show', ['user' => $user]);
    }

    /**
        * Show the form for editing the user
        */
    public function edit(int $id): Response
    {
        $user = User::findOrFail($id);
        return $this->view('users.edit', ['user' => $user]);
    }

    /**
        * Update the specified user
        */
    public function update(Request $request, int $id): Response
    {
        $user = User::findOrFail($id);
        
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email,'.$id
        ]);

        $user->update($validated);
        
        return $this->redirect('users.show', ['id' => $id])
                    ->with('success', 'User updated successfully');
    }
    /**
    * Remove the specified user
    */
    public function destroy(int $id): Response
    {
        $user = User::findOrFail($id);
        $user->delete();
        
        return $this->redirect('users.index')
                    ->with('success', 'User deleted successfully');
    }
}

Resource Routes

HTTP Method URI Action Purpose
GET /users index Display all users
GET /users/create create Show create form
POST /users store Store new user
GET /users/{id} show Display single user
GET /users/{id}/edit edit Show edit form
PUT/PATCH /users/{id} update Update user
DELETE /users/{id} destroy Delete user

Best Practices

Keep Controllers Focused

Each resource controller should handle a single type of resource. Don't mix different resources in the same controller.

Use Form Requests

For complex validation, create dedicated Form Request classes instead of validating directly in the controller.

Controller Responses

Response Types

Yabasi provides a variety of response types to handle different scenarios elegantly. From simple views to complex JSON responses, you have full control over your application's HTTP responses.

View Response

UserController.php
public function show(int $id): Response
{
    $user = User::find($id);
    
    return $this->view('users.show', [
        'user' => $user,
        'title' => 'User Profile'
    ]);
}

Returns a view with the specified template and data.

JSON Response

UserApiController.php
public function apiShow(int $id): Response
{
    $user = User::find($id);

    return $this->json([
        'data' => $user,
        'status' => 'success'
    ], 200);
}

Returns data as JSON with appropriate content type headers.

Redirect Response

ResourceController.php
public function store(Request $request): Response
{
    // ... save the resource
    return $this->redirect('/dashboard')
                ->with('success', 'Created successfully!');
}

Redirects to another URL with optional flash messages.

Download Response

DownloadController.php
public function download(string $filename): Response
{
    return $this->download(
        'path/to/file.pdf',
        'custom-name.pdf'
    );
}

Forces file download with optional custom filename.

Response Helpers

Headers

->withHeaders([ 'X-Header' => 'Value' ])

Status Codes

->setStatusCode(201)

Cookies

->withCookie('name', 'value')

Common Response Scenarios

API Success Response

ApiResponse
return $this->json([
    'status' => 'success',
    'data' => $result,
    'message' => 'Operation completed successfully'
], 200);

Error Response

ErrorResponse
return $this->json([
    'status' => 'error',
    'message' => 'Resource not found',
    'errors' => ['id' => 'Invalid ID provided']
], 404);

Redirect with Flash Data

RedirectResponse
return $this->redirect()
->route('users.index')
->with('success', 'User updated successfully')
->with('data', $userData);

Response Best Practices

Consistent Response Format

Maintain a consistent structure for your API responses across your application.

Appropriate Status Codes

Use proper HTTP status codes to indicate the outcome of requests.

Testing Controllers

Controller Testing Made Easy

Yabasi provides a robust testing framework that makes it simple to write tests for your controllers. Test HTTP requests, responses, and middleware behavior with confidence.

Basic Controller Test

tests/Controllers/UserControllerTest.php
namespace Tests\Controllers;

use Tests\TestCase;
use Yabasi\Models\User;

class UserControllerTest extends TestCase
{
    public function test_index_displays_users_list()
    {
        // Arrange
        $users = User::factory()->count(3)->create();

        // Act
        $response = $this->get('/users');

        // Assert
        $response->assertStatus(200)
                ->assertViewHas('users')
                ->assertSee($users[0]->name);
    }

    public function test_store_creates_new_user()
    {
        // Arrange
        $userData = [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'password' => 'password123'
        ];

        // Act
        $response = $this->post('/users', $userData);

        // Assert
        $response->assertStatus(302); // Redirect after creation
        $this->assertDatabaseHas('users', [
            'email' => 'john@example.com'
        ]);
    }

    public function test_update_modifies_existing_user()
    {
        // Arrange
        $user = User::factory()->create();
        $updatedData = ['name' => 'Updated Name'];

        // Act
        $response = $this->put("/users/{$user->id}", $updatedData);

        // Assert
        $response->assertRedirect();
        $this->assertDatabaseHas('users', [
            'id' => $user->id,
            'name' => 'Updated Name'
        ]);
    }
}

Testing JSON Responses

JsonResponseTest.php
public function test_api_returns_user_data()
{
    $user = User::factory()->create();

    $response = $this->getJson("/api/users/{$user->id}");

    $response
        ->assertStatus(200)
        ->assertJson([
            'data' => [
                'id' => $user->id,
                'name' => $user->name
            ]
        ]);
}

Testing Validation

ValidationTest.php
public function test_store_validates_required_fields()
{
    $response = $this->post('/users', []);

    $response
        ->assertStatus(422)
        ->assertJsonValidationErrors([
            'name',
            'email',
            'password'
        ]);
}

Testing Middleware

MiddlewareTest.php
public function test_authenticated_user_can_access_protected_route()
{
    // Create and authenticate a user
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get('/dashboard');
    $response->assertStatus(200);
}

public function test_unauthenticated_user_cannot_access_protected_route()
{
    $response = $this->get('/dashboard');
    $response->assertRedirect('/login');
}

Available Assertions

Assertion Description Example
assertStatus() Assert HTTP status code ->assertStatus(200)
assertJson() Assert JSON response structure ->assertJson(['status' => 'success'])
assertViewIs() Assert specific view was returned ->assertViewIs('users.index')
assertRedirect() Assert response is a redirect ->assertRedirect('/users')

Testing Best Practices

Test Each Route

Write tests for both successful and failed scenarios for each route.

Use Factories

Create test data using model factories for consistent test data.

Clean Tests

Follow AAA pattern: Arrange, Act, Assert for clear test structure.

Find the documentation helpful?

Show your support by starring our project on GitHub

Star on GitHub