hasanhawary / export-builder
A modular Laravel export builder around maatwebsite/excel (CSV/XLS/XLSX)
Installs: 71
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/hasanhawary/export-builder
Requires
- php: >=8.0
- illuminate/support: ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0
- maatwebsite/excel: ^3.1
README
Lightweight, framework-friendly export generation for Laravel powered by maatwebsite/excel. Define tiny export classes and trigger CSV/XLS/XLSX downloads with built-in filtering, relations, and smart value formatting.
โจ Why Export Builder?
- Zero boilerplate: focus on a simple config array, not Excel internals.
- Convention over configuration: resolves exports by page name and namespace.
- Powerful mapping: convert types (date, datetime, int, money, booleans), translate headings, and resolve enums.
- Relations aware: eager-load one/many relations, flatten nested data, count/list/concat children.
- Production-ready: safe file names, error logging, and HTTP responses that Just Work.
๐ฆ Installation
composer require hasanhawary/export-builder
The package auto-discovers its service provider. Optionally publish the default configuration to config/export.php:
php artisan vendor:publish --tag=export-builder-config
Configuration option available in config/export.php:
- namespace: Default is
HasanHawary\\ExportBuilder\\Types. Change it e.g. toApp\\Exportsto keep exports inside your app.
โก Quick Start
- Create an export class under your namespace. The class name must be
{Page}Export(StudlyCase of thepagefilter). Example: a User report export:
namespace App\Exports; use HasanHawary\ExportBuilder\BaseExport; use App\Models\User; use App\Enum\User\UserGenderEnum; class UserExport extends BaseExport { public function __construct(array $filter) { $config = [ 'model' => User::class, 'columns' => [ 'id' => 'int', 'name' => 'text', 'email' => 'text', 'phone' => 'text', 'gender' => UserGenderEnum::class, // enum class with static resolve($value) 'is_active' => 'boolean', 'last_login' => 'datetime', 'created_at' => 'datetime', ], 'relations' => [ 'one' => [ // foreign_key => [ relationName => [ column => type ]] 'created_by' => ['creator' => ['name' => 'text', 'id' => 'int']], ], 'many' => [ 'count' => [], 'list' => [], 'concat' => ['roles' => ['display_name' => 'text']] ], ], ]; parent::__construct($config, $filter); } }
- Trigger a download from a controller/route using the Facade (recommended):
use HasanHawary\ExportBuilder\Facades\Export; public function downloadUsers() { $filter = [ 'page' => 'user', // resolves to App\\Exports\\UserExport if config('export.namespace') = 'App\\\\Exports' 'format' => 'xlsx', // csv | xlsx | xls (default: xlsx) // Optional // 'filename' => 'users_report', // 'timestamp' => '2025-09-21_120000', ]; return (new ExportBuilder($filter))->response(); // BinaryFileResponse download }
๐ง Configuration
Publish the config and point the namespace to your preferred location:
// config/export.php return [ 'namespace' => 'App\\Exports', ];
Now an incoming filter like ['page' => 'order'] will resolve to App\Exports\OrderExport.
๐ง Columns, Types, and Formatting
Declare the shape of your dataset via the columns map. BaseExport automatically converts values using the following types:
- text: raw text
- date: Y-m-d
- datetime: Y-m-d H:i:s
- array: array => "a , b , c"
- int, float, money: numeric formatting (money uses 2 decimals)
- bool/boolean: localized to api.yes/api.no
- classPath: turns a class path into its tail name and tries to translate it
- Enum classes: If a class is provided, and it has a static resolve($value) method, it will be used to map values
Headings are generated from your column keys and passed through the api.* translation domain when available.
You can also pick specific columns at runtime by passing filter['columns'] = ['id','name','creator.name'].
๐ค Relations (one & many) and Nested Data
- one: eager-loads a single related model and flattens its fields using the pattern relation_field.
- many: supports three useful shapes for collections:
- count: include the total count of items
- list: return an array of values (useful for JSON exports)
- concat: a comma-separated string of values
You may also target nested attributes using dot notation directly in columns, e.g. 'creator.department.name' => 'text'. These are resolved automatically.
๐ Filtering & Query Options
You can control the dataset via the filter array you pass to your export or endpoint:
- apply_date: boolean; when true, start/end will filter the date_column (default created_at)
- start, end: date boundaries (YYYY-MM-DD)
- search: full-text like filter across top-level column names
- conditions: array of [key, operation, value] triplets passed to where()
- order_by, order_dir: sorting (asc|desc)
- limit: cap the number of records (handy for previews/testing)
- type: optional segmenting helper used by BaseExport to include keys matching this string
- columns: explicit column whitelist as an array
You can change the date column by setting 'date_column' in your export config.
๐ Permissions
Override isEnabled() in your export class to guard access with policies/permissions:
public function isEnabled(): bool { return auth()->user()?->can('export-users'); }
If it returns false, the request responds with 403.
๐งพ File Names & Formats
- format: csv, xlsx, or xls (default xlsx)
- filename: base name; defaults to page
- timestamp: defaults to current Ymd_His The final file name is slugified as {filename}_{timestamp}.{ext}.
๐ Troubleshooting
- 422 Missing export page: provide filter['page'].
- 404 Export class not found: ensure the class name matches {Page}Export and the namespace is configured.
- 403 Forbidden: isEnabled() returned false.
- Corrupted/blank Excel: the package cleans output buffers before streaming to avoid Excel corruption issues.
โ Version Support
- PHP: 8.0 โ 8.5
- Laravel: 8 โ 12
๐ Examples Cheat-Sheet
- Only last 7 days and search by email:
['page' => 'user', 'apply_date' => true, 'start' => now()->subDays(7)->toDateString(), 'search' => '@example.com']
- Sort and limit:
['page' => 'user', 'order_by' => 'created_at', 'order_dir' => 'desc', 'limit' => 500]
- Add conditions (status = active):
['page' => 'user', 'conditions' => [ ['key' => 'status', 'operation' => '=', 'value' => 'active'] ]]
โ Version Support
- PHP: 8.0 โ 8.5
- Laravel: 8 โ 12
๐ License
MIT ยฉ Hasan Hawary