PHPackages                             anourvalar/office - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. anourvalar/office

ActiveLibrary[Templating &amp; Views](/categories/templating)

anourvalar/office
=================

Generate documents from existing Excel &amp; Word templates | Export tables to Excel (Grids)

2.6.15(1mo ago)24085.2k—0.3%23MITPHPPHP ^8.1

Since Mar 30Pushed 2mo ago5 watchersCompare

[ Source](https://github.com/AnourValar/office)[ Packagist](https://packagist.org/packages/anourvalar/office)[ Docs](https://github.com/AnourValar/office)[ RSS](/packages/anourvalar-office/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (69)Used By (0)

Office: Documents | Reports | Grids
===================================

[](#office-documents--reports--grids)

Installation
------------

[](#installation)

### Minimal

[](#minimal)

```
composer require anourvalar/office
```

### Phpspreadsheet is required to work with Excel (xlsx).

[](#phpspreadsheet-is-required-to-work-with-excel-xlsx)

```
composer require phpoffice/phpspreadsheet "^3.6"
```

### Zipstream-php is required to work with Word (docx).

[](#zipstream-php-is-required-to-work-with-word-docx)

```
composer require maennchen/zipstream-php "^3.1"
```

### Mpdf is required to work with PDF.

[](#mpdf-is-required-to-work-with-pdf)

```
composer require mpdf/mpdf: "^8.1"
```

Generate a document from an XLSX (Excel) template
-------------------------------------------------

[](#generate-a-document-from-an-xlsx-excel-template)

### One-dimensional table (basic usage)

[](#one-dimensional-table-basic-usage)

**template1.xlsx:**

[![Demo](https://camo.githubusercontent.com/8727e7f29013319bcb3f98aaf5d051507128eeed1aeb80f6f72b07e22bfebaad/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31302e706e67)](https://camo.githubusercontent.com/8727e7f29013319bcb3f98aaf5d051507128eeed1aeb80f6f72b07e22bfebaad/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31302e706e67)

```
$data = [
    // scalar
    'vat' => 'No',
    'total' => [
        'price' => 2004.14,
        'qty' => 3,
    ],

    // one-dimensional table
    'products' => [
        [
            'name' => 'Product #1',
            'price' => 989,
            'qty' => 1,
            'date' => new \DateTime('2022-03-30'),
        ],
        [
            'name' => 'Product #2',
            'price' => 1015.14,
            'qty' => 2,
            'date' => new \DateTime('2022-03-31'),
        ],
    ],
];

// Save to the file
(new \AnourValar\Office\SheetsService())
    ->generate(
        'template1.xlsx', // template filename
        $data // markers
    )
    ->saveAs(
        'generated_document.xlsx', // filename
        \AnourValar\Office\Format::Xlsx // save format
    );

// Output to the browser
header('Content-type: ' . \AnourValar\Office\Format::Xlsx->contentType());
header('Content-Disposition: attachment; filename="generated_document.xlsx"');
echo (new \AnourValar\Office\SheetsService())
    ->generate('template1.xlsx', $data)
    ->save(\AnourValar\Office\Format::Xlsx);

// Available formats:
// \AnourValar\Office\Format::Xlsx
// \AnourValar\Office\Format::Pdf
// \AnourValar\Office\Format::Html
// \AnourValar\Office\Format::Ods
```

**generated\_document.xlsx:**

[![Demo](https://camo.githubusercontent.com/63ba78d71f5b64c7619217d750e023e6971ea8caf732fab1e0d58bb83b50438b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31312e706e67)](https://camo.githubusercontent.com/63ba78d71f5b64c7619217d750e023e6971ea8caf732fab1e0d58bb83b50438b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31312e706e67)

**The same template with empty data**

[![Demo](https://camo.githubusercontent.com/dc9aa94e5b2337f132b3a5ed468339838086db7270c38cd04b21cd6b42298e6b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31322e706e67)](https://camo.githubusercontent.com/dc9aa94e5b2337f132b3a5ed468339838086db7270c38cd04b21cd6b42298e6b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d31322e706e67)

### Two-dimensional table

[](#two-dimensional-table)

**template2.xlsx:**

[![Demo](https://camo.githubusercontent.com/1f8e50ed0fb7dc8b554a076f8807d6c95fdce4586abcb469a6e651153acf56c5/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d32302e706e67)](https://camo.githubusercontent.com/1f8e50ed0fb7dc8b554a076f8807d6c95fdce4586abcb469a6e651153acf56c5/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d32302e706e67)

```
$data = [
    'best_manager' => 'Sveta',

    // two-dimensional table
    'managers' => [
        'titles' => [[ 'William', 'James', 'Sveta' ]],

        'values' => [
            [ // additional row
                'month' => 'January',
                'amount' => [700, 800, 900], // additional columns
            ],
            [
                'month' => 'February',
                'amount' => [7000, 8000, 9000],
            ],
            [
                'month' => 'March',
                'amount' => [70000, 80000, 90000],
            ],
        ],
    ],
];

// Save as XLSX (Excel)
(new \AnourValar\Office\SheetsService())
    ->generate('template2.xlsx', $data)
    ->saveAs('generated_document.xlsx'); // second argument (format) is optional
```

**generated\_document.xlsx:**

[![Demo](https://camo.githubusercontent.com/c7b935910c2dad2f41257d9ee6b27ca87470edd0c484b72b79607b0f0498071b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d32312e706e67)](https://camo.githubusercontent.com/c7b935910c2dad2f41257d9ee6b27ca87470edd0c484b72b79607b0f0498071b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d32312e706e67)

### Additional Features

[](#additional-features)

**template3.xlsx:**

[![Demo](https://camo.githubusercontent.com/ec25d52c1ebbaee67e258132f6719030527034b87d6ff74e8676f3021b800281/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d33302e706e67)](https://camo.githubusercontent.com/ec25d52c1ebbaee67e258132f6719030527034b87d6ff74e8676f3021b800281/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d33302e706e67)

```
$data = [
    'foo' => 'Hello',

    'bar' => function (SheetsInterface $driver, $column, $row) {
        $driver->insertImage('logo.png', $cell, ['width' => 100, 'offset_y' => -45]);
        return 'Logo!'; // replace marker "[bar]" with "Logo!"
    }
];

(new \AnourValar\Office\SheetsService())
    ->hookValue(function (SheetsInterface $driver, $column, $row, $value, $sheetIndex) {
        // Hook will be called for every cell which is changing

        $value .= ' world';
        return $value;
    })
    ->generate(
        'template3.ods', // ods template
        $data,
        true // cells auto format instead of template setup
    )
    ->saveAs('generated_document.xlsx');

// Available hooks:
// hookLoad: Closure(SheetsInterface $driver, string $templateFile, Format $templateFormat)
// hookBefore: Closure(SheetsInterface $driver, array &$data)
// hookValue: Closure(SheetsInterface $driver, string $column, int $row, $value, int $sheetIndex)
// hookAfter: Closure(SheetsInterface $driver)
```

**generated\_document.xlsx:**

[![Demo](https://camo.githubusercontent.com/f404cb25de06b5e45e918539cddd3d1f90f6a6f5acd7ec10c36a13f8acd286a3/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d33312e706e67)](https://camo.githubusercontent.com/f404cb25de06b5e45e918539cddd3d1f90f6a6f5acd7ec10c36a13f8acd286a3/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d33312e706e67)

### Dynamic templates

[](#dynamic-templates)

```
$data = [
    'group1' => [
        'name' => 'Group 1',
        'products' => [
            ['name' => 'Product 1', 'stock' => 101],
            ['name' => 'Product 2', 'stock' => 102],
        ],
    ],
    'group2' => [
        'name' => 'Group 2',
        'products' => [
            ['name' => 'Product 3', 'stock' => 103],
            ['name' => 'Product 4', 'stock' => 104],
        ],
    ],
];

(new \AnourValar\Office\SheetsService())
    ->hookLoad(function ($driver, string $templateFile, $templateFormat) {
        // create empty document instead of using existing
        return $driver->create();
    })
    ->hookBefore(function ($driver, array &$data) {
        // place markers on-fly
        $row = 1;
        foreach (array_keys($data) as $group) {
            // group's title
            $driver
                ->setValue("A$row", "[{$group}.name]")
                ->mergeCells("A$row:B$row")
                ->setStyle("A$row", ['align' => 'center', 'bold' => true]);
            $row++;

            // group's products
            $driver
                ->setValue("A$row", "[$group.products.name]")
                ->setValue("B$row", "[$group.products.stock]");
            $row++;
        }
    })
    ->generate('', $data)
    ->saveAs('generated_document.xlsx');
```

**Dynamic template overview**

[![Demo](https://camo.githubusercontent.com/62caeb2ddc65885e6ed2b28f4128672ef18e28a745bbc9d11b9d44febb04723b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d36312e706e67)](https://camo.githubusercontent.com/62caeb2ddc65885e6ed2b28f4128672ef18e28a745bbc9d11b9d44febb04723b/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d36312e706e67)

**generated\_document.xlsx:**

[![Demo](https://camo.githubusercontent.com/c3f9abd23579de8df70f9896cdc1bb8d6bf34941b491dc461945a1b06e19695c/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d36322e706e67)](https://camo.githubusercontent.com/c3f9abd23579de8df70f9896cdc1bb8d6bf34941b491dc461945a1b06e19695c/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d36322e706e67)

### Merge (union) few documents to a single file

[](#merge-union-few-documents-to-a-single-file)

```
$dataA = ['foo' => 'hello'];
$dataB = ['foo' => 'world'];

$documentA = (new \AnourValar\Office\SheetsService())->generate('template.xlsx', $dataA);
$documentB = (new \AnourValar\Office\SheetsService())->generate('template.xlsx', $dataB);

$mixer = new \AnourValar\Office\Mixer();
$mixer($documentA, $documentB)->saveAs('generated_document.xlsx');
```

### Access the PhpSpreadsheet directly (default driver)

[](#access-the-phpspreadsheet-directly-default-driver)

```
(new \AnourValar\Office\SheetsService())
    ->hookBefore(function (\AnourValar\Office\Drivers\PhpSpreadsheetDriver $driver, array &$data) {
        $spreadsheet = $driver->spreadsheet;

        // @see \PhpOffice\PhpSpreadsheet\Spreadsheet
        $spreadsheet->createSheet()->setTitle('Foo Bar'); // adding a new Worksheet
    })
    ->generate('template.xlsx', [])
    ->saveAs('generated_document.xlsx');
```

Generate a document from an DOCX (Word) template
------------------------------------------------

[](#generate-a-document-from-an-docx-word-template)

```
(new \AnourValar\Office\DocumentService)
    ->generate('template.docx', ['foo' => 'bar'])
    ->saveAs('generated_document.docx');
```

**template.docx:**

[![Demo](https://camo.githubusercontent.com/07b96c4510e23f7bd7252d95d7a1a630db15befcf3cc6dc46282468a2ed47db8/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d37302e706e67)](https://camo.githubusercontent.com/07b96c4510e23f7bd7252d95d7a1a630db15befcf3cc6dc46282468a2ed47db8/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d37302e706e67)

**generated\_document.docx:**

[![Demo](https://camo.githubusercontent.com/360a986e3704c9679701f620daac40d1cd268631a12555548ceb1b14ada8f0e3/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d37312e706e67)](https://camo.githubusercontent.com/360a986e3704c9679701f620daac40d1cd268631a12555548ceb1b14ada8f0e3/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d37312e706e67)

Export table (Grid)
-------------------

[](#export-table-grid)

### Simple usage

[](#simple-usage)

```
$data = [
    ['William', 3000],
    ['James', 4000],
    ['Sveta', 5000],
];

// Save as XLSX (Excel)
(new \AnourValar\Office\GridService())
    ->generate(
        ['Name', 'Sales'], // headers
        $data // data
    )
    ->saveAs('generated_grid.xlsx');
```

**generated\_grid.xlsx:**

[![Demo](https://camo.githubusercontent.com/0bb415562d487544a410f148a635b9bc6cf58a50cc425391cfad2616ae542a80/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d34312e706e67)](https://camo.githubusercontent.com/0bb415562d487544a410f148a635b9bc6cf58a50cc425391cfad2616ae542a80/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d34312e706e67)

### Advanced usage (generators)

[](#advanced-usage-generators)

```
$headers = [
    ['title' => 'Name', 'width' => 30],
    ['title' => 'Sales'],
];

$data = function () {
    yield ['name' => 'William', 'sales' => 3000];
    yield ['name' => 'James', 'sales' => 4000];
    yield ['name' => 'Sveta', 'sales' => 5000];
};

// Save as XLSX (Excel)
(new \AnourValar\Office\GridService())
    ->hookHeader(function (GridInterface $driver, mixed $header, $key, $column) {
        if (isset($header['width'])) {
            $driver->setWidth($column, $header['width']); // column with fixed width
        } else {
            $driver->autoWidth($column); // column with auto width
        }

        return $header['title'];
    })
    ->hookRow(function (GridInterface $driver, mixed $row, $key) {
        return [
            $row['name'],
            $row['sales'],
        ];
    })
    ->hookAfter(function (
        GridInterface $driver,
        string $headersRange,
        string $dataRange,
        string $totalRange,
        array $columns
    ) {
        $driver->setSheetTitle('Foo');

        $driver->setStyle(
            $headersRange, // A1:B1
            ['bold' => true, 'background_color' => 'EEEEEE']
        );

        $driver->setStyle(
            $totalRange, // A1:B4
            ['borders' => true, 'align' => 'left']
        );
    })
    ->generate($headers, $data)
    ->saveAs('generated_grid.xlsx');
```

**generated\_grid.xlsx:**

[![Demo](https://camo.githubusercontent.com/26373606dad91c05480bdaae19a0a23552662bb6ca27d0c29ec1b88c9e5542ee/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d35312e706e67)](https://camo.githubusercontent.com/26373606dad91c05480bdaae19a0a23552662bb6ca27d0c29ec1b88c9e5542ee/68747470733a2f2f616e6f75722e72752f7265736f75726365732f6f66666963652d76312d35312e706e67)

### Performance

[](#performance)

By default, GridService uses PhpSpreadsheetDriver which gives a lot of features and flexability. The only cons are performance and memory consumtion.

ZipDriver as an alternative is simpler, but much more faster:

```
composer require maennchen/zipstream-php "^3.1"
```

```
$data = [
    ['William', 3000],
    ['James', 4000],
    ['Sveta', 5000],
];

// Save as XLSX (Excel)
(new \AnourValar\Office\GridService(new \AnourValar\Office\Drivers\ZipDriver()))
    ->generate(['Name', 'Sales'], $data)
    ->saveAs('generated_grid.xlsx');
```

###  Health Score

61

—

FairBetter than 99% of packages

Maintenance86

Actively maintained with recent releases

Popularity50

Moderate usage in the ecosystem

Community17

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 96.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~21 days

Total

68

Last Release

53d ago

Major Versions

1.5.5 → 2.0.02022-05-27

PHP version history (2 changes)1.0.0PHP &gt;=8.1

2.1.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/db1765d50b04dca380c835746bee50f75be1683fd930f276def046cef17f5d2c?d=identicon)[AnourValar](/maintainers/AnourValar)

---

Top Contributors

[![AnourValar](https://avatars.githubusercontent.com/u/54237693?v=4)](https://github.com/AnourValar "AnourValar (77 commits)")[![markwalet](https://avatars.githubusercontent.com/u/11446771?v=4)](https://github.com/markwalet "markwalet (2 commits)")[![Alexandr1994](https://avatars.githubusercontent.com/u/4815107?v=4)](https://github.com/Alexandr1994 "Alexandr1994 (1 commits)")

---

Tags

pdfexportgeneratordocexcelxlsxlsxanourvalartemplateworddocxodsgridreportviewvariablestabledocumentplaceholdergenerateenginecontractfillmarkerstemplaterreplacers

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/anourvalar-office/health.svg)

```
[![Health](https://phpackages.com/badges/anourvalar-office/health.svg)](https://phpackages.com/packages/anourvalar-office)
```

###  Alternatives

[phpoffice/phpword

PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)

7.6k34.7M186](/packages/phpoffice-phpword)[icircle/docx-template-in-php

Create Templates in MS Word docx format and use them Creating Business documents using PHP

4422.7k](/packages/icircle-docx-template-in-php)[aspose-cloud/aspose-words-cloud

Open, generate, edit, split, merge, compare and convert Word documents. Integrate Cloud API into your solutions to manipulate documents. Convert PDF to Word (DOC, DOCX, ODT, RTF and HTML) and in the opposite direction.

32157.4k](/packages/aspose-cloud-aspose-words-cloud)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
