laravel-inputbag maintained by jaikumar0101
Laravel InputBag
A fluent builder for standardizing request inputs (search, pagination, sorting) in Laravel applications. Simplify your controller logic and maintain consistent API responses across your application.
Features
- 🔍 Standardized Input Handling - Automatically process search, pagination, and sorting parameters
- 🎯 Fluent API - Chain methods for clean and readable code
- ⚙️ Configurable Defaults - Set global defaults for pagination and sorting
- 🔧 Custom Mappings - Map custom request fields with ease
- 📦 Type Safety - Return as array or collection based on your needs
- 🚀 Zero Configuration - Works out of the box with sensible defaults
- ✨ Auto-Load Custom Fields - Define common fields once in config, use everywhere
Requirements
- PHP 8.2 or higher
- Laravel 11.x or 12.x
Installation
Install the package via Composer:
composer require jaikumar0101/laravel-inputbag
Publish Configuration (Optional)
Publish the configuration file to customize default values:
php artisan vendor:publish --tag="inputbag-config"
This will create a config/inputbag.php file where you can customize defaults such as per_page, sort_by, order_by, and define custom fields.
Quick Start
Basic Usage
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
$inputs = InputBag::make($request)->standard()->toArray();
$products = Product::when($inputs['search'], fn($q) =>
$q->where('name', 'like', '%' . $inputs['search'] . '%'))
->orderBy($inputs['sort_by'], $inputs['order_by'])
->paginate($inputs['per_page']);
return response()->json($products);
}
}
Using Facade
use Jaikumar0101\LaravelInputbag\Facades\InputBag;
class ProductController extends Controller
{
public function index()
{
// Request is automatically injected when using facade
$inputs = InputBag::standard()->toArray();
$products = Product::when($inputs['search'], fn($q) =>
$q->where('name', 'like', '%' . $inputs['search'] . '%'))
->orderBy($inputs['sort_by'], $inputs['order_by'])
->paginate($inputs['per_page']);
return response()->json($products);
}
}
Standard Inputs
The standard() method processes the following request parameters:
| Parameter | Default Value | Description |
|---|---|---|
search |
null |
Search query string |
page |
1 |
Current page number |
per_page |
15 |
Number of items per page |
sort_by |
id |
Column to sort by |
order_by |
desc |
Sort direction (asc or desc) |
Custom Fields
Method 1: Manual Mapping (Per Controller)
Map custom request fields on a per-controller basis:
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
$inputs = InputBag::make($request)
->set('status', 'filter_status', 'active')
->set('category', 'category_id')
->set('min_price', 'price_min', 0)
->set('max_price', 'price_max', 999999)
->standard()
->toArray();
// Use $inputs array in your query
}
}
Method 2: Auto-Load from Config (Application-Wide) ⭐ NEW
Define custom fields once in your config file, and they'll be automatically included in all controllers using standard():
1. Configure in config/inputbag.php:
return [
'defaults' => [
'per_page' => 15,
'sort_by' => 'id',
'order_by' => 'desc',
],
'custom_fields' => [
// Simple syntax: 'key' => 'request_parameter'
'status' => 'status',
// Advanced syntax with default value
'category' => [
'request_key' => 'category_id',
'default' => null
],
'featured' => [
'request_key' => 'featured',
'default' => false
],
'min_price' => [
'request_key' => 'price_min',
'default' => 0
],
'max_price' => [
'request_key' => 'price_max',
'default' => 999999
],
],
];
2. Use in any controller:
class ProductController extends Controller
{
public function index(Request $request)
{
// One line - all custom fields automatically loaded!
$inputs = InputBag::make($request)->standard()->toArray();
// Available automatically:
// $inputs['search'], $inputs['page'], $inputs['per_page']
// $inputs['sort_by'], $inputs['order_by']
// $inputs['status'], $inputs['category'], $inputs['featured']
// $inputs['min_price'], $inputs['max_price']
$products = Product::query()
->when($inputs['search'], fn($q) =>
$q->where('name', 'like', '%' . $inputs['search'] . '%'))
->when($inputs['category'], fn($q) =>
$q->where('category_id', $inputs['category']))
->when($inputs['status'], fn($q) =>
$q->where('status', $inputs['status']))
->when($inputs['featured'], fn($q) =>
$q->where('featured', true))
->whereBetween('price', [$inputs['min_price'], $inputs['max_price']])
->orderBy($inputs['sort_by'], $inputs['order_by'])
->paginate($inputs['per_page']);
return response()->json($products);
}
}
Benefits of Config-Based Custom Fields:
- ✅ Define once, use everywhere
- ✅ Consistent across all controllers
- ✅ Easy to maintain
- ✅ Environment-specific defaults
Combining Both Methods
You can combine config-based fields with manual fields:
// Config has: status, category
$inputs = InputBag::make($request)
->set('special_filter', 'special_param', 'default') // Add extra field
->standard() // Loads standard + config fields
->toArray();
// Result: standard + config + manual fields
Advanced Example
E-commerce Product Listing (Array Access)
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
$inputs = InputBag::make($request)
->set('brand', 'brand_id')
->set('in_stock', 'in_stock')
->standard() // Also loads fields from config
->toArray();
$products = Product::query()
->when($inputs['search'], function ($query) use ($inputs) {
$query->where('name', 'like', '%' . $inputs['search'] . '%')
->orWhere('description', 'like', '%' . $inputs['search'] . '%');
})
->when($inputs['category'], fn($q) =>
$q->where('category_id', $inputs['category']))
->when($inputs['brand'], fn($q) =>
$q->where('brand_id', $inputs['brand']))
->when($inputs['status'], fn($q) =>
$q->where('status', $inputs['status']))
->when($inputs['in_stock'], fn($q) =>
$q->where('stock', '>', 0))
->when($inputs['featured'], fn($q) =>
$q->where('featured', true))
->whereBetween('price', [$inputs['min_price'], $inputs['max_price']])
->orderBy($inputs['sort_by'], $inputs['order_by'])
->paginate($inputs['per_page']);
return response()->json($products);
}
}
E-commerce Product Listing (Collection Access)
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
$inputs = InputBag::make($request)
->set('brand', 'brand_id')
->set('in_stock', 'in_stock')
->standard()
->toCollection(); // Returns Collection!
$products = Product::query()
->when($inputs->get('search'), function ($query) use ($inputs) {
$query->where('name', 'like', '%' . $inputs->get('search') . '%')
->orWhere('description', 'like', '%' . $inputs->get('search') . '%');
})
->when($inputs->get('category'), fn($q) =>
$q->where('category_id', $inputs->get('category')))
->when($inputs->get('brand'), fn($q) =>
$q->where('brand_id', $inputs->get('brand')))
->when($inputs->get('status'), fn($q) =>
$q->where('status', $inputs->get('status')))
->when($inputs->get('in_stock'), fn($q) =>
$q->where('stock', '>', 0))
->when($inputs->get('featured'), fn($q) =>
$q->where('featured', true))
->whereBetween('price', [
$inputs->get('min_price', 0),
$inputs->get('max_price', 999999)
])
->orderBy($inputs->get('sort_by', 'id'), $inputs->get('order_by', 'desc'))
->paginate($inputs->get('per_page', 15));
return response()->json($products);
}
}
Using Collection Methods for Advanced Filtering
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
$inputs = InputBag::make($request)->standard()->toCollection();
// Get only non-null filters
$activeFilters = $inputs->filter(fn($value) => !is_null($value))
->except(['page', 'per_page', 'sort_by', 'order_by']);
// Build query dynamically
$query = Product::query();
// Apply search
if ($inputs->has('search') && $inputs->get('search')) {
$query->where('name', 'like', '%' . $inputs->get('search') . '%');
}
// Apply all active filters
foreach ($activeFilters as $key => $value) {
match($key) {
'category' => $query->where('category_id', $value),
'brand' => $query->where('brand_id', $value),
'status' => $query->where('status', $value),
'featured' => $query->where('featured', true),
'in_stock' => $query->where('stock', '>', 0),
default => null
};
}
// Price range
if ($inputs->has('min_price') || $inputs->has('max_price')) {
$query->whereBetween('price', [
$inputs->get('min_price', 0),
$inputs->get('max_price', 999999)
]);
}
$products = $query->orderBy($inputs->get('sort_by', 'id'), $inputs->get('order_by', 'desc'))
->paginate($inputs->get('per_page', 15));
return response()->json([
'data' => $products,
'filters' => $activeFilters, // Return active filters to frontend
]);
}
}
Output Formats
As Array
$inputs = InputBag::make($request)->standard()->toArray();
// Returns: array
// Access values
$search = $inputs['search'];
$perPage = $inputs['per_page'];
As Collection
$inputs = InputBag::make($request)->standard()->toCollection();
// Returns: Illuminate\Support\Collection
// Access values using Collection methods
$search = $inputs->get('search');
$perPage = $inputs->get('per_page', 15); // with default
$category = $inputs->get('category');
// Use Collection helper methods
$hasSearch = $inputs->has('search');
$onlyFilters = $inputs->only(['category', 'status', 'featured']);
$exceptPagination = $inputs->except(['page', 'per_page']);
// Chain Collection methods
$filters = $inputs->filter(fn($value) => !is_null($value));
When to Use Each Format
Use Array when:
- You need simple key-value access
- Working with traditional arrays
- Passing to functions expecting arrays
Use Collection when:
- You want Laravel Collection methods
- Need to transform/filter inputs
- Want null-safe access with defaults
- Chaining operations
Configuration
After publishing the configuration file, you can customize:
// config/inputbag.php
return [
// Default values for standard fields
'defaults' => [
'per_page' => 15, // Default items per page
'sort_by' => 'id', // Default sort column
'order_by' => 'desc', // Default sort direction
],
// Custom fields auto-loaded with standard()
'custom_fields' => [
// Simple syntax
'status' => 'status',
// Advanced syntax
'category' => [
'request_key' => 'category_id',
'default' => null
],
],
];
API Reference
Methods
make(Request $request)
Creates a new InputBag instance with the given request.
InputBag::make($request)
Parameters:
$request(Request): The HTTP request instance
standard()
Processes standard request inputs (search, page, per_page, sort_by, order_by) plus any custom fields defined in config.
InputBag::make($request)->standard()
set($key, $requestKey, $default = null)
Maps a custom request field to the input bag.
InputBag::make($request)->set('status', 'filter_status', 'active')
Parameters:
$key(string): The key to store the value under in the result$requestKey(string): The request parameter name to look for$default(mixed, optional): Default value if request parameter is not present
toArray()
Returns the input bag as an array.
InputBag::make($request)->standard()->toArray()
toCollection()
Returns the input bag as a Laravel Collection.
InputBag::make($request)->standard()->toCollection()
reset()
Resets the input bag data.
InputBag::make($request)->reset()
Usage Patterns
Pattern 1: Static make() Method (Recommended)
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
$inputs = InputBag::make($request)->standard()->toArray();
Pattern 2: Facade (Auto-inject Request)
use Jaikumar0101\LaravelInputbag\Facades\InputBag;
$inputs = InputBag::standard()->toArray();
Pattern 3: Direct Instantiation
use Jaikumar0101\LaravelInputbag\InputBag;
use Illuminate\Http\Request;
$bag = new InputBag($request);
$inputs = $bag->standard()->toArray();
Example API Requests
Basic Search and Pagination
GET /api/products?search=laptop&page=2&per_page=20
Sorting
GET /api/products?sort_by=price&order_by=asc
With Custom Filters
GET /api/products?search=phone&category=1&status=active&featured=1&price_min=100&price_max=1000
Complete Example
GET /api/products?search=laptop&category=electronics&status=active&featured=1&price_min=500&price_max=2000&sort_by=price&order_by=asc&per_page=50&page=2
Real-World Use Cases
Multi-Tenant Application
// config/inputbag.php
'custom_fields' => [
'tenant_id' => [
'request_key' => 'tenant',
'default' => null
],
'workspace_id' => [
'request_key' => 'workspace',
'default' => null
],
],
// All controllers automatically filter by tenant/workspace
$inputs = InputBag::make($request)->standard()->toArray();
Blog/CMS
// config/inputbag.php
'custom_fields' => [
'status' => [
'request_key' => 'status',
'default' => 'published'
],
'author' => 'author_id',
'tag' => 'tag_id',
'category' => 'category_id',
],
E-commerce Platform
// config/inputbag.php
'custom_fields' => [
'category' => 'category_id',
'brand' => 'brand_id',
'in_stock' => [
'request_key' => 'in_stock',
'default' => null
],
'on_sale' => 'on_sale',
'min_price' => [
'request_key' => 'price_min',
'default' => 0
],
'max_price' => [
'request_key' => 'price_max',
'default' => 999999
],
],
Testing
Run the test suite:
composer test
Generate code coverage:
composer test-coverage
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Security Vulnerabilities
If you discover a security vulnerability within this package, please send an email to Jai Kumar at jaikumar43044@gmail.com. All security vulnerabilities will be promptly addressed.
Credits
License
The MIT License (MIT). Please see License File for more information.
Made with ❤️ by Jai Kumar