Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Simplesheet Laravel Package

nikazooz/laravel-simplesheet

Laravel SimpleSheet provides a simple, expressive API for importing and exporting spreadsheet files in Laravel. Read rows into arrays, write data to Excel or CSV files, and integrate file uploads or storage with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

nikazooz/laravel-simplesheet is a thin, Spout-backed wrapper that gives you Laravel-Excel-style Exports/Imports classes without PhpSpreadsheet's memory and styling baggage. Treat it as a streaming XLSX/CSV/ODS pipe, not a spreadsheet authoring tool.

  1. Check compatibility before anything else. The package was last released in 2021 and is archived, so its composer.json constraints target Laravel 8/9-era and PHP 7.4/8.0. On Laravel 10/11 you will likely need composer require nikazooz/laravel-simplesheet --ignore-platform-reqs or, more sanely, fork it and bump illuminate/* + box/spout/openspout yourself. Also confirm ext-zip, ext-xml, ext-libxml, and ext-mbstring are enabled — Spout will fail without them.

  2. Install and let auto-discovery register the provider, then optionally publish config if you need to tune defaults:

    composer require nikazooz/laravel-simplesheet
    php artisan vendor:publish --provider="Nikazooz\Simplesheet\SimplesheetServiceProvider"
    
  3. Look first at the generated stubs, not the README. The artisan generators are the de-facto documentation for the supported concern set:

    php artisan make:export UsersExport --model=User
    php artisan make:import UsersImport --model=User
    
  4. First use case: stream a collection to a download. An export class implements a "source" concern, the controller hands it to the facade:

    // app/Exports/UsersExport.php
    namespace App\Exports;
    
    use App\Models\User;
    use Nikazooz\Simplesheet\Concerns\FromQuery;
    use Nikazooz\Simplesheet\Concerns\WithHeadings;
    
    class UsersExport implements FromQuery, WithHeadings
    {
        public function query() { return User::query()->where('active', true); }
    
        public function headings(): array
        {
            return ['ID', 'Name', 'Email', 'Created'];
        }
    }
    
    // controller
    use Nikazooz\Simplesheet\Facades\Simplesheet;
    
    return Simplesheet::download(new UsersExport, 'users.xlsx');
    

That's the whole mental model: a class describes where rows come from and how they look, the facade decides where they go (download, disk, or an import target).

Implementation Patterns

Pick the cheapest source concern. FromQuery is the one you want day-to-day — Spout streams while the query cursor advances, so memory stays flat regardless of table size. Use FromCollection only for small in-memory sets, FromArray for fixtures and tests, and avoid ->get() in collection() unless you've measured it.

Shape output with mapping, not SQL aliases. WithMapping keeps presentation logic in the export class and lets you format dates/booleans in PHP:

use Nikazooz\Simplesheet\Concerns\WithMapping;

class UsersExport implements FromQuery, WithHeadings, WithMapping
{
    public function map($user): array
    {
        return [
            $user->id,
            $user->name,
            $user->email,
            $user->created_at->toDateTimeString(),
        ];
    }
}

Persist to disk instead of streaming when the file is large or emailed/queued:

Simplesheet::store(new UsersExport, 'reports/users.xlsx', 's3');

The filename extension selects the writer (.xlsx, .csv, .ods), so you don't configure a format anywhere — a genuinely nice ergonomic.

Imports: always pair ToModel/ToCollection with WithChunkReading. This is the pattern that makes the package worth using — a 500k-row XLSX imports in constant memory:

use Nikazooz\Simplesheet\Concerns\ToModel;
use Nikazooz\Simplesheet\Concerns\WithHeadingRow;
use Nikazooz\Simplesheet\Concerns\WithChunkReading;
use Nikazooz\Simplesheet\Concerns\WithBatchInserts;

class UsersImport implements ToModel, WithHeadingRow, WithChunkReading, WithBatchInserts
{
    public function model(array $row)
    {
        return new User([
            'name'  => $row['name'],
            'email' => $row['email'],
        ]);
    }

    public function chunkSize(): int { return 1000; }
    public function batchSize(): int { return 500; }
}

Add SkipsOnFailure / SkipsOnError and log the collected failures rather than letting one bad row abort a 200k-row job. Combine with WithValidation if your row shape is user-supplied CSV.

Integration tips:

  • Wrap imports in a queued job. Spout is streaming, but the HTTP request still shouldn't host a ten-minute import; dispatch a job, chunk-read inside it, and store a status column the UI polls.
  • For user-facing downloads, return the facade response directly from the controller — it sets the correct Content-Type and Content-Disposition for the extension.
  • Test with Storage::fake('local') + Simplesheet::store(...) and assert the file exists; for imports, write a tiny fixture via an export first so round-trips stay honest.
  • Prefer ToCollection when you need to aggregate rows before touching the DB (dedupe, upsert, validation batches).

Gotchas and Tips

It is archived — budget for that. No security patches, no Laravel 10+ support out of the box, and the dependency on Spout (box/spout or the renamed openspout/openspout) may itself be abandoned. If this is greenfield, maatwebsite/excel (featureful, slower, PhpSpreadsheet) or a direct openspout/openspout dependency (zero abstraction tax, fully maintained) are the pragmatic choices. Choose simplesheet only if you're already on it or want its Laravel-Excel-like ergonomics on a legacy stack.

No styling, and column formatting is narrow. Spout cannot do fonts, fills, merged cells, or column widths. WithColumnFormatting maps column letters to Excel format codes and that's the extent of it. If product wants branded headers, this is the wrong tool — stop early.

Dates come back as strings. The reader gives you formatted text, not DateTime objects. Either format on read with WithColumnFormatting + explicit parsing, or keep date columns in ISO in the export and Carbon::parse() on import. This bites everyone once.

WithHeadingRow slugifies headings, so "First Name" becomes $row['first_name']. Duplicate or blank headings collapse — guard against this in generated CSVs, and remember row keys shift if a heading cell is merged.

CSV encoding and delimiters are not auto-detected reliably. Excel on Windows writes Windows-1252 with ; separators. Set input_encoding, delimiter, and enclosure via the custom-CSV-settings concern rather than debugging mojibake for an afternoon.

Batch inserts skip Eloquent events. WithBatchInserts goes through the query builder, so created/updated hooks, observers, and model mutators won't fire. If you rely on observers for indexing or notifications, either drop batching or dispatch events manually after each chunk.

Transactions: if the config exposes import transactions (Laravel Excel does), verify behaviour per chunk — a mid-import failure won't necessarily roll back earlier chunks. Design idempotency into model() (upserts keyed on a natural ID) instead of trusting a global transaction.

Debugging tip: when an import silently produces zero rows, check the row-count vs. heading offset first — WithHeadingRow consumes row 1, so $row[0]-style indexed access in old code will be off by one after enabling it. For stubborn cases, import with ToCollection and dd($collection->take(5)) before wiring the database.

Extension points are thin. The practical knobs are the concerns you implement on your class plus the facade's download/store/import calls. If you need custom behaviour (multiple sheets, pre-write hooks), you'll be reaching into Spout's writer directly — at which point the wrapper is adding little, and you should consider owning the Spout code yourself.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core