https://github.com/whilesmartphp/eloquent-campaigns
Channel-agnostic campaign attribution for Laravel: owner-scoped campaigns, source-tagged events and conversions, per-source analytics.
https://github.com/whilesmartphp/eloquent-campaigns
Last synced: about 18 hours ago
JSON representation
Channel-agnostic campaign attribution for Laravel: owner-scoped campaigns, source-tagged events and conversions, per-source analytics.
- Host: GitHub
- URL: https://github.com/whilesmartphp/eloquent-campaigns
- Owner: whilesmartphp
- License: mit
- Created: 2026-06-27T16:53:48.000Z (about 1 month ago)
- Default Branch: dev
- Last Pushed: 2026-07-22T14:45:40.000Z (6 days ago)
- Last Synced: 2026-07-22T15:16:16.193Z (6 days ago)
- Language: PHP
- Size: 23.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# whilesmart/eloquent-campaigns
Channel-agnostic campaign attribution for Laravel. Group activity into owner-scoped **campaigns**, record **source-tagged events** (a creator's link click, an email open, an ad impression, a signup, a purchase), and report **per-source performance** including conversions and value.
A campaign is just a named, owner-scoped initiative. What feeds it is up to the host, so the same package powers:
- tracking which **creators** convert from their links,
- a **marketing campaign** on a business dashboard,
- an **email re-engagement** blast to inactive users.
Each campaign is scoped to an owner (workspace, organization, team, user) through [`whilesmart/eloquent-owner-access`](https://github.com/whilesmartphp/eloquent-owner-access).
## Install
```
composer require whilesmart/eloquent-campaigns
php artisan migrate
```
Routes register automatically under the `api` prefix with `auth:sanctum`. Set `CAMPAIGNS_REGISTER_ROUTES=false` to mount them yourself. Bind your own `OwnerAuthorizer` to enforce who can see which owner's campaigns (the default allows all authenticated requests).
## Owning model
```php
use Whilesmart\Campaigns\Traits\HasCampaigns;
class Workspace extends Model
{
use HasCampaigns;
}
$campaign = $workspace->campaigns()->create(['name' => 'Summer Creator Push']);
```
## Record events from any channel
```php
// A creator's link click (web)
$campaign->recordEvent('click', [
'source' => 'jane_creator',
'visitor_hash' => $signature,
'subject' => $link,
'metadata' => ['device' => 'mobile', 'country' => 'US'],
]);
// A conversion ($ value attributed back to the source that drove it)
$campaign->recordEvent('conversion', ['source' => 'jane_creator', 'value' => 49.99]);
// An email open / signup (no clicks at all)
$campaign->recordEvent('open', ['source' => 'inactive_30d']);
$campaign->recordEvent('signup', ['source' => 'inactive_30d']); // counts as a conversion
```
`type` is a plain string, so any channel can define its own events. Which types count as conversions is configurable (`conversion_types`).
`visitor_hash` is an **opaque unique-actor id you supply** (a hashed IP+UA for web traffic, a recipient id for email, a device id for push). The package never computes it, so it makes no assumption about the channel; it only uses it for unique counts and to match a conversion back to the actor's earlier event.
## Endpoints
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/campaigns` | List (filter by `is_active`, `q`, owner) |
| POST | `/api/campaigns` | Create |
| GET | `/api/campaigns/{campaign}` | Show |
| PUT/PATCH | `/api/campaigns/{campaign}` | Update |
| DELETE | `/api/campaigns/{campaign}` | Soft delete |
| GET | `/api/campaigns/{campaign}/analytics` | Per-source attribution report (`?from`, `?to`) |
| POST | `/api/campaigns/{campaign}/events` | Record an event |
## Analytics shape
```json
{
"total_events": 4, "engagements": 3, "unique_visitors": 3,
"total_conversions": 1, "conversion_rate": 33.3, "total_value": 49.99,
"by_source": [
{ "source": "jane", "events": 3, "engagements": 2, "visitors": 2, "conversions": 1, "value": 49.99, "conversion_rate": 50 }
],
"by_type": [{ "type": "click", "count": 3 }],
"by_day": [{ "date": "2026-06-27", "count": 4 }]
}
```
## License
MIT © WhileSmart LTD