maize-tech/laravel-nps
Add Net Promoter Score surveys to your Laravel app. Create NPS surveys, collect and store responses, and calculate your score with configurable models, routes, and views, plus customizable migrations for quick setup.
Install the package and publish its assets:
composer require maize-tech/laravel-nps:^2.2
php artisan vendor:publish --provider="Maize\Nps\NpsServiceProvider"
php artisan migrate
Publish config, migrations, and (optionally) views using the tags documented in the README; then wire the package into your user model:
use Maize\Nps\Traits\HasNps;
class User extends Authenticatable
{
use HasNps;
}
Point config/nps.php at your user model (nps.models.user) and adjust the survey question, scale, route prefix/middleware, and the command timings (delay, expire, frequency).
Look at these first, in order:
config/nps.php — every behavioral knob lives here.src/Console/Commands/ — the nps:send command decides who gets a survey and when.src/Models/Survey.php and SurveyAnswer.php — the data contract and route key.routes/ and resources/views/ — what the recipient actually sees.HasNps trait — the relationship and helpers exposed on your user model.First real use case: schedule the send command and let the package do the rest.
// routes/console.php (Laravel 11) or app/Console/Kernel.php
Schedule::command('nps:send')->daily();
Eligible users then get a survey record with a unique link; their submitted score and comment land in the survey-answer table, ready for reporting.
Command-driven lifecycle. The package is not "always on." Surveys are created by running nps:send, which skips users who are too new (delay), already have an open survey (expire), or were surveyed too recently (frequency). Run it daily and treat those three config values as your product policy, not as infrastructure settings.
Custom models. Extend the package Survey/SurveyAnswer models when you need extra columns or casts, then swap the class names in config/nps.php. Keep the parent casts, route key, and table names intact.
Custom delivery channel. The package persists the survey; how it reaches the user is your call. Listen for the survey-created event (or override the mailable/notification in config) to send via your own notification, Slack, in-app banner, etc. This is the cleanest seam for multi-channel delivery without forking the package.
Custom UI. Publish the views and restyle them, or point the route at your own controller/view. Keep the form posting to the package endpoint, or replicate the answer-creation logic (score, comment, answered_at) so reporting stays consistent.
Reporting. NPS is computed from answers, not stored: promoters are 9–10, passives 7–8, detractors 0–6, and NPS = %promoters − %detractors. Build a small query/action around SurveyAnswer and scope it by date range and by the morph relation to your user model. Cache or snapshot the score per period if you display it on a dashboard.
Multi-tenant / segmented surveys. Because the survey belongs to the user via a morph relation, segment by adding a whereHas/global scope on your user model inside a custom command that wraps the package logic—or run nps:send per tenant with a scoped query.
Testing. Use RefreshDatabase, Carbon::setTestNow() to control the delay/frequency windows, and assert on survey records rather than on emails. For HTTP tests, act as the user and hit the generated survey URL (pull it from the model) rather than hand-building the route.
^2.2; if you previously pinned --ignore-platform-reqs or a fork to install on L11, drop that workaround and upgrade properly. Re-publish migrations only if the package's stubs changed—compare before overwriting.schedule:run cron, or schedule:work locally). Check php artisan schedule:list before debugging package code.config/nps.php requires php artisan config:cache (or a cache clear) in cached environments.HasNps adds relationships/helpers to your User; if you already have an nps method or relationship, alias the trait method or rename yours.Relation::enforceMorphMap(), register the survey/user morph aliases or the polymorphic relation will fail to resolve.id leaks volume and enables enumeration of other users' surveys.delay, expire, and frequency are evaluated against timestamps; schedule the command in the same timezone you reason about in config, and be consistent with app.timezone.How can I help you explore Laravel packages today?