Let’s create a new page for a contact form.
Add another controller action to the PageController
class:
return [
'bikes' => $bikes,
];
}
+ /**
+ * @Route("/contact")
+ * @Template
+ */
+ public function contactAction()
+ {
+ return [];
+ }
}
and create a new frontend template for it:
./bin/console perform-dev:create:page AppBundle:Page:contact --frontend twbs3
Finally, update the frontend menu in nav.html.twig
to link to the new route.
<ul class="nav navbar-nav">
{% block menu %}
<li><a href="/">Home</a></li>
+ <li><a href="{{path('app_page_contact')}}">Contact</a></li>
{% endblock %}
</ul>
Note
We are using the path
twig function to link to the new action, but you could simply write /contact
as well.
Remember you’ll need to update the url manually whe using this method if the route ever changes.
nav.html.twig
was generated by the bootstrap 3 frontend.
Not all frontends will create this file; you may need to update the
frontend menu (if one even exists) in a different file when using a different frontend.
The PerformContactBundle provides a contact form type, with tools to handle submissions and sending of notifications when a message is sent. We’ll use this bundle to construct a contact form quickly.
Update the contactAction
method:
+ use Symfony\Component\HttpFoundation\Request;
+ use Perform\ContactBundle\Form\Type\MessageType;
/**
* @Route("/contact")
* @Template
*/
- public function contactAction()
+ public function contactAction(Request $request)
+ {
+ $form = $this->createForm(MessageType::class);
+ $handler = $this->get('perform_contact.form.handler.contact');
+ try {
+ $result = $handler->handleRequest($request, $form);
+ if ($result) {
+ $this->addFlash('success', 'Thank you. Your message has been sent.');
+ return $this->redirectToRoute('app_page_contact');
+ }
+ } catch (\Exception $e) {
+ $this->get('logger')->error($e);
+ $this->addFlash('danger', 'An error occurred. Please try again.');
+ }
- return [];
+ return [
+ 'form' => $form->createView(),
+ ];
}
This will look familiar if you’ve used forms in a Symfony controller action before.
We create a new form with Perform\ContactBundle\Form\Type\MessageType
, redirecting with a flash message on success, and showing an error message on failure.
However, instead of handling the form submission ourselves, we get the perform_contact.form.handler.contact
service to handle it for us.
This service checks the form submission, saves a new message to the database, checks for spam, and sends notifications when configured.
See the PerformContactBundle documentation for more information on how this works.
Now update contact.html.twig
:
- <div class="col-md-12">
- <h1>Contact</h1>
+ <div class="col-md-6">
+ <h1>Contact Us</h1>
+ {{form_start(form)}}
+ {{form_row(form.name)}}
+ {{form_row(form.email)}}
+ {{form_row(form.message)}}
+ <button type="submit" class="btn btn-primary">Send</button>
+ {{form_end(form)}}
</div>
Head to the new page at http://127.0.0.1:8000/contact and fill out the form. The page should refresh, and you’ll be shown a success message.
Now head to the admininstration area and click on the ‘Contact Form’ link. You’ll see a grid of form submissions, with buttons to archive messages you’ve dealt with and mark messages as spam.
You can be notified of successful form submissions in a variety of ways.
By default, email notifications will be sent to the email address you configure in the settings page. Open the ‘Contact Form’ panel in the ‘Settings’ page of the admin and add an email address to send notifications too.
Depending on your system, you might need to update the swiftmailer bundle configuration to send emails correctly.
By default, it uses the values of the mailer_*
parameters in app/config/parameters.yml
.
See the Swiftmailer bundle documentation for more information.
The delivery_address
setting can be useful for local development.
All emails will be sent to this address, with the original address being included in the X-Swift-To
email header.
Once you’ve configured email sending, try submitting the form again. You’ll be sent an email notification with details of the submission.
Unfortunately Perform’s message entity doesn’t quite fit our needs. We’ve been asked to include another optional field in the form; favourite bike, asking for the visitor’s favourite bike on the site.
Does this mean we have to scrap the PerformContactBundle, the admin interface, and all the tooling? Of course not! Perform has tools that make it easy to extend an entity from a vendor bundle to fit your requirements.
First, create a new entity class that extends Perform\ContactBundle\Entity\Message
:
<?php
namespace AppBundle\Entity;
use Perform\ContactBundle\Entity\Message;
class ContactMessage extends Message
{
protected $favouriteBike;
/**
* @param Bike|null $favouriteBike
*
* @return ContactMessage
*/
public function setFavouriteBike(Bike $favouriteBike = null)
{
$this->favouriteBike = $favouriteBike;
return $this;
}
/**
* @return Bike|null
*/
public function getFavouriteBike()
{
return $this->favouriteBike;
}
}
Then create a doctrine mapping file, but only include the new favouriteBike
property:
AppBundle\Entity\ContactMessage:
type: entity
manyToOne:
favouriteBike:
targetEntity: Bike
joinColumn:
nullable: true
Note
You could also use the perform-dev:create:entity
command to create these files, but remember to remove the id and other unrelated fields, and to extend the Perform\ContactBundle\Entity\Message
class.
Now for the clever bit - we will tell Perform that this entity extends the message entity in the contact bundle. All the tools in the contact bundle will continue to work, even though they have no knowledge about our new entity.
Add an entry to perform_base:extended_entities
in app/config/config.yml
:
perform_base:
+ extended_entities:
+ "PerformContactBundle:Message": "AppBundle:ContactMessage"
panels:
left: []
Note
Under the hood, Perform will rewrite some Doctrine metadata to treat the original message entity like an abstract mapped superclass.
Read the extending entities documentation for a look into how it works.
Now update the database schema, where you’ll notice creation of a table for the new message entity.
With the --complete
flag, the existing perform_contact_message
table will be removed.
./bin/console doctrine:schema:update --force --dump-sql --complete
Now we need to add the new option to the contact form.
Create a new form type in src/AppBundle/Form/Type/ContactMessageType.php
that extends the existing form type:
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Perform\ContactBundle\Form\Type\MessageType;
use AppBundle\Entity\ContactMessage;
class ContactMessageType extends MessageType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->add('favouriteBike', EntityType::class, [
'class' => 'AppBundle:Bike',
'choice_label' => 'title',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ContactMessage::class,
]);
}
}
And update the controller action to use this new form type:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
- use Perform\ContactBundle\Form\Type\MessageType;
+ use AppBundle\Form\Type\ContactMessageType;
use Symfony\Component\HttpFoundation\Request;
public function contactAction(Request $request)
{
- $form = $this->createForm(MessageType::class);
+ $form = $this->createForm(ContactMessageType::class);
$handler = $this->get('perform_contact.form.handler.contact');
Finally, update the twig template for the page:
{{form_start(form)}}
{{form_row(form.name)}}
{{form_row(form.email)}}
+ {{form_row(form.favouriteBike)}}
{{form_row(form.message)}}
<button type="submit" class="btn btn-primary">Send</button>
{{form_end(form)}}
That’s it!
Refresh the contact page and you’ll see the new form field.
All contact form submissions will now be an instance of AppBundle\Entity\ContactMessage
, saving the favouriteBike
field as well.
There is just one piece missing. We can save form submissions with the new field, but it doesn’t appear in the admin yet.
Create a new crud class:
./bin/console perform-dev:create:crud AppBundle:ContactMessage
and make some modifications.
First, extend the existing crud from the contact bundle:
- use Perform\BaseBundle\Crud\AbstractCrud;
+ use Perform\ContactBundle\Crud\MessageCrud;
use Perform\BaseBundle\Config\FieldConfig;
use Perform\BaseBundle\Config\FilterConfig;
use Perform\BaseBundle\Config\ActionConfig;
- class ContactMessageCrud extends AbstractCrud
+ class ContactMessageCrud extends MessageCrud
Then add the new field to configureFields
, making sure to also call the parent method:
public function configureFields(FieldConfig $config)
{
+ parent::configureFields($config);
+
+ $config->add('favouriteBike', [
+ 'type' => 'entity',
+ 'options' => [
+ 'class' => 'AppBundle:Bike',
+ 'display_field' => 'title',
+ ],
+ ]);
}
Finally, either call the parent method in configureFilters
, or remove the method entirely.
public function configureFilters(FilterConfig $config)
{
+ parent::configureFilters($config);
}
After refreshing the page, the contact message admin page should display the new field.
While the new field shows up in the list of messages, unfortunately it’s not visible when viewing the messages.
This is because the contact bundle has a different template for viewing message entities. Let’s override that template with our own version that displays the visitor’s favourite bike too.
Override the getTemplate
method of ContactMessageCrud
to the following:
use Perform\BaseBundle\Config\ActionConfig;
use Perform\ContactBundle\Crud\MessageCrud;
+ use Twig\Environment;
<?php
public function getTemplate(Environment $twig, $entityName, $context)
{
if ($context === CrudRequest::CONTEXT_VIEW) {
return '@App/crud/contact_message/view.html.twig';
}
return parent::getTemplate($twig, $entityName, $context);
}
Here we override the template, but only for the view
context. All other contexts fall back to the default behaviour of the parent crud class.
Note
See the crud documentation for more information on overriding templates.
Create the file src/AppBundle/Resources/views/crud/contact_message/view.html.twig
for this new view and insert the following:
{% extends '@PerformContact/crud/message/view.html.twig' %}
{% block extras %}
{% if entity.favouriteBike is not null %}
<p>
Favourite bike:
<a href="{{perform_crud_route(entity.favouriteBike, 'view')}}">
{{entity.favouriteBike.title}}
</a>
</p>
{% endif %}
{% endblock %}
Now a link to the visitor’s favourite bike will be shown when viewing the contact form message.