Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Streak Bundle Laravel Package

streak/streak-bundle

A Symfony bundle that integrates the Streak CRM API client into your PHP application. It provides service container configuration for API credentials and convenient access to Streak email CRM features from controllers and services.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm what you actually get. With 2 GitHub stars and zero dependents, this is a low-traffic package — read the source before wiring it into anything user-facing.

composer require streak/streak-bundle

Because the package ships as a bundle, check first whether Laravel's package auto-discovery picks it up. In composer.json you want "extra": {"laravel": {"providers": [...]}}. If it's absent (common in older bundles), register the service provider manually in bootstrap/providers.php (Laravel 11+) or config/app.php (older versions), and add any facade alias you find under a Facades/ directory.

Where to look first, in this order:

  1. composer.json — confirms the Laravel version constraint and PSR-4 namespace. A streak/streak-bundle with a require: laravel/framework: ^5.* will not autoload cleanly on Laravel 11 without a shim.
  2. src/ for a service provider and any Config/ or Database/Migrations/ directories — these tell you what's publishable.
  3. README.md — for the one blessed entry point (facade vs. injected service vs. model trait). Pick that and ignore the rest until you understand it.

Publish assets:

php artisan vendor:publish --provider="Streak\StreakBundle\StreakServiceProvider" --tag=config
php artisan vendor:publish --provider="Streak\StreakBundle\StreakServiceProvider" --tag=migrations
php artisan migrate

First use case — the one that justifies the package. Streak tracking almost always means: given a user and a timestamp/date, record an activity and get back the current consecutive-day count. Start here, in a single tinker session or a throwaway command:

use Streak\StreakBundle\Facades\Streak;

Streak::record($user, now());          // first day  -> current: 1
Streak::record($user, now()->addDay()); // next day  -> current: 2
Streak::current($user);                 // => 2
Streak::longest($user);                 // => 2

If that runs, you understand 90% of the package. If it doesn't, the facade name or the method signature is off — grep public function in src/ and adjust.

Implementation Patterns

Trait-based model integration. If the bundle exposes a trait (look for UsesStreaks, HasStreaks, or similar under src/Concerns), prefer it over the facade. It typically gives you $user->streaks(), $user->currentStreak(), and relation access for eager loading:

class User extends Authenticatable
{
    use HasStreaks;
}

// Controller
public function dashboard()
{
    $user = auth()->user()->load('streaks'); // avoids N+1 on the streak relation
    return view('dashboard', [
        'current' => $user->currentStreak(),
        'longest' => $user->longestStreak(),
    ]);
}

Middleware for the "active day" pattern. The dominant real-world use is a streak that advances once per calendar day, not once per request. Record on the first authenticated request of the day and let every subsequent request be a no-op:

class RecordDailyStreak
{
    public function handle(Request $request, Closure $next)
    {
        if ($user = $request->user()) {
            Streak::recordOncePerDay($user, now()); // or guard with your own cache check
        }
        return $next($request);
    }
}

If the package doesn't ship an idempotent method, wrap it yourself: Cache::remember("streak:{$user->id}:".now()->toDateString(), 86400, fn () => Streak::record($user, now()));

Domain events as the source of truth. Don't scatter Streak::record() through controllers. Fire a single event and let one listener own the streak update — this keeps the "what counts as activity" rule in one place:

// app/Events/PostPublished.php  ->  EventServiceProvider
protected $listen = [
    PostPublished::class => [IncrementUserStreak::class],
];

class IncrementUserStreak
{
    public function handle(PostPublished $event): void
    {
        Streak::record($event->post->user, $event->post->created_at);
    }
}

Backfill and repair. Historical streaks are almost always wrong on first adoption. Write a one-off artisan command that iterates real activity rows (logins, posts, sessions) in chronological order and replays them through Streak::record(). Run it in chunks inside a transaction; you can drop it once the historical data is repaired.

Nightly reconciliation. Because the current streak decreases the moment a user misses a day, treat the stored value as derived state. A scheduled job that recomputes current for recently active users protects against drift, timezone bugs, and race conditions:

Schedule::call(fn () => Streak::reconcileRecent())->dailyAt('00:15');

Gotchas and Tips

Timezone is the number-one bug source. "Consecutive days" is meaningless without a timezone. Store the date the streak was recorded in UTC or in the user's timezone consistently — never mix. If the package uses now() internally, its config('streak.timezone') (or equivalent) must be set per-user if your app spans regions. Debugging tip: a user in Pacific/Auckland will appear to "miss" days if you record and compare in UTC.

The first day is a special case. Many naive implementations return 0 for a single recorded day rather than 1, or treat a NULL last_activity_at as "streak broken." Verify with an explicit test before shipping:

Carbon::setTestNow('2024-01-01 09:00:00');
Streak::record($user, now());
$this->assertSame(1, $user->fresh()->currentStreak());

Carbon test freezing is non-negotiable. Streak logic is entirely date arithmetic. Any test not using Carbon::setTestNow() is flaky. Freeze time, advance a day, assert, advance two days, assert the streak resets.

Concurrent writes cause double-counts. If two requests record activity at the same instant for the same user, a read-modify-write UPDATE will increment twice. Wrap the record operation in a transaction with lockForUpdate() on the streak row, or add a unique index on (user_id, activity_date) so the second insert fails harmlessly. Inspect the published migration — if the date column isn't part of a unique constraint, add one in your own migration.

Index your queries. current()/longest() are useless at scale without an index on (user_id, recorded_at) or (user_id, streak_date). The published migration may not include one; add it.

Migration conflicts. Because the bundle publishes migrations into database/migrations/, re-publishing after an update can create duplicate table names. Publish once, commit the migration, and diff future versions manually.

Config caching traps. config('streak.*') values are read at boot. If you're tempted to toggle streak behavior at runtime (e.g., per-tenant settings), don't do it through config — pass options to the recording call instead, since config:cache freezes those values.

Extension points are thin. With 2 stars, assume the package has no plugin interface or overridable repository. If you need custom behavior (weighted streaks, streak freezes like Duolingo, multiple streak types per user), you'll likely need to either (a) subclass the model the bundle points to in config, or (b) fork. Check config/streak.php for a models array — that's the cheapest extension seam. If it's absent, wrap the package in your own StreakService interface so you can swap the implementation later without touching call sites.

Debugging a wrong count. In order: (1) dd() the exact dates being stored, (2) confirm the timezone, (3) replay the activity log manually, (4) check for duplicate rows. Nine times out of ten it's (1) or (2).

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums