---
title: "Artisan commands — Events for Laravel | Roundly"
description: "events:install, events:doctor and its checks, events:sweep, events:repair and events:geocode — plus the scheduler lines and the PHP equivalents."
url: https://roundly-consulting.com/open-source/docs/events-for-laravel/commands
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Events for Laravel](https://roundly-consulting.com/open-source/docs/events-for-laravel.md)

# Artisan commands

| Command | Does |
| --- | --- |
| `events:install` | Refuses while a configured table exists (naming the events.tables.\* key); publishes the config and the migrations, prints the key types side by side and the next steps. |
| `events:doctor {--counts} {--json}` | Checks the installation; exits non-zero on an error, warnings alone exit 0. |
| `events:sweep {--limit=} {--chunk=100}` | Expires lapsed holds, moves waitlists up, sends due reminders — schedule every minute. |
| `events:repair {--counts} {--orphans} {--dry-run}` | Recomputes counters / releases orphaned RSVPs — both without a flag. |
| `events:geocode {--missing} {--venue=} {--force} {--chunk=100}` | Geocodes venues from their addresses. |

The provider registers the commands but puts nothing on the scheduler:

```php
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('events:sweep')->everyMinute();      // held seats expire, waitlists move up, reminders go out
Schedule::command('lifecycle:sweep')->everyMinute();   // finished events complete at their end
```

## events:install

- Exits 1 while a configured table already exists, naming the events.tables.\* key to change.
- Publishes events-config, lifecycle-migrations, tags-migrations, media-migrations and events-migrations (plus sluggable-migrations with slug history on); files you already published are kept.
- Prints events.primary\_key\_type, lifecycle.key\_type, tags.key\_type, events.key\_type and lifecycle.actor\_key\_type side by side, then the next steps: migrate, the model traits, the two scheduler lines, the payment collector and events:doctor.

## events:doctor

```bash
php artisan events:doctor
php artisan events:doctor --counts     # also compare every event's counters with a recount
php artisan events:doctor --json       # [{severity, check, message}]
```

Exits non-zero when an error is found; warnings alone exit 0. Each finding prints as \[check\] message (or “Everything looks right.”), and --json prints \[{severity, check, message}\]:

| Check | Severity | Finds |
| --- | --- | --- |
| `key_types` | error | lifecycle.key\_type / tags.key\_type ≠ events.primary\_key\_type; lifecycle.actor\_key\_type ≠ events.key\_type; organizer-scoped labels with tags.key\_type ≠ events.key\_type; slug history with sluggable.key\_type ≠ events.primary\_key\_type. |
| `lifecycle` | error | The configured lifecycle breaks the contract (initial state not a draft, a state without a phase, it does not compile). |
| `tag_types` | error / warning | The tag types are missing, or an override allows more than one category · an override makes the category not curated, global or hierarchical, labels not private, or either type a free-form tree. |
| `tables` | error | A configured table is missing; slug history on without slug\_history. |
| `distance` | error | The near-me distance query fails on the default connection. |
| `policy, venue_policy, resources, payments` | error | A configured class is missing or not an implementation; payments also a hold that is not a positive duration. |
| `reminders` | error | Offsets outside PT5M–P31D, more than five, or not durations; an unreadable max\_lateness. |
| `payments` | warning | Paid ticket types, the null collector and no RsvpHeld listener. |
| `urls · routes` | warning | events.urls.event is blank · the API is on with an empty auth\_middleware. |
| `notifications` | warning | The database channel without a notifications table; attendee types that cannot be notified. |
| `timezones · organizers · postponed` | warning | Events in a timezone other than their venue’s · a stored organizer\_type not in events.organizers · postponed for more than 90 days. |
| `counters` | warning | With --counts: counters that drifted from a recount. |

## events:sweep

```bash
php artisan events:sweep                  # expire lapsed holds, move waitlists up, send due reminders
php artisan events:sweep --limit=500      # at most this many events per part (holds, reminders)
php artisan events:sweep --chunk=100      # events per batch
```

Holds expire per event under its lock, trashed events included; promotions follow; reminders are claimed once — only for scheduled or rescheduled events starting within the next 32 days. --limit caps the events of each part (holds, reminders). The command prints the four counts.

## events:repair

```bash
php artisan events:repair                 # both: counters and orphaned RSVPs
php artisan events:repair --counts        # recompute every event's counters under its row lock
php artisan events:repair --orphans       # release the RSVPs of attendees whose row is gone
php artisan events:repair --dry-run       # report without writing
```

- \--counts recomputes every event’s counters under its row lock — a drift means a bug or a hand-made write.
- \--orphans releases the RSVPs of attendees whose row is gone — a mass delete fires no model events: seats returned, waitlist promoted, rows removed, one query per attendee type.
- \--dry-run reports without writing. The command prints the three counts, marked as a dry run when nothing was written.

## events:geocode

```bash
php artisan events:geocode                 # venues without coordinates (--missing, the default)
php artisan events:geocode --venue=7       # one venue (skipped when it has coordinates, unless --force)
php artisan events:geocode --force         # every venue again
php artisan events:geocode --chunk=200     # venues per batch (default 100)
```

Venues are read in id order, --chunk at a time; a failing venue is counted as not found and warned about, never fatal. It prints Geocoded and Not found and exits 0.

## From PHP

```php
use RoundlyConsulting\Events\DataTransferObjects\RepairOptions;
use RoundlyConsulting\Events\DataTransferObjects\SweepOptions;

$swept = Events::sweep(new SweepOptions(limit: null, chunk: 100));
// SweepResult {events, expiredHolds, promoted, reminders, notified}

$report = Events::repair(new RepairOptions(counts: true, orphans: true, dryRun: false, chunk: 100));
// RepairReport {eventsChecked, countersFixed, orphansReleased, dryRun}
```

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/events-for-laravel.md)
