# Contact
IMPORTANT
- Since version 9.35.x, Laravel uses a new syntax for sending emails (opens new window)
- If you're using an older version, first update to the latest version with
composer update
- We'll use the integrated MailHog service to send (fake) mails in a development and testing environments
- You can access the MailHog mailbox in the browser at
http://localhost:8025
(or click on the @-icon in the footer menu)
- The contact page contains some static content and a (dynamic) contact form
- In web.php, we keep the route as a view route to resources/views/contact.blade.php and embed the Livewire component in that view
# Preparation
# Create a ContactForm component
- Create a new Livewire ContactForm component with
php artisan make:livewire ContactForm
- app/Http/Livewire/Admin/ContactForm.php (the component class)
- resources/views/livewire/admin/contact-form.blade.php (the component view)
- Refactor the contact.blade.php view and embed the ContactForm component in it
contact.blade.php
livewire/contact-form.blade.php
result
- The page contains some static content and embeds (on line 8) the ContactForm component
<x-vinylshop-layout> <x-slot name="description">Contact info</x-slot> <x-slot name="title">Contact info</x-slot> <div class="grid grid-cols-4 gap-4"> <x-tmk.section class="col-span-4 lg:col-span-3 lg:order-2"> {{-- embed the Livewire ContactForm component --}} @livewire('contact-form') </x-tmk.section> <section class="col-span-4 lg:col-span-1 lg:order-1"> <h3>The Vinyl Shop</h3> <p>Kleinhoefstraat 4</p> <p class="pb-2 border-b">2440 Geel - Belgium</p> <p class="flex items-center pt-2 cursor-pointer"> <x-phosphor-phone-call class="w-6 mr-2 text-gray-400"/> <a href="tel:+3214562310" class="mr-2">+32(0)14/56.23.10</a> </p> <p class="flex items-center pt-2 cursor-pointer"> <x-heroicon-o-mail-open class="w-6 mr-2 text-gray-400"/> <a href="mailto:info@thevinylshop.com">info@thevinylshop.com</a> </p> </section> </div> </x-vinylshop-layout>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Basic scaffolding
- Open app/Http/Livewire/ContactForm.php and resources/views/livewire/contact-form.php
Livewire/ContactForm.php
livewire/contact-form.blade.php
- Line 4 - 6: the form has three input fields (
name
,email
andmessage
) so we need three public properties in the component - Line 12 - 15: the
updated()
method will be called every time a property is updated (so we can use it to validate the input in real time) - Line 18 -21: the form has a submit button, so we need a method for this
sendEmail()
- This method contains the validation and the sending of the email
class ContactForm extends Component { // public properties public $name; public $email; public $message; // validation rules protected $rules = []; // real-time validation public function updated($propertyName, $propertyValue) { } // send email public function sendEmail() { } public function render() { return view('livewire.contact-form'); } }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Real-time form validation
- We always need to validate the form input fields before we can send the email
- Livewire has multiple built-in validation systems, (opens new window) and this time we will use the real-time validation
Livewire/ContactForm.php
result with validation errors
result with success message
- Line 7 - 9: define the validation rules in the protected
$rules
array- The
name
field is required and must be at least 2 characters long - The
email
field is required and must be a valid email address - The
message
field is required and must be at least 10 characters long
- The
- Line 15: the
updated()
hook checks each updated input field individually against his validation rules - Line 22: you cannot pass this line until the validation is successful
- Line 27 - 31: show a success message when the email is sent (send the actual mail will be covered soon)
- Line 34: reset all public properties to their initial state
class ContactForm extends Component { ... // validation rules protected $rules = [ 'name' => 'required|min:2', 'email' => 'required|email', 'message' => 'required|min:10', ]; // real-time validation public function updated($propertyName, $propertyValue) { $this->validateOnly($propertyName); } // send email public function sendEmail() { // validate the whole request before sending the email $validatedData = $this->validate(); // send email // show a success toast $this->dispatchBrowserEvent('swal:toast', [ 'background' => 'success', 'html' => "<p class='font-bold mb-2'>Dear $this->name,</p> <p>Thank you for your message.<br>We'll contact you as soon as possible.</p>", ]); // reset all public properties $this->reset(); } public function render() { ... } }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Disable the submit button
- Remember that we modified the Jetstream button component a few chapters ago
We added adisabled
and thecolor
attributes to the component - We can use the
disabled
attribute on the submit button to disable the button when the validation is not successful
Livewire/ContactForm.php
livewire/contact-form.blade.php
result
- Line 7: add a public property
$can_submit
with a default value offalse
- Line 15: set the
$can_submit
property tofalse
if the full validation is successful - Line 17 - 18: set the
$can_submit
property totrue
if thename
,email
andmessage
fields are not empty
REMARK: theif()
statement contains only rough validation (fields can not be empty). We don't check the length of the input fields, but that's enough for this example
class ContactForm extends Component { // public properties public $name; public $email; public $message; public $can_submit = false; // validation rules protected $rules = [ ...]; // real-time validation public function updated($propertyName, $propertyValue) { $this->can_submit = false; $this->validateOnly($propertyName); if ($this->name && $this->email && $this->contact && $this->message) $this->can_submit = true; } public function sendEmail() { ... } public function render() { ... } }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Sending email
- After configuring our mail settings, we set up a mail template, pass some data to this template and send the mail itself
# configure mail settings
- The mail is already set up for you in the
.env
file - The
.env
file contains the following settings for the mail service:- Line 2: replace mailhog with localhost
- Line 7: replace "hello@example.com" with your mailadres e.g. "info@vinyl_shop.test"
MAIL_MAILER=smtp MAIL_HOST=localhost # replace 'mailhog' with 'localhost' MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="info@vinyl_shop.test" # replace "hello@example.com" with your mailadres e.g. "info@vinyl_shop.test" MAIL_FROM_NAME="${APP_NAME}"
Copied!
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
WARNINGS
- If your variable contains spaces, surround it with quotes. E.g. MAIL_FROM_NAME="John Doe"
- If you want a REAL email address to test certain functionalities, use Mailinator (opens new window) e.g.
john.doe@mailinator.com
.
Emails sent to Mailinator are public but temporary and will disappear after a few hours.
# Create mailable
- In Laravel, each type of email sent by your application is represented as a mailable (opens new window) class
- Create a new mailable class with an associated view with the command:
php artisan make:mail ContactMail --markdown=emails.contact-mail
- The ContactMail mailable class is located in the app/Mail directory
- The contact-mail view is located in the resources/views/emails directory
- The --markdown option will create a markdown based view (opens new window)
(Besides a Markdown-based mail template, used in this site, you can also opt for HTML or text-based templates for the mail)
php artisan make:mail ContactMail --markdown=emails.contact
Copied!
1
# Send your first email
- Send an email inside the
sendMail()
method from ourContactForm
component and open MailHog to see the result
Livewire/ContactForm.php
result in MailHog
- Line 8: make a new variable
$template
of theContactMail
mailable class - Line 9: the variable
$to
contains the email address (first parameter) and the name (second parameter) of the person who receives the email
(Import the class for the new Address:use Illuminate\Mail\Mailables\Address;
!) - Line 10: add the recipient using the
to()
method (on theMail
facade) - Line 11: send the mail using the
send()
method with$template
as parameter
// send email public function sendEmail() { // validate the whole request before sending the email $validatedData = $this->validate(); // send email $template = new ContactMail(); $to = new Address($this->email, $this->name); Mail::to($to) ->send($template); // show a success toast $this->dispatchBrowserEvent('swal:toast', [ 'background' => 'success', 'html' => "<p class='font-bold mb-2'>Dear $this->name,</p> <p>Thank you for your message.<br>We'll contact you as soon as possible.</p>", ]); // reset all public properties $this->reset(); }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
REMARKS
- You are not limited to the
to()
recipients. You can also add thecc()
andbcc()
recipients - All these methodes can handle multiple recipients with an array of recipients
- The second parameter (the name) of the
new Address()
method is optional - If you don't need the name of the recipient, you can omit the
new Address()
and just pass the email address as a string,
e.g.$to = 'john.doe@exmpale.com';
is the same as$to = new Address('john.doe@exmpale.com');
- Example:
// send email $template = new ContactMail(); $to_1 = new Address($this->email, $this->name); // email + name $to_2 = new Address('user2@example.com'); // email only $to_3 = 'user3@example.com'; // email only (same as above) Mail::to([$to_1, $to_2, $to_3]) ->cc(['user3@example.com', 'user4@example.com']) ->bcc(['user5@example.com', 'user6@example.com']) ->send($template);
Copied!
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- All other mail settings (
subject
,body
,from
,replayTo
, ...) can be configured in theContactMail
class
# Pass data to the mailable and update the mail template
- Add the data you want to use in the email as parameters to
new ContactMail()
to inject it into the constructor of mailable class and make it available in the mail template- ContactForm component: add an array with the data you want to inject into the constructor of the mailable (and use in the mail template)
- ContactMail mailable: add a public property (e.g.
$data
) and assign the injected data via te constructor to it - Update the Contact view and use the data from the mailable in the mail template
Livewire/ContactForm.php
app/Mail/ContactMail.php
resources/views/emails/contact-mail.blade.php
result in MailHog
- fromName and fromEmail will be used to override the default from address in the mailable class
- subject will be used to override the default subject in the mailable class
- name, email and message will be used in the body of the email
public function sendEmail() { // send email $template = new ContactMail([ 'fromName' => 'The Vinyl Shop - Info', 'fromEmail' => 'info@thevinylshop.com', 'subject' => 'The Vinyl Shop - Contact Form', 'name' => $this->name, 'email' => $this->email, 'message' => $this->message, ]); $to = new Address($this->email, $this->name); Mail::to($to) ->send($template); }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Extra features
- Check Laravel's mail documentation (opens new window) to discover extra mail features, e.g:
- add attachments
- publish and change the default mailable template (
x-mail::message
) - use an HTML-based or plain text mail template
- ...
# Show error bag (optional)
- Instead of showing each error message individually, we can show all errors in one single error block
- To reuse the error block on different pages, we can best make a component for it
- Create a new errorbag component inside the component's folder: resources/views/components/tmk/errorbag.blade.php
- The component don't need extra properties or a slot because all the logic is inside the component itself
components/tmk/errorbag.blade.php
livewire/contact-form.blade.php
result
- Add some extra code to the component:
- Now we can re-use this component on every page where we want bundle the errors into a single error block
@if ($errors->any()) <x-tmk.alert type="danger"> <x-tmk.list> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </x-tmk.list> </x-tmk.alert> @endif
Copied!
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# EXERCISE: Add a contact field to the form
- Add a dropdown list to the form in order to choose a specific contact
- The list contains four options: Select a contact (
value=""
), Info, Billing and Support - This is a required field, so add it to your validation
- Depending on the choice of the user, the from (and carbon copy) address should be adjusted:
- info@thevinylshop.com with the name The Vinyl Shop - Info
- billing@thevinylshop.com with the name The Vinyl Shop - Billing
- or support@thevinylshop.com with the name The Vinyl Shop - Support
The form
Result