Perzistencia
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 useBez 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 kryptomienOdoslaní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.