PHPackages                             glhd/conveyor-belt - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. glhd/conveyor-belt

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

glhd/conveyor-belt
==================

2.3.0(4mo ago)150128.8k—0.1%5[1 issues](https://github.com/glhd/conveyor-belt/issues)MITPHPPHP &gt;= 8.0CI passing

Since Jan 28Pushed 4mo ago2 watchersCompare

[ Source](https://github.com/glhd/conveyor-belt)[ Packagist](https://packagist.org/packages/glhd/conveyor-belt)[ RSS](/packages/glhd-conveyor-belt/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (10)Dependencies (26)Versions (13)Used By (0)

 [ ![Build Status](https://github.com/glhd/conveyor-belt/workflows/PHPUnit/badge.svg) ](https://github.com/glhd/conveyor-belt/actions) [ ![Latest Stable Release](https://camo.githubusercontent.com/8abff2eb7be2a336115d7e04f2c465efd15480eae5d5a5288e7da06ce302de97/68747470733a2f2f706f7365722e707567782e6f72672f676c68642f636f6e7665796f722d62656c742f762f737461626c65) ](https://packagist.org/packages/glhd/conveyor-belt) [ ![MIT Licensed](https://camo.githubusercontent.com/302d0fe88317347bd3b47367d7772c087b9a4e0c3c836c9daadfd09021c591e4/68747470733a2f2f706f7365722e707567782e6f72672f676c68642f636f6e7665796f722d62656c742f6c6963656e7365) ](./LICENSE) [ ![Follow @cmorrell.com on bsky](https://camo.githubusercontent.com/8790699596340eff27762850c822f5e8e08a379e860085e46bf69359e4d2bc18/68747470733a2f2f696d672e736869656c64732e696f2f626c7565736b792f666f6c6c6f776572732f636d6f7272656c6c2e636f6d) ](https://bsky.app/profile/cmorrell.com)

Conveyor Belt
=============

[](#conveyor-belt)

Conveyor Belt provides all the underlying mechanics necessary to write artisan commands that process lots of data efficiently.

Quickly process 1000's of records
---------------------------------

[](#quickly-process-1000s-of-records)

[![Screencast of default behavior](img/default.svg)](img/default.svg)

Get verbose output when necessary
---------------------------------

[](#get-verbose-output-when-necessary)

[![Screencast of verbose behavior](img/verbose.svg)](img/verbose.svg)

Step through execution &amp; log operations if needed
-----------------------------------------------------

[](#step-through-execution--log-operations-if-needed)

[![Screencast of step behavior](img/step.svg)](img/step.svg)

See what data is changed by your commands
-----------------------------------------

[](#see-what-data-is-changed-by-your-commands)

[![Screencast of diff behavior](img/diff.svg)](img/diff.svg)

And so much more
----------------

[](#and-so-much-more)

[![Screencast of help view](img/more.svg)](img/more.svg)

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

[](#installation)

```
# composer require glhd/conveyor-belt
```

Usage
-----

[](#usage)

To use Conveyor Belt, use one of the conveyor belt traits in your Laravel command:

### Databases

[](#databases)

- `\Glhd\ConveyorBelt\IteratesIdQuery` — use this if your underlying query can be ordered by `id` (improves performance)
- `\Glhd\ConveyorBelt\IteratesQuery` — use this if your query **is not ordered by `id`**

### Files

[](#files)

- `\Glhd\ConveyorBelt\IteratesSpreadsheet` — use this to read CSV or Excel files
- `\Glhd\ConveyorBelt\IteratesJson` — use this to read JSON files or JSON API data

### Other

[](#other)

- `\Glhd\ConveyorBelt\IteratesEnumerable` — use this to work with any generic data source

Configuration
-------------

[](#configuration)

Most commands can be configured by setting public properties on the command itself. For example, if you want to enable exception handling, you would add `public $collect_exceptions = true;` to your command. Each config option can also be managed by overriding a function (if you need more dynamic control over its value). See the source of each trait to find the appropriate function name.

### Common for all commands

[](#common-for-all-commands)

- `$collect_exceptions` — set to `true` to have your command continue to run if an exception is triggered (the exception will be printed at the end of command execution)
- `$row_name` — set this to customize command output (e.g. if you're operating on `User` models you could set this to `"user"`)
- `$row_name_plural` — the plural of `$row_name` (usually not necessary, as we use `Str::plural` for you)

### `IteratesQuery`

[](#iteratesquery)

- `$chunk_size` — the number of database records to load at one time
- `$use_transaction` — whether to run the whole command inside a database transaction (can cause locking issues if your command runs for a long time)

### `IteratesIdQuery`

[](#iteratesidquery)

The `IteratesIdQuery` trait accepts all the options that `IteratesQuery` does, as well as:

- `$id_column` — the name of your ID column (if it is not `"id"`)
- `$id_alias` — the alias to your ID column in your query

### `IteratesSpreadsheet`

[](#iteratesspreadsheet)

- `$use_headings` — whether to treat the first row of each sheet as headings
- `$preserve_empty_rows` — whether empty rows should be included
- `$format_dates` — whether date columns should be formatted (typically you don't need this because Conveyor Belt automatically converts date cells to `Carbon` instances for you)
- `$filename` — the file to load (only set if this is not dynamic in any way, which is unusual)
- `$excel_temp_directory` — set if you need to customize where temp files are stored
- `$field_delimiter` — change this if you need to import non-standard CSV files (e.g. tab-delimited)
- `$field_enclosure` — change this if you need to import non-standard CSV files (that don't use the `"` character)
- `$spreadsheet_encoding` — change this if you're dealing with non-UTF-8 data
- `$heading_format` — Change this to any `Str::` function to change the format of your array keys (`"snake"` by default)

### `IteratesJson`

[](#iteratesjson)

- `$filename` — the file to load (only set if this is not dynamic in any way, which is unusual)
- `$json_endpoint` — the JSON endpoint to query for data (use `getJsonEndpoint` to set this dynamically)
- `$json_pointer` — use this to iterate over nested JSON data ([see spec](https://datatracker.ietf.org/doc/html/rfc6901))

Examples
--------

[](#examples)

### Database Example

[](#database-example)

```
class ProcessUnverifiedUsers extends Command
{
  use \Glhd\ConveyorBelt\IteratesIdQuery;

  // By setting $collect_exceptions to true, we tell Conveyor Belt to catch
  // and log exceptions for display, rather than aborting execution
  public $collect_exceptions = true;

  // First, set up the query for the data that your command will operate on.
  // In this example, we're querying for all users that haven't verified their emails.
  public function query()
  {
    return User::query()
      ->whereNull('email_verified_at')
      ->orderBy('id');
  }

  // Then, set up a handler for each row. Our example command is either going to
  // remind users to verify their email (if they signed up recently), or queue
  // a job to prune them from the database.
  public function handleRow(User $user)
  {
    // The `progressMessage()` method updates the progress bar in normal mode,
    // or prints the message in verbose/step mode
    $this->progressMessage("{$user->name} email}>");

    $days = $user->created_at->diffInDays(now());

    // The `progressSubMessage()` method adds additional context. If you're in
    // normal mode, this gets appended to the `progressMessage()`. In verbose or
    // step mode, this gets added as a list item below your `progressMessage()`
    $this->progressSubMessage('Registered '.$days.' '.Str::plural('day', $days).' ago…');

    // Sometimes our command trigger exceptions. Conveyor Belt makes it easy
    // to handle them and not have to lose all our progress
    ThirdParty::checkSomethingThatMayFail();

    if (1 === $days) {
      $this->progressSubmessage('Sending reminder');
      Mail::send(new EmailVerificationReminderMail($user));
    }

    if ($days >= 7) {
      $this->progressSubmessage('Queuing to be pruned');
      PruneUnverifiedUserJob::dispatch($user);
    }
  }
}
```

### File Example

[](#file-example)

```
class ProcessSignUpSheet extends Command
{
  use \Glhd\ConveyorBelt\IteratesSpreadsheet;

  // Conveyor Belt will automatically pick up a "filename" argument. If one
  // is missing you can set a $filename property or implement the getSpreadsheetFilename method
  protected $signature = 'process:sign-up-sheet {filename}';

  public function handleRow($item)
  {
    // $item is an object keyed by the spreadsheet headings in snake_case,
    // so for example, the following CSV:
    //
    // Full Name, Sign Up Date, Email
    // Chris Morrell, 2022-01-02, chris@mailinator.com
    //
    // Will result in a full_name, sign_up_date, and email property
    // on the $item object. You can change from snake case to any other
    // string helper format by setting $heading_format
  }
}
```

The `IteratesJson` trait works exactly the same as the `IteratesSpreadsheet` trait, just with different configuration options.

###  Health Score

53

—

FairBetter than 96% of packages

Maintenance73

Regular maintenance activity

Popularity48

Moderate usage in the ecosystem

Community14

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 97.8% 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 ~137 days

Recently: every ~282 days

Total

12

Last Release

148d ago

Major Versions

0.3.3 → 1.0.02023-02-17

1.0.0 → 2.0.02023-07-03

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/21592?v=4)[Chris Morrell](/maintainers/inxilpro)[@inxilpro](https://github.com/inxilpro)

---

Top Contributors

[![inxilpro](https://avatars.githubusercontent.com/u/21592?v=4)](https://github.com/inxilpro "inxilpro (87 commits)")[![Skullbock](https://avatars.githubusercontent.com/u/1104083?v=4)](https://github.com/Skullbock "Skullbock (1 commits)")[![skylerkatz](https://avatars.githubusercontent.com/u/7297992?v=4)](https://github.com/skylerkatz "skylerkatz (1 commits)")

---

Tags

laravel

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/glhd-conveyor-belt/health.svg)

```
[![Health](https://phpackages.com/badges/glhd-conveyor-belt/health.svg)](https://phpackages.com/packages/glhd-conveyor-belt)
```

###  Alternatives

[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

265.2k](/packages/aedart-athenaeum)[spatie/laravel-export

Create a static site bundle from a Laravel app

679153.2k7](/packages/spatie-laravel-export)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k31.8M162](/packages/laravel-cashier)[laravel/sail

Docker files for running a basic Laravel application.

1.9k212.4M1.5k](/packages/laravel-sail)[craftcms/cms

Craft CMS

3.6k3.7M3.4k](/packages/craftcms-cms)

PHPackages © 2026

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