NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

Balík je predvolene bezstavový, no využije OnboardingStore (RoundlyConsulting\Onboarding\Contracts\OnboardingStore), ktorý mu poskytne aplikácia. Neprináša žiadnu tabuľku, model ani migráciu a bez store nerobí nič. Úložisko patrí vašej aplikácii: dokončenia (zvyčajne cez listener na StepCompleted) aj skrytia krokov, ktoré balík odovzdá do markDismissed().

namespace RoundlyConsulting\Onboarding\Contracts;

interface OnboardingStore
{
    public function isDismissed(Authenticatable|Model|null $subject, string $stepKey): bool;

    public function completedAt(Authenticatable|Model|null $subject, string $stepKey): ?DateTimeInterface;

    public function markDismissed(Authenticatable|Model|null $subject, string $stepKey): void;
}

Čo store umožní

  • Skryteľné voliteľné kroky — voliteľný krok s dismissible(), ktorý subjekt skryl, zmizne zo steps() aj z percent. Povinné kroky sa skryť nedajú.
  • Jednorazové udalosti — record() preskočí každý krok, ktorého completedAt() nie je null, takže StepCompleted sa pre krok odošle naprieč požiadavkami len raz.

Implementácia store

Kontrakt môžete oprieť o ľubovoľné úložisko. Tento príklad používa tabuľku, ktorú vlastní vaša aplikácia:

namespace App\Onboarding;

use DateTimeInterface;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use RoundlyConsulting\Onboarding\Contracts\OnboardingStore;

// Your own table — the package ships none.
final class DatabaseOnboardingStore implements OnboardingStore
{
    public function isDismissed(Authenticatable|Model|null $subject, string $stepKey): bool
    {
        return $this->row($subject, $stepKey)?->dismissed_at !== null;
    }

    public function completedAt(Authenticatable|Model|null $subject, string $stepKey): ?DateTimeInterface
    {
        $at = $this->row($subject, $stepKey)?->completed_at;

        return $at === null ? null : Carbon::parse($at);
    }

    // Called by the manager when the subject dismisses an optional, dismissible step.
    public function markDismissed(Authenticatable|Model|null $subject, string $stepKey): void
    {
        if ($subject === null) {
            return;
        }

        DB::table('onboarding_steps')->updateOrInsert(
            ['user_id' => $subject->getKey(), 'step' => $stepKey],
            ['dismissed_at' => now()],
        );
    }

    private function row(Authenticatable|Model|null $subject, string $stepKey): ?object
    {
        if ($subject === null) {
            return null;
        }

        return DB::table('onboarding_steps')
            ->where('user_id', $subject->getKey())
            ->where('step', $stepKey)
            ->first();
    }
}

Zapojenie

Store odovzdajte do Onboarding::useStore() — triedu vytvorí kontajner pri prvom použití a trieda, ktorá kontrakt neimplementuje, vyhodí InvalidStoreException:

// app/Providers/AppServiceProvider.php — boot()
use App\Onboarding\DatabaseOnboardingStore;
use RoundlyConsulting\Onboarding\Facades\Onboarding;

Onboarding::useStore(DatabaseOnboardingStore::class);   // resolved through the container on first use
Onboarding::useStore(new DatabaseOnboardingStore);      // or an instance

Onboarding::hasStore();   // true
Onboarding::store();      // ?OnboardingStore — the store in use

Bez useStore() sa použije store naviazaný v kontajneri:

// the fallback when useStore() was never called — app/Providers/AppServiceProvider.php, register()
use App\Onboarding\DatabaseOnboardingStore;
use RoundlyConsulting\Onboarding\Contracts\OnboardingStore;

$this->app->bind(OnboardingStore::class, DatabaseOnboardingStore::class);

Ukladanie dokončení

Počúvajte StepCompleted a zapíšte čas dokončenia — ďalšie record() potom tento krok preskočí:

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Onboarding\Events\StepCompleted;

Event::listen(StepCompleted::class, function (StepCompleted $e): void {
    if ($e->for === null) {
        return;
    }

    DB::table('onboarding_steps')->updateOrInsert(
        ['user_id' => $e->for->getKey(), 'step' => $e->step->stepKey()],
        ['completed_at' => now()],
    );
});

Skrytie kroku

$flow = Onboarding::flow('default')->title('Profile Onboarding');

$flow->add('Add a bio')->key('add-bio')->optional()->dismissible()
    ->completeWhenFilled('bio');

// e.g. from a "Not now" button — all three go through the manager to markDismissed()
Onboarding::for($user)?->dismiss('add-bio');   // returns the Flow
$user->dismissOnboardingStep('add-bio');       // the trait shorthand
$user->onboarding()?->dismiss('add-bio');

Každé skrytie ide cez manager, ktorý zavolá markDismissed() v store. Pôsobí len na viditeľný krok označený dismissible() a bez store, pri neznámom či neskryteľnom kroku nerobí nič.

FlowCompleted sa nepotláča — record() ju odošle vždy, keď je tok dokončený, a record($key) vždy, keď ohlásil daný povinný krok. Listener, ktorý má „oslavovať“ len raz, preto navrhnite idempotentne.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.