# Performance

# TLDR;

1 Data

2 Queuing

3 Cell caching

# Data

Most important part of performance is the amount of data that is held in memory...

# Limiting data

public function query(): Builder
{
    return User::query()
        ->with('role')
        ->select('users.id', 'users.name', 'users.email', 'role.name');
}
1
2
3
4
5
6

# Joining data

public function query(): Builder
{
    return User::query()
        ->innerJoin('roles')
        ->select('users.id', 'users.name', 'users.email', 'roles.name');
}
1
2
3
4
5
6

# Skipping Eloquent hydration

public function query(): Builder
{
    return DB
        ::table('users')
        ->innerJoin('roles')
        ->select('users.id', 'users.name', 'users.email', 'roles.name');
}
1
2
3
4
5
6
7

# Using LazyCollections/Generators

public function collection(): LazyCollection
{
    return DB::table('users')->select('id', 'name', 'email')->lazy();
}
1
2
3
4

# Queuing

When the data set is too big for the user to wait sync, you can queue it.

class ExportUsers implements ShouldQueue
{
    use Queueable;

    public $queue = 'long-running';
    public $timeout = 900;

    public function handle(): void
    {
        Excel::store(new UsersExport, 'users.xlsx');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# Job batching

Queued exports (and chunked queued imports) can be dispatched as a Laravel job batch (opens new window) instead of a chain by implementing the ShouldBatch marker interface.

use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\ShouldBatch;
use Illuminate\Contracts\Queue\ShouldQueue;

class UsersExport implements FromQuery, ShouldQueue, ShouldBatch
{
    use Exportable;

    public function query(): Builder
    {
        return User::query();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

When ShouldBatch is implemented, queue() and store() return a PendingBatch instead of a PendingDispatch, so you can attach batch callbacks:

$batch = (new UsersExport)->queue('users.xlsx')
    ->name('Users export')
    ->then(fn () => // batch succeeded)
    ->catch(fn () => // at least one job failed);

$batch->dispatch();
1
2
3
4
5
6

ShouldBatch requires WithMultipleSheets or WithChunkReading to have more than one job in the batch. A single-sheet export without chunk reading will still return a PendingBatch, but it will contain only one job.

# Cell caching

By default PhpSpreadsheet keeps all cell values in memory, however when dealing with large files, this might result into memory issues. If you want to mitigate that, you can configure a cell caching driver here.

When using the illuminate driver, it will store each value in the cache store. This can slow down the process, because it needs to store each value. However it will use less memory. It will automatically use your default cache store. However if you prefer to have the cell cache on a separate store, you can configure the store name here. You can use any store defined in your cache config. When leaving at "null" it will use the default store.

You can use the "batch" store if you want to only persist to the store when the memory limit is reached. You can tweak the memory limit in the config.