sylius/contact-bundle
Sylius Contact Bundle adds a configurable contact form to your Sylius storefront and lets administrators review and manage submitted contact requests from the admin panel, integrating with Sylius resources, channels, and email notifications.
Reality check first: this is not a Laravel package. sylius/contact-bundle is a Symfony bundle for Sylius, the Symfony-based e-commerce platform. There is no service provider, config file, facade, migration stub, or Blade view to publish. composer require sylius/contact-bundle in a Laravel app does nothing useful — you will pull in Symfony/Doctrine dependencies that fight with your Laravel stack and gain zero usable code paths.
So your "getting started" depends on which side of the fence you are on:
If you are on Sylius (the only place this runs natively):
ContactRequest before adding anything — installing the standalone bundle into a version that already has it produces duplicate service IDs and conflicting routes.composer require sylius/contact-bundle, register it in config/bundles.php, and import its routing from Resources/config/routing.yml.sylius_contact_request and sylius_contact_topic tables exist via Doctrine migrations or bin/console doctrine:schema:validate.src/Model/ContactRequest.php, src/Model/ContactTopic.php — the domainsrc/Form/Type/ — the storefront form and its validationsrc/Controller/ — storefront submit + admin actionsResources/config/ — routing, services, grid definitionsResources/translations/ — copy and email templates (this is the configuration surface)ContactRequest row is created, open it in the admin contact grid, and transition it to handled.If you are on Laravel (the likely case):
Do not install. Treat the repository as a reference design for a ~200-line feature, then build it natively. Clone it to a scratch directory, read src/Model/ and src/Form/Type/, and treat the two entities plus one form as a spec. Your "getting started" is one afternoon of scaffolding:
ContactTopic and ContactRequest Eloquent models (with the belongsTo relation and a status enum)StoreContactRequest form request with validationMailable triggered from a model eventThat is the whole bundle, reimplemented idiomatically.
Inside Sylius — the override layers, in order of preference:
sylius_resource config, not autodiscovery. To change the model, form, controller, or repository, override the resource entry — never edit the class in place. This is the primary customization seam and the only one that survives upgrades cleanly.templates/bundles/SyliusContactBundle/.... This is how you restyle or restructure the storefront form.AbstractTypeExtension) to add or hide fields without forking the form.Modeling the workflow:
ContactTopic is a resource, meaning extra topics (sales, support, legal) are database rows, not code. Seed them in fixtures or a migration rather than hardcoding an enum. If you need per-topic routing, attach metadata to the topic row and resolve the recipient in an event listener.Resources/config/. Add columns, filters, and bulk actions in the grid config. Status transitions (new → handled) live on the resource workflow, not in the controller — resist the urge to set status in a controller action.sylius.contact_request.pre_create / post_create resource events via a subscriber. Do not inject mailer calls into the controller; the bundle is thin here and you want to swap transport logic later without touching vendor code.Porting the pattern to Laravel (the recommended path):
// app/Models/ContactRequest.php
class ContactRequest extends Model {
protected $fillable = ['contact_topic_id','name','email','message','status'];
protected function casts(): array {
return ['status' => ContactStatus::class];
}
public function topic(): BelongsTo { return $this->belongsTo(ContactTopic::class); }
}
// app/Http/Requests/StoreContactRequest.php
public function rules(): array {
return [
'contact_topic_id' => ['required','exists:contact_topics,id'],
'name' => ['required','string','max:255'],
'email' => ['required','email'],
'message' => ['required','string','max:5000'],
];
}
// Controller: throttle + honeypot, persist, dispatch event
Route::post('/contact', StoreContactController::class)
->middleware('throttle:5,1');
Then attach a listener to ContactRequest::created that queues a NewContactRequest mailable to the recipient resolved from the topic, and a Filament resource for admin triage with a status transition. That reproduces 100% of the bundle's behavior with native primitives — queues, rate limiting, and validation are all things Laravel already gives you.
symfony/* and doctrine/* constraints. Plan to pin the platform or expect resolution failures on the next bump.ContactRequest before installing — many deployments already have it via core, and installing the standalone bundle produces conflicting service IDs and duplicate routes. This is the most common silent failure with this package.vendor/ or copying controllers wholesale breaks on every update. If a class is not exposed as a sylius_resource or a sylius.* parameter, decorate the service instead.bin/console cache:clear. Stale router caches produce confusing 404s on the contact page that look like a missing route when it is actually cached.bin/console debug:container sylius.contact — confirm servicesbin/console debug:router | grep contact — confirm routesbin/console debug:event-dispatcher — confirm your subscriber is registered on sylius.contact_request.*throttle:5,1 middleware.How can I help you explore Laravel packages today?