visiosoft/contact-plugin
A Laravel/PHP contact plugin from VisioSoft for adding contact functionality to your application. It offers a lightweight starting point for integrating contact forms and related features into Laravel or VisioSoft-based projects; refer to the package documentation for setup and usage details.
Reality check first: visiosoft/contact-plugin is not a general-purpose Laravel package — it is a Streams Platform addon (the PyroCMS-derived core that powers OpenClassify). "Installing it in Laravel" means "installing it in a Streams Platform app." If your app is vanilla Laravel, budget a day to either stand up Streams Platform alongside it or reimplement the ~200 lines this addon contains yourself.
Minimal path:
composer require visiosoft/contact-plugin
php artisan addon:install visiosoft.contact
php artisan migrate
Then look, in this order:
addons/default/visiosoft/contact-plugin/src/Contact/ — the Streams stream definitions. This is where the storage schema (name, email, message, timestamps) actually lives. There are no traditional Eloquent migrations to read; the "schema" is a Stream + Assignments graph.src/ContactModuleServiceProvider.php — registers the module, routes, and the admin control panel section.resources/views/ — the form partial and the admin listing. This is what you will override 90% of the time.src/Http/Controller/ — submission handling and any notification dispatch.First use case, the 10-minute one: drop the contact form onto a page and confirm submissions land in Control Panel → Contact. In your theme template:
{% include "visiosoft.module.contact::contact/form" %}
If submissions appear in the admin but no email arrives, the problem is your MAIL_* config — not the addon (see Gotchas).
Use the stream form builder, not hand-written HTML. The addon's value is that fields are admin-editable. Render via Streams forms so client-added fields appear automatically:
{{ form('contact').fields() }}
Hardcoding <input name="email"> will silently drop any field the client adds later.
Reading submissions in your own code. Entries are Streams entries, not plain models. Use the repository so you get the field-accessor plumbing:
use Visiosoft\ContactModule\Contact\Contract\ContactRepositoryInterface;
public function __construct(protected ContactRepositoryInterface $contacts) {}
$recent = $contacts->all(); // Collection of ContactInterface
$recent = $contacts->findBy('email', $email);
Do not ContactModel::where(...) directly unless you've checked the generated model — Streams models use a dynamic EntryModel contract and raw Eloquent scopes on custom fields will bite you.
Overriding views. Never edit the addon in addons/default/; changes vanish on composer update. Publish or override in the theme:
php artisan addon:publish visiosoft.contact
# or place files at resources/{theme}/streams/visiosoft/contact-module/...
Adding fields without code. New fields go through Admin → Streams → Contacts → Assignments. Everything downstream (form rendering, admin columns, validation from field rules) picks it up. This is the single biggest day-to-day win of the addon.
Replacing the submission flow. For anything non-trivial (CRM push, ticketing, Slack), don't fork — bind a decorator or listen for the entry-created event and handle it in your own listener:
Event::listen(\Streams\Core\Entry\Event\EntryWasSaved::class, function ($event) {
if ($event->entry->stream()->id === 'contacts') {
// dispatch job, notify Slack, etc.
}
});
Notification customization. Keep mail out of the request cycle: Mail::to(...)->queue(new ContactReceived($entry)) and set QUEUE_CONNECTION to database or Redis. The addon's default sync send will hang your form on a slow SMTP host.
Testing. Fake mail and isolate the stream:
Mail::fake();
$this->post(route('visiosoft.module.contact::contact.create'), [
'name' => 'A', 'email' => '[email protected]', 'message' => 'hi',
])->assertRedirect();
Mail::assertQueued(ContactReceived::class);
Wrap in RefreshDatabase — Streams migrations create real tables (data_contact_contacts style) and transaction rollback of stream writes is unreliable.
"visiosoft/contact-plugin": "~1.0" and commit composer.lock; an unpinned transitive Streams bump will break field assignment silently.migrate:fresh on a dev box you get a different schema than production. Always replay from a known snapshot before assuming a bug.php artisan cache:clear, then php artisan streams:compile if you have it, and clear the theme's compiled views. "My new field doesn't show" is almost always this.g-recaptcha into an overridden addon view, use your own controller action so it survives updates.storage/logs/laravel.log and MAIL_MAILER=log before touching addon code. Also confirm the recipient address — control-panel notification settings override .env.visiosoft.module.contact::…; a stray .blade.php extension or a contact-module vs contact-plugin mixup in the override directory means your file is silently ignored with zero errors.data_* tables are managed by the Streams migrator. Manual ALTER TABLE is reverted on the next compile and can corrupt the assignment cache.site — a query that "loses" records is usually a missing site scope.MAIL_MAILER=log gets you through 95% of "form doesn't work" reports. If a submission 500s, look at EntryModel save — validation rules defined in the field assignments are the usual culprit (a required field added by the client, not present in their form).ContactRepositoryInterface → view override → fork. Forking is the last resort, but given the package's age and zero dependents, treat the source as reference implementation you can own rather than a dependency you can rely on upstream to maintain.How can I help you explore Laravel packages today?