PHP code example of anil / fast-api-crud

1. Go to this page and download the library: Download anil/fast-api-crud library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

anil / fast-api-crud example snippets


// app/Http/Controllers/PostController.php

class PostController extends BaseController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}

// routes/api.php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::fastApiResource('posts', PostController::class);

// Partial registration, mirroring Laravel's apiResource:
Route::fastApiResource('posts', PostController::class, ['only' => ['index', 'show']]);
Route::fastApiResource('posts', PostController::class, ['except' => ['delete', 'restoreAll']]);

Route::get('posts', [PostController::class, 'index']);
Route::post('posts', [PostController::class, 'store']);
Route::get('posts/{id}', [PostController::class, 'show']);
Route::put('posts/{id}', [PostController::class, 'update']);
Route::delete('posts/{id}', [PostController::class, 'destroy']);

return [
    // Pagination
    'pagination' => [
        'default_per_page' => 15,    // Default records per page
        'max_per_page'     => 100,   // Maximum allowed per page
        'allow_all'        => true,  // Allow rowsPerPage=0 to fetch all
    ],

    // Bulk delete
    'bulk' => [
        'max_rows' => 1000,          // Max IDs per bulk delete (0 = unlimited)
        'field'    => 'delete_rows', // Request key holding the array of IDs
    ],

    // Query parameter keys — rename any of these to match your API conventions.
    // Defaults reproduce: ?filters={...}&sortBy=...&descending=...&rowsPerPage=...&page=...&search=...
    'query' => [
        'filters'    => 'filters',
        'search'     => 'search',
        'sort_by'    => 'sortBy',
        'descending' => 'descending',
        'per_page'   => 'rowsPerPage',
        'page'       => 'page',
        'cursor'     => 'cursor',
        '

'query' => [
    'per_page'   => 'limit',
    'sort_by'    => 'sort',
    'descending' => 'dir',
    'search'     => 'q',
],

use Anil\FastApiCrud\Http\Controllers\BaseController;

class PostController extends BaseController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}

use Anil\FastApiCrud\Http\Controllers\BaseWebController;

class PostController extends BaseWebController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            viewPrefix: 'admin.posts',     // views: admin.posts.index, admin.posts.create, etc.
            routePrefix: 'admin.posts',    // redirects: route('admin.posts.index')
            resourceName: 'post',          // $post variable in views
            collectionName: 'posts',       // $posts variable in index view
            resource: PostResource::class, // Optional — for data transformation before views
        );
    }
}

class PostController extends BaseWebController
{
    protected function storeSuccessMessage(): string       { return __('Post created!'); }
    protected function updateSuccessMessage(): string      { return __('Post updated!'); }
    protected function destroySuccessMessage(): string     { return __('Post deleted.'); }
    protected function bulkDeleteSuccessMessage(): string  { return __('Posts deleted.'); }
    protected function statusChangeSuccessMessage(): string    { return __('Status updated.'); }
    protected function columnUpdateSuccessMessage(): string    { return __('Column updated.'); }
    protected function restoreSuccessMessage(): string         { return __('Post restored.'); }
    protected function restoreAllSuccessMessage(): string      { return __('All posts restored.'); }
    protected function permanentDeleteSuccessMessage(): string { return __('Post permanently deleted.'); }
}

class PostController extends BaseWebController
{
    public function create(): \Illuminate\View\View
    {
        return view($this->viewName('create'), [
            'categories' => Category::all(),
            'tags' => Tag::all(),
        ]);
    }

    public function edit(int|string $id): \Illuminate\View\View
    {
        $query = $this->buildShowQuery();

        return view($this->viewName('edit'), [
            $this->resourceName => $query->findOrFail($id),
            'categories' => Category::all(),
        ]);
    }
}

// routes/web.php

use App\Http\Controllers\PostController;

Route::resource('admin/posts', PostController::class);

// Additional routes for extended operations
Route::patch('admin/posts/{id}/status', [PostController::class, 'changeStatus']);
Route::patch('admin/posts/{id}/restore', [PostController::class, 'restore']);
Route::post('admin/posts/restore', [PostController::class, 'restoreAll']);
Route::delete('admin/posts/{id}/force', [PostController::class, 'permanentDelete']);

class PostController extends BaseController
{
    // Pagination
    protected PaginationType $paginationType = PaginationType::LengthAware;
    // Options: LengthAware, Simple, Cursor, None

    // Index query customization
    protected array $scopes = ['active'];                    // Apply scopes to index
    protected array $with = ['user', 'tags'];                // Eager load in index
    protected array $withCount = ['comments'];               // Count relations in index
    protected array $withAggregate = ['ratings' => 'score']; // Aggregates in index

    // Show query customization
    protected array $loadScopes = [];                        // Scopes for show
    protected array $load = ['user', 'tags', 'comments'];   // Eager load in show
    protected array $loadCount = ['comments'];               // Count in show
    protected array $loadAggregate = [];                     // Aggregates in show

    // Client-driven query features (driven via the filters JSON)
    protected array $allowedIncludes = ['user', 'tags'];     // filters={"

// Simple scopes (no parameters)
protected array $scopes = ['active', 'published'];

// Parameterized scopes
protected array $scopes = [
    'status' => 1,
    'active' => 1,
    'type'   => 'article',
];

// Array parameters
protected array $scopes = [
    'statusIn' => [1, 2, 3],
];

// Closure parameters
protected array $scopes = [
    'custom' => function ($query) {
        $query->where('featured', true);
    },
];

// Mixed
protected array $scopes = [
    'active',
    'status' => 1,
];

class Post extends Model
{
    // Called before/after store()
    public function beforeCreate(): void
    {
        $this->slug = Str::slug($this->name);
    }

    public function afterCreate(): void
    {
        // Sync relations from request
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }

    // Called before/after update()
    public function beforeUpdate(): void { }
    public function afterUpdate(): void { }

    // Called before/after destroy() and delete()
    public function beforeDelete(): void { }
    public function afterDelete(): void { }

    // Called before/after changeStatus()
    public function beforeStatusChange(): void { }
    public function afterStatusChange(): void { }

    // Called before/after updateColumn()
    public function beforeColumnUpdate(): void { }
    public function afterColumnUpdate(): void { }

    // Called before/after restore()
    public function beforeRestore(): void { }
    public function afterRestore(): void { }

    // Called before/after permanentDelete()
    public function beforeForceDelete(): void { }
    public function afterForceDelete(): void { }
}

class PostController extends BaseController
{
    protected function afterCreate(Model $model): void
    {
        // Controller-level hook overrides model hook
        Notification::send($admins, new PostCreated($model));
    }
}

use Anil\FastApiCrud\Contracts\Searchable;

class Post extends Model implements Searchable
{
    public function searchableColumns(): array
    {
        return [
            'name',
            'desc',
            'user:name,email',  // Search in related model columns
        ];
    }
}

use Anil\FastApiCrud\Contracts\Sortable;

class Post extends Model implements Sortable
{
    public function sortByDefaults(): array
    {
        return [
            'sortBy'     => 'created_at',
            'sortByDesc' => true,
        ];
    }
}

use Anil\FastApiCrud\Contracts\HasPermissionSlug;

class Post extends Model implements HasPermissionSlug
{
    public function getPermissionSlug(): string
    {
        return 'posts';
    }
}

use Anil\FastApiCrud\Concerns\HasDateScopes;

class Post extends Model
{
    use HasDateScopes;
}

Post::query()->today();                // Records from today
Post::query()->yesterday();            // Records from yesterday
Post::query()->thisWeek();             // Monday to now
Post::query()->lastWeek();             // Last Monday to Sunday
Post::query()->monthToDate();          // 1st of month to now
Post::query()->thisMonth();            // Entire current month
Post::query()->lastMonth();            // Entire previous month
Post::query()->quarterToDate();        // Start of quarter to now
Post::query()->lastQuarter();          // Previous quarter
Post::query()->yearToDate();           // January 1 to now
Post::query()->lastYear();             // Last 12 months
Post::query()->last7Days();            // Last 7 days
Post::query()->last30Days();           // Last 30 days
Post::query()->date('2025-01-01 to 2025-01-31');  // Custom range

// Use a different column
Post::query()->today('published_at');
Post::query()->lastMonth('updated_at');

use Illuminate\Database\Eloquent\Concerns\HasUuids;          // UUID v7 (time-ordered, recommended)
// use Illuminate\Database\Eloquent\Concerns\HasVersion4Uuids; // ordered UUID

class Post extends Model
{
    use HasUuids;
}

use Anil\FastApiCrud\Concerns\AnonymizesOnDelete;

class User extends Model
{
    use SoftDeletes, AnonymizesOnDelete;
}

use Anil\FastApiCrud\Concerns\ReplicatesWithRelations;

class Post extends Model
{
    use ReplicatesWithRelations;
}

// Usage
$post = Post::with(['tags', 'comments', 'author'])->find(1);
$clone = $post->replicateWithRelations();
// $clone is a saved copy with all relations duplicated

->initializer(bool $orderBy = true): Builder

$query = Post::query()->initializer();

// With orderBy disabled
$query = Post::query()->initializer(orderBy: false);

->likeWhere(array $attributes, ?string $searchTerm = null): Builder

// Simple columns
Post::query()->likeWhere(['name', 'desc'], 'laravel');

// With relation columns (colon syntax: 'relation:column1,column2')
Post::query()->likeWhere(['name', 'user:name,email', 'tags:name'], 'search');

// Null search returns query unchanged
Post::query()->likeWhere(['name'], null);  // No-op

->paginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): Paginator

Post::query()->paginates();                              // Default columns
Post::query()->paginates(['id', 'name']);                 // Specific columns
Post::query()->paginates(['*'], 'p', 2);                 // Custom page name & page

->simplePaginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): Paginator

->cursorPaginates(array $columns = ['*'], ?string $cursorName = null, ?Cursor $cursor = null): CursorPaginator

->withAggregates(array $aggregates): Builder

Post::query()->withAggregates([
    'comments' => 'id',                    // withAggregate('comments', 'id')
    'ratings'  => ['score', 'avg'],        // withAggregate('ratings', 'score', 'avg')
]);

->withCountWhereHas(string $relation, ?Closure $callback = null, string $operator = '>=', int $count = 1): Builder
->orWithCountWhereHas(string $relation, ?Closure $callback = null, string $operator = '>=', int $count = 1): Builder

// Basic — posts that have at least 1 comment, with comment count
Post::query()->withCountWhereHas('comments');

// With callback — posts with approved comments
Post::query()->withCountWhereHas('comments', function ($q) {
    $q->where('approved', true);
});

// With operator/count — posts with 5+ comments
Post::query()->withCountWhereHas('comments', null, '>=', 5);

// OR variant — posts with comments OR tags
Post::query()
    ->withCountWhereHas('comments')
    ->orWithCountWhereHas('tags');

->paginate(int $perPage, ?int $total = null, ?int $page = null, string $pageName = 'page'): LengthAwarePaginator

$items = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

$paginated = $items->paginate(perPage: 5);
// Page 1: [1, 2, 3, 4, 5] — auto-resolves current page from request

$paginated = $items->paginate(perPage: 3, page: 2);
// Page 2: [4, 5, 6]

$paginated = $items->paginate(perPage: 5, total: 100);
// Override total count (useful for pre-sliced data)

use Anil\FastApiCrud\Concerns\HasApiResponse;

class MyController extends Controller
{
    use HasApiResponse;
}

// success(array $data = [], int $code = 200): JsonResponse
return $this->success(['key' => 'value']);          // {"data": {"key": "value"}}
return $this->success(['key' => 'value'], 201);    // Same with 201 status

// error(string $message = 'Something went wrong', array $data = [], int $status = 400): JsonResponse
return $this->error('Something went wrong');        // {"errors": [], "message": "Something went wrong"}
return $this->error('Not found', [], 404);          // Same with 404 status

diffForHumans(?string $date): ?string
// diffForHumans('2025-01-15')           → "2 months ago"
// diffForHumans(null)                   → null

ymdDate(?string $date, string $format = 'Y-m-d'): ?string
// ymdDate('2025-01-15 14:30:00')        → "2025-01-15"
// ymdDate('2025-01-15', 'd/m/Y')        → "15/01/2025"

dateForReports(?string $date, string $format = 'Y-m-d H:i'): ?string
// dateForReports('2025-01-15 14:30:00') → "2025-01-15 14:30"
// Returns null on invalid date (doesn't throw)

toFormattedDateString(?string $date): ?string
// toFormattedDateString('2025-01-15')   → "January 15, 2025"

toDateString(?string $date): ?string
// toDateString('2025-01-15 14:30:00')   → "2025-01-15"

toDateTimeString(?string $date): ?string
// toDateTimeString('2025-01-15 14:30')  → "2025-01-15 14:30:00"

toTimeString(?string $date): ?string
// toTimeString('2025-01-15 14:30:00')   → "14:30:00"

parseTimeToSeconds(string $timeString): int|float
// parseTimeToSeconds('01:30:00')        → 5400    (H:i:s format)
// parseTimeToSeconds('30:45')           → 1845    (i:s format)
// parseTimeToSeconds('120')             → 120     (plain seconds)

formatDuration(int|float|null $duration, ?string $format = '%y %mo %d %h %m %s', string $separator = ' '): string
// formatDuration(3661)                  → "1h 1m 1s"
// formatDuration(90061)                 → "1d 1h 1m 1s"
// formatDuration(3661, '%h %m')         → "1h 1m"
// formatDuration(3661, '%h %m', '-')    → "1h-1m"
// formatDuration(null)                  → "0s"
// formatDuration(0)                     → "0s"
// formatDuration(-3661)                 → "1h 1m 1s"  (absolute value)
// Placeholders: %y=years, %mo=months, %d=days, %h=hours, %m=minutes, %s=seconds

filterValue(string $key = 'date'): ?string
// Reads from ?filters={"date":"2025-01-15"} query parameter
// filterValue('date')                   → "2025-01-15"
// filterValue('missing')                → null

arrayFilters(array|string|null $data): array
// arrayFilters('{"active":1,"name":""}')→ ['active' => 1]  (removes falsy)
// arrayFilters(['a' => 1, 'b' => null]) → ['a' => 1]
// arrayFilters(null)                    → []

flattenArray(array $data, int $depth = 0): array
// flattenArray(['a' => [1, 2], 'b' => [3]])  → [1, 2, 3]
// flattenArray(['a' => [1, [2]]], 1)          → [1, [2]]

sortDirection(): string
// Reads ?descending query parameter
// ?descending=true  → "ASC"
// ?descending=false → "DESC"

sortBy(): array|string|null
// Reads ?sort query parameter
// ?sort=name        → "name"
// ?sort[]=name&sort[]=id → ["name", "id"]
// (not set)         → null

uuid(): UuidInterface
// uuid()                                → Ramsey\Uuid\UuidInterface (v4)

slug(?string $text = null): ?string
// slug('Hello World')                   → "hello-world"
// slug(null)                            → null

relativePath(string $path): string
// relativePath('/var/www/app/Models/Post.php') → "app/Models/Post.php"
// (relative to base_path())

classShortName(string $param): ?string
// classShortName(App\Models\Post::class) → "Post"
// Uses reflection, throws ReflectionException if unresolvable

scopeMethods(object $class): array
// scopeMethods(new Post)                → ["scopeActive", "scopePublished", ...]
// Returns all public methods starting with "scope"

tableColumns(string|Model $table = 'users'): array
// tableColumns('posts')                 → ["id", "active", "desc", "name", ..., "created_at", "updated_at", "deleted_at"]
// tableColumns(Post::class)             → Same (accepts class-string)
// tableColumns(new Post)                → Same (accepts model instance)
// Columns sorted alphabetically, with id first and timestamps last

fillableCsv(string $model): string
// fillableCsv(Post::class)              → "name,desc,status,active"
// fillableCsv('posts')                  → All columns from table (if not a model class)

columnsCsv(string $model, string $separator = ','): string
// columnsCsv(Post::class)               → "id,active,desc,name,status,created_at,updated_at"
// columnsCsv(Post::class, '|')          → "id|active|desc|name|status|created_at|updated_at"

appClasses(string $path = 'App', array $excluding = []): array
// appClasses('Models')                   → ["App\Models\Post", "App\Models\User", ...]
// appClasses('Models', [App\Models\User::class])  → ["App\Models\Post", ...]

databaseClasses(?string $directory = null, array $excluding = []): array
// databaseClasses('seeders')             → ["Database\Seeders\PostSeeder", ...]
// databaseClasses()                      → All classes in database/ directory
// databaseClasses('factories', ['Database\Factories\UserFactory']) → Filtered list

use Anil\FastApiCrud\Exceptions\ApiException;

throw new ApiException('Resource not found', 404);

class PostController extends BaseController
{
    public static function middleware(): array
    {
        return static::permissionMiddleware('posts');
    }

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}

use App\Http\Controllers\PostController;

Route::fastApiResource('posts', PostController::class);

// Restrict actions, or customise the {id} parameter / route-name prefix:
Route::fastApiResource('posts', PostController::class, ['only' => ['index', 'show']]);
Route::fastApiResource('posts', PostController::class, ['except' => ['delete', 'restoreAll']]);

use App\Http\Controllers\PostController;

Route::get('posts', [PostController::class, 'index'])->name('posts.index');
Route::post('posts', [PostController::class, 'store'])->name('posts.store');
Route::delete('posts', [PostController::class, 'delete'])->name('posts.delete');
Route::post('posts/restore', [PostController::class, 'restoreAll'])->name('posts.restoreAll');
Route::patch('posts/{id}/status/{column}', [PostController::class, 'updateColumn'])->name('posts.updateColumn');
Route::patch('posts/{id}/status', [PostController::class, 'changeStatus'])->name('posts.changeStatus');
Route::patch('posts/{id}/restore', [PostController::class, 'restore'])->name('posts.restore');
Route::delete('posts/{id}/force', [PostController::class, 'permanentDelete'])->name('posts.permanentDelete');
Route::get('posts/{id}', [PostController::class, 'show'])->name('posts.show');
Route::match(['put', 'patch'], 'posts/{id}', [PostController::class, 'update'])->name('posts.update');
Route::delete('posts/{id}', [PostController::class, 'destroy'])->name('posts.destroy');

use Anil\FastApiCrud\Support\Pagination;

Pagination::defaultPerPage();       // 15 (from config)
Pagination::maxPerPage();           // 100 (from config)
Pagination::requestedPerPage(15);   // Value of ?rowsPerPage or default
Pagination::resolvePerPage();       // Effective per-page (respects max, allow_all)

// Generic config helpers
Pagination::configInt('fast-api.pagination.default_per_page', 15);   // int
Pagination::configBool('fast-api.pagination.allow_all', true);       // bool

use Anil\FastApiCrud\Enums\PaginationType;

PaginationType::LengthAware  // 'length-aware' — Standard pagination with total count
PaginationType::Simple        // 'simple'       — Simple pagination without total
PaginationType::Cursor        // 'cursor'       — Cursor-based pagination
PaginationType::None          // 'none'         — No pagination, returns all records

use Anil\FastApiCrud\Enums\CrudAction;

CrudAction::View          // 'view'
CrudAction::Store         // 'store'
CrudAction::Update        // 'update'
CrudAction::Delete        // 'delete'
CrudAction::ChangeStatus  // 'change-status'
CrudAction::Restore       // 'restore'

namespace App\Models;

use Anil\FastApiCrud\Concerns\HasDateScopes;
use Anil\FastApiCrud\Concerns\AnonymizesOnDelete;
use Anil\FastApiCrud\Contracts\HasPermissionSlug;
use Anil\FastApiCrud\Contracts\Searchable;
use Anil\FastApiCrud\Contracts\Sortable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model implements Searchable, Sortable, HasPermissionSlug
{
    use SoftDeletes, HasDateScopes, AnonymizesOnDelete;

    protected $fillable = ['name', 'desc', 'status', 'active', 'user_id'];

    // --- Contracts ---

    public function searchableColumns(): array
    {
        return ['name', 'desc', 'user:name,email'];
    }

    public function sortByDefaults(): array
    {
        return ['sortBy' => 'created_at', 'sortByDesc' => true];
    }

    public function getPermissionSlug(): string
    {
        return 'posts';
    }

    // --- Relations ---

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }

    // --- Scopes (callable via ?filters={"active":1}) ---

    public function scopeActive($query, int $active = 1)
    {
        return $query->where('active', $active);
    }

    // --- Lifecycle Hooks ---

    public function afterCreate(): void
    {
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }

    public function afterUpdate(): void
    {
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }
}

namespace App\Http\Controllers\Api;

use Anil\FastApiCrud\Enums\PaginationType;
use Anil\FastApiCrud\Http\Controllers\BaseController;
use App\Http\Requests\Post\StorePostRequest;
use App\Http\Requests\Post\UpdatePostRequest;
use App\Http\Resources\Post\PostResource;
use App\Models\Post;

class PostController extends BaseController
{
    protected PaginationType $paginationType = PaginationType::LengthAware;

    protected array $with = ['user', 'tags'];
    protected array $withCount = ['tags'];
    protected array $load = ['user', 'tags', 'tags.posts'];
    protected array $scopes = ['active'];

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}

namespace App\Http\Controllers\Web;

use Anil\FastApiCrud\Http\Controllers\BaseWebController;
use App\Http\Requests\Post\StorePostRequest;
use App\Http\Requests\Post\UpdatePostRequest;
use App\Models\Post;

class PostController extends BaseWebController
{
    protected array $with = ['user', 'tags'];
    protected array $load = ['user', 'tags'];

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            viewPrefix: 'posts',
            routePrefix: 'posts',
            resourceName: 'post',
            collectionName: 'posts',
        );
    }

    protected function storeSuccessMessage(): string
    {
        return __('Post created successfully!');
    }
}
bash
php artisan vendor:publish --provider="Anil\FastApiCrud\FastApiCrudServiceProvider" --tag=config
json
{
  "error": {
    "message": "Resource not found",
    "file": "/app/Http/Controllers/PostController.php",
    "line": 42
  }
}