sst/survey-lib-bundle
SST Survey Lib Bundle is a PHP/Symfony package that brings reusable survey components into your application, offering structures for surveys, questions, and responses so teams can add survey creation and management features without building the core survey logic from scratch.
Reality check before anything else: this is a Symfony bundle, not a Laravel package. There is no SurveyServiceProvider, no config/survey.php, no facade. If you composer require sst/survey-lib-bundle in a Laravel app, nothing will auto-wire — Laravel does not read Resources/config/services.yaml or *Bundle::class registrations. Plan for one of two paths:
src/ into your own namespace, dropping the Symfony plumbing (DI extensions, Twig, Form types).Most Laravel teams will end up doing (2), so start by auditing how coupled the core is to Symfony.
Minimal steps (evaluation spike, ~1 hour):
git clone https://github.com/sst-software/SurveyLib
cd SurveyLib
cat composer.json # PHP + symfony/* version constraints — read these first
ls src/ # find the Bundle class and the Entity/Model dir
cat LICENSE # license shows as NOASSERTION on Packagist — verify by hand
Read in this order:
composer.json — PHP constraint and the Symfony component versions. This decides whether it can coexist with Laravel's already-installed Symfony components (console, http-foundation, finder, process, var-dumper).*Bundle.php class — see what it registers and which extensions it loads.DependencyInjection/ — the service definitions tell you what the public API actually is.Entity/ or Model/ — the survey/question/response schema. This is what you actually want.Resources/views/ and Form/ — if present, these are Symfony/Twig-bound and will be thrown away.First use case to validate: define a survey with two or three question types, persist it, render it, record a response. If you can do that without booting a Symfony kernel, the domain layer is portable and worth continuing with. If it needs the container, budget for the sidecar approach instead.
Adapter over the domain, not the bundle. Wrap the library's survey objects in your own repository so Eloquent never leaks into library code and vice versa:
final class SurveyRepository
{
public function find(int $id): ?Survey
{
$row = DB::table('surveys')->find($id);
return $row ? SurveyHydrator::fromRow($row) : null; // your mapper
}
}
Keep the mapping in one class. When the package goes unmaintained — and at 8 stars with 0 dependents, assume it might — you have a single seam to replace.
Treat it as a definition/validation engine, render yourself. The highest-leverage integration is headless: use the library to model questions, validate answers, and compute scores; let Blade/Inertia/Livewire handle presentation. That side-steps Twig and Symfony Forms entirely, which are the least portable parts.
Question types via a registry. Survey libraries usually implement question polymorphism with a discriminator column plus a handler per type. In Laravel, mirror that with a container-tagged registry:
// AppServiceProvider
$this->app->tag([TextQuestion::class, ChoiceQuestion::class], 'survey.questions');
Then resolve the handler from the question's type string rather than switching on it inline. This keeps your added question types out of the vendor package.
Multi-step flows: store partial responses, not session state. Persist an incomplete response row with a completed_at of null, keyed by a UUID in the URL. It survives browser restarts, works across devices, and makes the "resume survey" feature free. Session-backed drafts are the usual first implementation and the usual first bug report.
Schema ownership: one ORM, one migration path. If you keep Doctrine entities for the survey side and Eloquent for the app side, you now own two migration systems and two connection configurations. Prefer a plain mapper (as above) or port the entities to Eloquent models so a single php artisan migrate describes the whole database.
Integration tips:
/api/surveys/{id}) so the frontend and any mobile client share one contract.SurveyCompleted) from your adapter, not from inside the package. You cannot add Laravel events to vendor code cleanly.License is the first blocker. Packagist reports NOASSERTION, meaning the LICENSE file is unrecognized or ambiguous. Open it and read it literally before you write a line of integration code. "No clear license" defaults to all-rights-reserved in most legal readings — a real problem for anything shipped to customers. Escalate this early; it can end the evaluation on its own.
Composer conflicts are likely. Laravel already pins symfony/console, symfony/http-foundation, symfony/finder, symfony/process, symfony/error-handler, symfony/var-dumper. An old bundle will often require symfony/*: ^4.4 || ^5.0, which cannot resolve against a Laravel 11 install. Try the install in a throwaway directory first:
composer require sst/survey-lib-bundle --dry-run -W
If it fails, you are back to cloning and copying src/ — which also conveniently removes the license-as-dependency question, though not the license-as-copyright question.
Abandonment risk is the dominant cost. 8 stars, 0 dependents, a 0.23 score. Assume no security patches, no PHP 8.x fixes, no Symfony 7 support. Pin the commit in composer.json if you vendor it, and keep your adapter thin enough that replacing it is a week, not a quarter.
Debugging without the Symfony profiler. Inside a Laravel app the Symfony debug toolbar, bin/console debug:container, and the bundle's config dump are all unavailable. When something misbehaves: log the resolved service graph yourself, dd() at the boundary between your adapter and the library, and check whether the library is silently reading $_ENV or a parameters.yaml key that does not exist in your app. Config that "works" in the demo app often defaults silently rather than throwing.
Config quirks to expect. Symfony bundle config is normally injected via a Configuration tree in DependencyInjection/Configuration.php. In a Laravel context that tree is never evaluated, so any behavior it controls falls back to hardcoded defaults in the service definitions. Read that file and hardcode the values you want explicitly — do not assume the defaults are sensible.
Extension points, in order of preference:
decorates:) — only viable in the sidecar setup.$this->app->bind(...)) — works if the library resolves collaborators through a container abstraction, which is uncommon.PATCHES.md so the next developer knows why the vendored copy differs from upstream.Do not let Twig templates into your repo. If the bundle ships Resources/views, resist the urge to "just make Twig work in Laravel." View helpers, form themes, and translation domains will drag in the whole Symfony stack one composer require at a time. Rebuild the two or three templates you actually need in Blade.
How can I help you explore Laravel packages today?