# Events and Campaigns
Krayin's Marketing package adds event-driven email campaigns โ create an event with a date, attach an email template to it via a campaign, and Krayin queues one email per contact on the day the event fires.
This page covers the admin workflow, the command that processes campaigns, and the cron + queue setup required to keep it ticking.
Not the same as listener events
The "events" here are marketing events โ admin-created records with a date, used to schedule email blasts. They are unrelated to the Laravel listener events documented in Events Listeners.
# ๐งฉ What you can build
| Feature | Detail |
|---|---|
| Date-bound campaigns | Each campaign is tied to an event with a date. Krayin only processes it on (or before) that date. |
| Template-driven | Campaign body comes from an admin-managed email template โ no code changes to tweak copy. |
| Bulk send to all persons | Every email address on every Person record gets the email. |
| Queued delivery | Emails go through Laravel's queue, so a large send doesn't block the request. |
| Daily auto-run | Scheduled command picks up eligible campaigns once per day. |
# ๐ Workflow overview
- Create an email template.
- Create an event (give it a date).
- Create a campaign that links the event and the template.
- Make sure the scheduler and queue worker are running.
- On the event's date, the daily job queues an email to every person.
# ๐๏ธ 1. Create an email template
In the admin go to Settings โ Email Templates, then Create.
| Field | Purpose |
|---|---|
| Name | Internal label for the template. |
| Subject | Email subject line. |
| Content | The email body โ HTML supported. |
Save. The template is now selectable from the campaign form.
# ๐ 2. Create an event
In the admin go to Settings โ Events, then Create.
| Field | Purpose |
|---|---|
| Name | Label shown when picking the event on a campaign. |
| Date | The day the event "fires". Campaigns linked to this event are processed on this date. |
| Description | Optional note for context. |
Date semantics
The processor compares event.date against today. A campaign is eligible when the event date is today or NULL. Past-dated events are skipped.
# ๐ง 3. Create a campaign
In the admin go to Settings โ Campaigns, then Create.
| Field | Purpose |
|---|---|
| Name | Internal label. |
| Subject | Subject of the outgoing email (overrides the template subject). |
| Event | The event whose date triggers the send. |
| Email Template | The template to render as the email body. |
| Status | Active (1) campaigns run; anything else is ignored. |
Save. From this moment, the campaign is eligible โ the daily job will pick it up when the event date matches today.
# โ๏ธ 4. How processing works
The Webkul\Marketing\Console\Commands\CampaignCommand console command drives the send:
php artisan campaign:process
The command delegates to Webkul\Marketing\Helpers\Campaign::process(), which:
Joins
marketing_campaignswithmarketing_eventsandemail_templates.Filters to campaigns where
status = 1andevent.dateis today (or NULL).Pulls every email address from every Person record.
Queues one
CampaignMailjob per address:Mail::queue(new CampaignMail($email, $campaign));
The helper builds the contact list with:
return $this->personRepository->pluck('emails')
->flatMap(fn ($emails) => collect($emails)->pluck('value'))
->all();
# Daily auto-run
MarketingServiceProvider registers the command on Laravel's scheduler:
public function boot(): void
{
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('campaign:process')->daily();
});
}
So once the scheduler cron is set up (next section), nothing else is required โ the command fires every day at midnight.
# ๐ ๏ธ 5. Wire up cron + queue
Two background pieces have to be running for campaigns to actually go out.
# Scheduler cron
Add one entry to your server's crontab so Laravel's scheduler fires every minute (it then decides which scheduled commands are due):
* * * * * php /path/to/krayin/artisan schedule:run >> /dev/null 2>&1
Edit the crontab with:
crontab -e
Replace /path/to/krayin with the absolute path to your Krayin install.
# Queue worker
Mail::queue(...) writes jobs to the queue โ a worker has to actually run them.
For local development:
php artisan queue:work
For production, run it under Supervisor so it restarts on crash. See the Data Transfer queue setup for a working Supervisor config โ the same approach applies here.
Set a real queue driver in .env:
QUEUE_CONNECTION=database
or
QUEUE_CONNECTION=redis
Don't use the sync driver
With QUEUE_CONNECTION=sync, every queued email runs inside the campaign:process command itself โ a campaign with thousands of recipients will time out. Use database or redis so the worker handles them in the background.
# ๐งช Verify
Run the command manually to make sure the wiring works:
php artisan campaign:process
Then check the queue:
php artisan queue:work --once
tail -f storage/logs/laravel.log
You should see one queued CampaignMail job per person email, and the worker log them as processed. If nothing happens, the most common causes are:
- The campaign's event date isn't today (use today's date for testing).
- The campaign's status is not
1. MAIL_MAILERin.envisn't pointing at a working mailer.- No queue worker is running.
# ๐ Next steps
- Events Listeners โ hook into the lifecycle events Krayin fires (different concept โ programmatic events, not marketing campaigns).
- Data Transfer โ bulk-import the Person records that campaigns will email.
โ Events Listeners Helpers โ