fromhome/laravel-model-upload
The laravel-model-upload package simplifies handling Excel file uploads for Laravel Eloquent models by leveraging Laravel Excel. To begin, install the package via Composer and publish its migrations and config:
composer require atfromhome/laravel-model-upload
php artisan vendor:publish --tag="laravel-model-upload-migrations"
php artisan vendor:publish --tag="laravel-model-upload-config"
php artisan migrate
First Use Case:
Upload a CSV/Excel file containing user data and import it into a User model. Start by defining a UserUpload class extending ModelUpload (likely provided by the package) and specifying the import rules:
use Atfromhome\LaravelModelUpload\ModelUpload;
class UserUpload extends ModelUpload
{
protected $model = \App\Models\User::class;
protected $rules = [
'name' => 'required|string',
'email' => 'required|email|unique:users',
'role' => 'nullable|in:admin,user',
];
protected $map = [
'name' => 'Name',
'email' => 'Email',
'role' => 'Role',
];
protected $batchSize = 50; // Process 50 records at a time
}
Create a controller method to handle the upload:
use Atfromhome\LaravelModelUpload\Facades\ModelUpload;
public function upload(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,csv',
]);
$upload = new UserUpload();
$result = $upload->upload($request->file('file'));
return response()->json($result);
}
Route the upload endpoint:
Route::post('/upload-users', [UserUploadController::class, 'upload']);
Extend ModelUpload for each model you want to support. Key properties:
$model: The Eloquent model class.$rules: Validation rules (same as Laravel’s FormRequest).$map: Maps Excel headers to model attributes.Example for Product model:
class ProductUpload extends ModelUpload
{
protected $model = \App\Models\Product::class;
protected $rules = [
'sku' => 'required|string|unique:products',
'price' => 'required|numeric|min:0',
'stock' => 'integer|min:0',
];
protected $map = [
'sku' => 'SKU',
'price' => 'Price (USD)',
'stock' => 'In Stock',
];
}
For large files, use $batchSize to avoid memory issues. The package likely processes records in batches under the hood.
Override rules() or validateRow() to add dynamic logic:
protected function validateRow(array $row): array
{
$row['price'] = $row['price'] * 0.8; // Apply discount
return $row;
}
Use the afterImport hook to trigger actions (e.g., send notifications):
protected function afterImport(array $importedData)
{
foreach ($importedData as $record) {
Notification::send($record, new ProductImported($record));
}
}
Standardize upload handling in a base controller:
public function upload(Request $request, string $uploadClass)
{
$request->validate(['file' => 'required|file|mimes:xlsx,csv']);
$upload = app($uploadClass);
return response()->json($upload->upload($request->file('file')));
}
Route dynamically:
Route::post('/upload/{uploadClass}', [UploadController::class, 'upload']);
Mock uploads with Laravel Excel’s FakeUploadedFile:
public function test_upload()
{
Storage::fake('tmp');
$file = UploadedFile::fake()->create('test.xlsx', 100, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response = $this->post('/upload-users', ['file' => $file]);
$response->assertJson(['success' => true]);
}
Header Mismatches:
$map keys match exactly with Excel headers (case-sensitive). Use trim() if headers have inconsistent spacing.normalizeHeader method to clean headers:
protected function normalizeHeader(string $header): string
{
return strtolower(trim($header));
}
Memory Limits:
memory_limit. Reduce $batchSize or increase the limit temporarily:
ini_set('memory_limit', '512M');
Unique Constraints:
unique:users,email may fail silently if the package doesn’t throw exceptions. Wrap uploads in a try-catch:
try {
$result = $upload->upload($file);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 422);
}
Timeouts:
max_execution_time. Use queues (see Extension Points).protected function beforeImport()
{
\Log::debug('First 5 rows:', array_slice($this->rows, 0, 5));
}
Validator:
$validator = Validator::make(['email' => 'invalid'], $this->rules);
dd($validator->errors());
Excel::toArray() to inspect:
Excel::toArray([], $file)->first();
storage/app/uploads. Override in config/laravel-model-upload.php:
'storage' => 's3://your-bucket/uploads',
/tmp. Configure Laravel Excel’s temp directory:
Excel::storeUsing(function ($file) {
return Storage::disk('s3')->put('temp/' . $file->hashName(), file_get_contents($file->getRealPath()));
});
Queue Processing: Integrate with Laravel Queues to handle large files asynchronously:
$upload->upload($file)->onQueue('uploads');
Create a job:
class ProcessUploadJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function handle()
{
$upload = new UserUpload();
$upload->upload($this->file);
}
}
Custom Importers: Extend the package’s importer class to add pre/post-processing:
class CustomImporter extends \Atfromhome\LaravelModelUpload\Importers\ModelImporter
{
public function transform(array $row)
{
$row['created_at'] = now();
return parent::transform($row);
}
}
Bind it in AppServiceProvider:
ModelUpload::macro('customImporter', function () {
return new CustomImporter();
});
Webhooks: Trigger external APIs after import (e.g., sync with ERP):
protected function afterImport(array $importedData)
{
Http::post('https://erp.example.com/sync', ['data' => $importedData]);
}
Localization:
Customize error messages in $rules:
'email' => 'required|email:rfc,dns,spoof|unique:users,email,'.$user->id.',id',
Or override the package’s language file in resources/lang/en/validation.php.
$rules (e.g., unique) are indexed.DB::table('users')->insert($batch);
protected static $rulesCache = [];
public function rules()
{
if (!isset(self::$rulesCache[$this->model])) {
self::$rulesCache[$this->model] = $this->getRulesFromModel();
}
return
How can I help you explore Laravel packages today?