{"id":13452963,"url":"https://github.com/rap2hpoutre/fast-excel","last_synced_at":"2026-10-19T08:42:46.583Z","repository":{"id":41555463,"uuid":"128174809","full_name":"rap2hpoutre/fast-excel","owner":"rap2hpoutre","description":"🦉 Fast Excel import/export for Laravel","archived":false,"fork":false,"pushed_at":"2026-09-14T21:38:27.000Z","size":374,"stargazers_count":2389,"open_issues_count":5,"forks_count":272,"subscribers_count":25,"default_branch":"master","last_synced_at":"2026-10-14T07:36:02.978Z","etag":null,"topics":["csv","excel","fast","hacktoberfest","laravel","laravel-package","memory-efficiency","php","xlsx"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rap2hpoutre.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"rap2hpoutre","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2018-04-05T07:49:06.000Z","updated_at":"2026-10-11T20:10:25.000Z","dependencies_parsed_at":"2024-06-03T09:50:21.583Z","dependency_job_id":"1bebcf08-788d-4d48-a2f1-5885bbe79fb8","html_url":"https://github.com/rap2hpoutre/fast-excel","commit_stats":{"total_commits":170,"total_committers":34,"mean_commits":5.0,"dds":0.611764705882353,"last_synced_commit":"c6d2b8b707b85f7c43028d3b4cc969388d8b6c17"},"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"purl":"pkg:github/rap2hpoutre/fast-excel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap2hpoutre%2Ffast-excel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap2hpoutre%2Ffast-excel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap2hpoutre%2Ffast-excel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap2hpoutre%2Ffast-excel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rap2hpoutre","download_url":"https://codeload.github.com/rap2hpoutre/fast-excel/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap2hpoutre%2Ffast-excel/sbom","scorecard":{"id":761803,"data":{"date":"2025-08-11","repo":{"name":"github.com/rap2hpoutre/fast-excel","commit":"a460ac5b78f01162eadcae49053017686c53e102"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Code-Review","score":5,"reason":"Found 16/30 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: topLevel 'contents' permission set to 'write': .github/workflows/dependabot-auto-merge.yml:6","Info: topLevel 'contents' permission set to 'read': .github/workflows/tests.yml:8","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/dependabot-auto-merge.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/rap2hpoutre/fast-excel/dependabot-auto-merge.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/rap2hpoutre/fast-excel/tests.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tests.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/rap2hpoutre/fast-excel/tests.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tests.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/rap2hpoutre/fast-excel/tests.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T23:39:58.750Z","repository_id":41555463,"created_at":"2025-08-22T23:39:58.750Z","updated_at":"2025-08-22T23:39:58.750Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":344872684,"owners_count":38341143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-10-03T21:59:58.778Z","status":"online","status_checked_at":"2026-10-18T02:00:06.448Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csv","excel","fast","hacktoberfest","laravel","laravel-package","memory-efficiency","php","xlsx"],"created_at":"2024-07-31T08:00:29.430Z","updated_at":"2026-10-19T08:42:46.577Z","avatar_url":"https://github.com/rap2hpoutre.png","language":"PHP","funding_links":["https://github.com/sponsors/rap2hpoutre"],"categories":["Popular Packages","Data and Storage","Paquetes utiles"],"sub_categories":["Serialization and Formats"],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/36028424/40173202-9a03d68a-5a03-11e8-9968-6b7e3b4f8a1b.png\"\u003e\n\u003c/p\u003e\n\n[![Version](https://poser.pugx.org/rap2hpoutre/fast-excel/version?format=flat)](https://packagist.org/packages/rap2hpoutre/fast-excel)\n[![License](https://poser.pugx.org/rap2hpoutre/fast-excel/license?format=flat)](https://packagist.org/packages/rap2hpoutre/fast-excel)\n[![StyleCI](https://github.styleci.io/repos/128174809/shield?branch=master)](https://github.styleci.io/repos/128174809?branch=master)\n[![Tests](https://github.com/rap2hpoutre/fast-excel/actions/workflows/tests.yml/badge.svg)](https://github.com/rap2hpoutre/fast-excel/actions/workflows/tests.yml)\n[![Total Downloads](https://poser.pugx.org/rap2hpoutre/fast-excel/downloads)](https://packagist.org/packages/rap2hpoutre/fast-excel)\n\nFast Excel import/export for Laravel, thanks to [Spout](https://github.com/box/spout).\nSee [benchmarks](#benchmarks) below.\n\n## Quick start\n\nInstall via composer:\n\n```\ncomposer require rap2hpoutre/fast-excel\n```\n\nExport a Model to `.xlsx` file:\n\n```php\nuse Rap2hpoutre\\FastExcel\\FastExcel;\nuse App\\User;\n\n// Load users\n$users = User::all();\n\n// Export all users\n(new FastExcel($users))-\u003eexport('file.xlsx');\n```\n\n## Export\n\nExport a Model, Query or **Collection**:\n\n```php\n$list = collect([\n    [ 'id' =\u003e 1, 'name' =\u003e 'Jane' ],\n    [ 'id' =\u003e 2, 'name' =\u003e 'John' ],\n]);\n\n// export() returns the absolute path to the written file\n$path = (new FastExcel($list))-\u003eexport('file.xlsx');\n```\n\nExport `xlsx`, `ods` and `csv`:\n\n```php\n$invoices = App\\Invoice::orderBy('created_at', 'DESC')-\u003eget();\n(new FastExcel($invoices))-\u003eexport('invoices.csv');\n```\n\nExport only some attributes specifying columns names:\n\n```php\n(new FastExcel(User::all()))-\u003eexport('users.csv', function ($user) {\n    return [\n        'Email' =\u003e $user-\u003eemail,\n        'First Name' =\u003e $user-\u003efirstname,\n        'Last Name' =\u003e strtoupper($user-\u003elastname),\n    ];\n});\n```\n\nHide columns from the exported file while still being able to use them inside the\ncallback, using `hideColumnsPrefixedWith()`. This is handy for callback-only data\n(lookups, computed flags, etc.) that should not appear as a column:\n\n```php\n(new FastExcel(User::all()))-\u003ehideColumnsPrefixedWith()-\u003eexport('users.csv', function ($user) {\n    return [\n        'Name'  =\u003e $user-\u003ename,\n        '_role' =\u003e $user-\u003erole, // used for logic below, never written to the file\n    ];\n});\n```\n\nColumns are hidden from both the header row and every data row. The prefix\ndefaults to `_`, and any other one can be used — `hideColumnsPrefixedWith('tmp_')`.\nHiding is opt-in: unless you call this method, every column is exported, so\ncolumns that already start with an underscore keep working as before.\n\nDownload (from a controller method):\n\n```php\nreturn (new FastExcel(User::all()))-\u003edownload('file.xlsx');\n```\n\n## Import\n\n`import` returns a Collection:\n\n```php\n$collection = (new FastExcel)-\u003eimport('file.xlsx');\n```\n\nImport a `csv` with specific delimiter, enclosure characters and \"gbk\" encoding:\n\n```php\n$collection = (new FastExcel)-\u003econfigureCsv(';', '#', 'gbk')-\u003eimport('file.csv');\n```\n\nImport and insert to database:\n\n```php\n$users = (new FastExcel)-\u003eimport('file.xlsx', function ($line) {\n    return User::create([\n        'name' =\u003e $line['Name'],\n        'email' =\u003e $line['Email']\n    ]);\n});\n```\n\nLimit the number of data rows imported with `limitRows` (headers excluded). It works with both `import` and `importLazy`:\n\n```php\n$collection = (new FastExcel)-\u003elimitRows(100)-\u003eimport('file.xlsx');\n```\n\nStart reading at a given row with `startRow`. On its own, `startRow` also treats\nthat row as the header row. Use `headerRow` to read the headers from their real\nposition while data starts further down:\n\n```php\n// Headers from row 1, data from row 155 onwards.\n$collection = (new FastExcel)-\u003eheaderRow(1)-\u003estartRow(155)-\u003eimport('file.xlsx');\n```\n\nTogether with `limitRows`, that is how a large file is imported in chunks — one\nslice per job run, each with the correct header names:\n\n```php\n$chunk = (new FastExcel)\n    -\u003eheaderRow(1)\n    -\u003estartRow(2 + ($page * 1000)) // data begins on row 2\n    -\u003elimitRows(1000)\n    -\u003eimport('file.xlsx');\n```\n\n`headerRow` is opt-in: without it, `startRow` keeps its previous behaviour of\nusing the start row as the header row.\n\nTruncate each imported row after a given column with `limitColumns`, which takes\neither a column reference or a number of columns:\n\n```php\n$collection = (new FastExcel)-\u003elimitColumns('H')-\u003eimport('file.xlsx');\n$collection = (new FastExcel)-\u003elimitColumns(8)-\u003eimport('file.xlsx');\n```\n\nThis is useful for files where formatting has been applied to entire rows: the\nspreadsheet then reports thousands of trailing cells that look like real columns,\nand importing them yields empty `column_9`, `column_10`… entries on every row.\nThose cells are dropped from the imported collection (OpenSpout still parses the\nsheet). Like `limitRows`, it works with both `import` and `importLazy`.\n\nKeep specific columns (and drop everything else, including middle empties) with\n`onlyColumns`. Letters and 1-based indexes can be mixed; order is preserved:\n\n```php\n$collection = (new FastExcel)-\u003eonlyColumns(['A', 'B', 'H'])-\u003eimport('file.xlsx');\n$collection = (new FastExcel)-\u003eonlyColumns([1, 2, 8])-\u003eimport('file.xlsx');\n```\n\n`onlyColumns` and `limitColumns` cannot both be active — setting one clears the other.\nPassing `null` only clears that setter and leaves the other alone.\n\n## Facades\n\nYou may use FastExcel with the optional Facade. Add the following line to ``config/app.php`` under the ``aliases`` key.\n\n````php\n'FastExcel' =\u003e Rap2hpoutre\\FastExcel\\Facades\\FastExcel::class,\n````\n\nUsing the Facade, you will not have access to the constructor. You may set your export data using the ``data`` method.\n\n````php\n$list = collect([\n    [ 'id' =\u003e 1, 'name' =\u003e 'Jane' ],\n    [ 'id' =\u003e 2, 'name' =\u003e 'John' ],\n]);\n\nFastExcel::data($list)-\u003eexport('file.xlsx');\n````\n\n## Global helper\n\nFastExcel provides a convenient global helper to quickly instantiate the FastExcel class anywhere in a Laravel application.\n\n```php\n$collection = fastexcel()-\u003eimport('file.xlsx');\nfastexcel($collection)-\u003eexport('file.xlsx');\n```\n\n## Advanced usage\n\n### Export multiple sheets\n\nExport multiple sheets by creating a `SheetCollection`:\n\n```php\n$sheets = new SheetCollection([\n    User::all(),\n    Project::all()\n]);\n(new FastExcel($sheets))-\u003eexport('file.xlsx');\n```\n\nUse index to specify sheet name:\n```php\n$sheets = new SheetCollection([\n    'Users' =\u003e User::all(),\n    'Second sheet' =\u003e Project::all()\n]);\n```\n\n### Import multiple sheets\n\nImport multiple sheets by using `importSheets`:\n\n```php\n$sheets = (new FastExcel)-\u003eimportSheets('file.xlsx');\n```\n\nYou can also import a specific sheet by its number:\n\n```php\n$users = (new FastExcel)-\u003esheet(3)-\u003eimport('file.xlsx');\n```\n\n`sheet()` also accepts a sheet name, so you can select a sheet without knowing its position:\n\n```php\n$users = (new FastExcel)-\u003esheet('Users')-\u003eimport('file.xlsx');\n```\n\nImport multiple sheets with sheets names:\n\n```php\n$sheets = (new FastExcel)-\u003ewithSheetsNames()-\u003eimportSheets('file.xlsx');\n```\n\nUse `withSheetContext()` to receive the current sheet name as the first argument\nof the `importSheets` callback — handy when the same field names need to be\nhandled differently per sheet:\n\n```php\n$sheets = (new FastExcel)\n    -\u003ewithSheetContext()\n    -\u003eimportSheets('file.xlsx', function ($sheetName, $row) {\n        if ($sheetName === 'Users' \u0026\u0026 empty($row['email'])) {\n            return null; // skip rows without an email on the Users sheet\n        }\n\n        return $row + ['_sheet' =\u003e $sheetName];\n    });\n```\n\n### Export large collections (low memory)\n\nPassing a materialized collection (`User::all()`, `-\u003eget()`, `collect([...])`) loads every\nrow into memory *before* the export starts, so it grows with the size of the data and a\nlarge enough dataset fails outright with `Allowed memory size of N bytes exhausted`. Feed a\nlazy source instead and peak memory stays flat, whatever the row count.\n\nEloquent's `cursor()` (or `-\u003elazy()`) returns a\n[`LazyCollection`](https://laravel.com/docs/collections#lazy-collections), which FastExcel\nstreams row by row — no wrapper needed:\n\n```php\n// Export consumes only a few MB, even with 10M+ rows.\n(new FastExcel(User::cursor()))-\u003eexport('users.xlsx');\n```\n\nFor any other source, hand `export()` a generator [using `yield`](https://www.php.net/manual/en/language.generators.syntax.php):\n\n```php\nfunction rowsGenerator() {\n    foreach (some_paginated_source() as $row) {\n        yield $row;\n    }\n}\n\n(new FastExcel(rowsGenerator()))-\u003eexport('test.xlsx');\n```\n\nExporting 1,000,000 rows (4 columns) under a 512 MB `memory_limit`:\n\n| How you export | Peak memory | Result |\n| --- | --- | --- |\n| `export()` from a materialized collection | \u003e 512 MB | **fails** once it exceeds `memory_limit` |\n| `export()` from a cursor / generator (streaming) | ~4 MB | always completes |\n\nStreaming does not change export *speed* (that is dominated by the underlying\n[OpenSpout](https://github.com/openspout/openspout) writer) — it is what keeps memory flat\nso very large files finish at all. `transpose()` cannot stream, as it must buffer the whole\ndataset to pivot rows and columns.\n\n### Import large files (low memory)\n\n`import` returns a Collection containing every row, so memory grows with the size of\nthe file. On a file larger than your PHP `memory_limit`, the default `import()` fails\noutright with `Allowed memory size of N bytes exhausted`. To import a large file\nwithout running out of memory, pass a callback and **return `null`** — each row is\nthen processed but not accumulated, so memory stays flat:\n\n```php\n// Memory stays flat regardless of the number of rows.\n(new FastExcel)-\u003eimport('file.xlsx', function ($line) {\n    User::create([\n        'name'  =\u003e $line['Name'],\n        'email' =\u003e $line['Email'],\n    ]);\n\n    return null; // don't keep the row in memory\n});\n```\n\n\u003e If the callback returns a value (for example the created model), that value is\n\u003e collected and returned to you — handy for small files, but it keeps every row in\n\u003e memory. Return `null` when you only need the side effect (e.g. inserting rows).\n\nImporting a 730,000-row file (8 columns), measured under a constrained\n`memory_limit`:\n\n| How you import | Peak memory | Result |\n| --- | --- | --- |\n| `import($file)` (returns a Collection) | ~440 MB | **fails** once it exceeds `memory_limit` |\n| `import($file, fn ($row) =\u003e null)` (streaming) | ~4 MB | always completes |\n\nReading speed is the same either way — it is dominated by the underlying\n[OpenSpout](https://github.com/openspout/openspout) parser, not by how the rows are\nreturned. The difference above is memory, which is what lets very large files finish\nat all.\n\nIf you would rather keep working with the rows than process them inside a callback,\n`importLazy` returns a [`LazyCollection`](https://laravel.com/docs/collections#lazy-collections)\nthat streams rows one at a time — you get the full Collection API while memory stays\nflat:\n\n```php\nuse Illuminate\\Support\\LazyCollection;\n\n(new FastExcel)-\u003eimportLazy('file.xlsx')\n    -\u003echunk(1000)\n    -\u003eeach(function (LazyCollection $chunk) {\n        User::insert($chunk-\u003eall());\n    });\n```\n\n`importLazy` accepts the same optional callback as `import`, and honors `sheet()`,\n`withoutHeaders()`, and header de-duplication. Transposing (`transpose()`) is not\nsupported with lazy import.\n\n### Add header and rows style\n\nAdd header and rows style with `headerStyle` and `rowsStyle` methods.\n\n```php\nuse OpenSpout\\Common\\Entity\\Style\\Style;\n\n$header_style = (new Style())-\u003esetFontBold();\n\n$rows_style = (new Style())\n    -\u003esetFontSize(15)\n    -\u003esetShouldWrapText()\n    -\u003esetBackgroundColor(\"EDEDED\");\n\nreturn (new FastExcel($list))\n    -\u003eheaderStyle($header_style)\n    -\u003erowsStyle($rows_style)\n    -\u003edownload('file.xlsx');\n```\n\nYou can also style each header column individually with `setHeaderColumnStyles`\n(the header-row counterpart of `setColumnStyles`). Keys are the zero-based\ncolumn positions:\n\n```php\nuse OpenSpout\\Common\\Entity\\Style\\Color;\nuse OpenSpout\\Common\\Entity\\Style\\Style;\n\nreturn (new FastExcel($list))\n    -\u003esetHeaderColumnStyles([\n        0 =\u003e (new Style())-\u003esetBackgroundColor(Color::YELLOW),\n        1 =\u003e (new Style())-\u003esetFontColor(Color::BLUE),\n    ])\n    -\u003edownload('file.xlsx');\n```\n\n### Set column widths\n\nColumn widths are an OpenSpout writer option, so they are set through\n`configureOptionsUsing`. Widths are expressed in Excel's own unit (roughly the\nnumber of characters that fit), and column numbers are **1-based**:\n\n```php\n(new FastExcel($list))\n    -\u003econfigureOptionsUsing(function ($options) {\n        $options-\u003esetColumnWidth(40, 1);      // first column\n        $options-\u003esetColumnWidth(15, 2, 3);   // second and third columns\n    })\n    -\u003eexport('file.xlsx');\n```\n\nUse `setColumnWidthForRange` for a contiguous span:\n\n```php\n(new FastExcel($list))\n    -\u003econfigureOptionsUsing(function ($options) {\n        $options-\u003esetColumnWidthForRange(20, 1, 4); // columns 1 through 4\n    })\n    -\u003eexport('file.xlsx');\n```\n\nThis works with streaming exports (cursors and generators) as well, since widths\nare written when the file is finalized rather than per row.\n\nOnly `xlsx` and `ods` support widths. `csv` has no notion of column width, and\n`OpenSpout\\Writer\\CSV\\Options` does not define `setColumnWidth()` at all — calling\nit on a csv export raises `Error: Call to undefined method`. If the same code path\ncan export either format, guard the call:\n\n```php\n-\u003econfigureOptionsUsing(function ($options) {\n    if (method_exists($options, 'setColumnWidth')) {\n        $options-\u003esetColumnWidth(40, 1);\n    }\n})\n```\n\nNote that widths are explicit — there is no automatic sizing to fit the content.\n\n### Export values as strings or numbers\n\nBy default numbers are written as numbers and strings as strings. Use\n`stringValues()` to force every value to a text cell — handy to keep leading\nzeros or long numeric IDs (e.g. phone numbers) intact:\n\n```php\n// 0660123456 stays \"0660123456\" instead of becoming 660123456\n(new FastExcel($users))-\u003estringValues()-\u003eexport('users.xlsx');\n```\n\nNeed finer control? `setColumnFormat()` overrides the type per column and takes\nprecedence over `stringValues()`:\n\n```php\n(new FastExcel($users))\n    -\u003estringValues()                                  // text by default\n    -\u003esetColumnFormat([\n        'id'    =\u003e 'number',                          // keep as number\n        'phone' =\u003e 'string',                          // keep as text\n    ])\n    -\u003eexport('users.xlsx');\n```\n\n### Escape formulas (prevent formula injection)\n\nBy default any string starting with `=` (e.g. `=1+2`) is written as a live\nformula cell, which can corrupt the file or enable CSV/formula injection. Call\n`escapeFormulas()` to write string values as literal text cells instead:\n\n```php\n(new FastExcel($rows))-\u003eescapeFormulas()-\u003eexport('file.xlsx');\n```\n\n### Skip rows during export\n\nIf the export callback returns `null` or `false` for a row, that row is\nskipped entirely and does not appear in the output file:\n\n```php\n(new FastExcel($users))-\u003eexport('file.xlsx', function ($user) {\n    return $user-\u003eactive ? ['name' =\u003e $user-\u003ename, 'email' =\u003e $user-\u003eemail] : null;\n});\n```\n\n### Use raw OpenSpout Cell instances\n\nFor full control over a single cell's type or style, a row value may be an\n`OpenSpout\\Common\\Entity\\Cell` instance. It is written through as-is, while the\nother (scalar) values in the row keep their normal handling:\n\n```php\nuse OpenSpout\\Common\\Entity\\Cell;\nuse OpenSpout\\Common\\Entity\\Style\\Style;\n\n$users = collect([\n    ['name' =\u003e 'John', 'note' =\u003e Cell::fromValue('paid', (new Style())-\u003esetFontBold())],\n    ['name' =\u003e 'Jane', 'note' =\u003e 'pending'],\n]);\n\n(new FastExcel($users))-\u003eexport('users.xlsx');\n```\n\n## Why?\n\nFastExcel is intended at being Laravel-flavoured [Spout](https://github.com/box/spout):\na simple, but elegant wrapper around [Spout](https://github.com/box/spout) with the goal\nof simplifying **imports and exports**. It could be considered as a faster (and memory friendly) alternative\nto [Laravel Excel](https://laravel-excel.com/), with less features.\nUse it only for simple tasks.\n\n## Benchmarks\n\nXLSX export of 10,000 rows × 20 columns of random data, average of 10 runs (PHP 8.4, July 2026). **Don't trust benchmarks.**\n\n![FastExcel vs Laravel Excel — memory and time benchmark](bench/benchmark.svg)\n\n|   | Peak memory | Execution time |\n|---|---|---|\n| Laravel Excel 3.1 | 218 MB | 2.53 s |\n| FastExcel — collection | 42 MB | 0.90 s |\n| **FastExcel — generator** | **4 MB** | **0.93 s** |\n\nFastExcel streams rows through [OpenSpout](https://github.com/openspout/openspout) instead of building the whole\nspreadsheet in memory. Feed it a generator (or `importLazy()` on the read side) and peak memory stays flat no matter how\nmany rows you export — here about **55× less** than Laravel Excel. Reproduce with\n[`bench/readme-export-bench.php`](bench/readme-export-bench.php).\n\nStill, remember that [Laravel Excel](https://laravel-excel.com/) **has many more features.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frap2hpoutre%2Ffast-excel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frap2hpoutre%2Ffast-excel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frap2hpoutre%2Ffast-excel/lists"}