Hello everyone!

We have been investing plenty of personal time and energy for many years to share our knowledge with you all. However, we now need your help to keep this blog running. All you have to do is just click one of the adverts on the site, otherwise it will sadly be taken down due to hosting etc. costs. Thank you.

Example below uses webforms and all the process are handled in controller although it is not a "good practise". It just gives you a basic idea. The better way is to move logic into custom services. "Thin controller, fat service" is considered as "good practice" when developing applications.


Facts



Entity


namespace Football\FrontendBundle\Entity;

use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
* @ORM\Entity(repositoryClass="Football\FrontendBundle\Repository\CountryRepository")
* @ORM\Table(name="country")
* @UniqueEntity(fields="code", message="Code is already taken.")
* @UniqueEntity(fields="name", message="Name is already taken.")
* @ORM\HasLifecycleCallbacks
*/
class Country
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="smallint")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @var string
*
* @ORM\Column(type="string", length=2, unique=true)
* @Assert\NotBlank(message="Code is required.")
* @Assert\Regex(pattern="/^[A-Z]{2}$/",
* message="Code must be exactly 2 character long upper case letters."
* )
*/
protected $code;

/**
* @var string
*
* @ORM\Column(name="name", type="string", length=100, unique=true)
* @Assert\NotBlank(message="Name is required.")
* @Assert\Length(
* max=100,
* exactMessage="Name cannot be longer than {{ limit }} characters."
* )
*/
protected $name;

/**
* @var datetime
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
*/
protected $createdAt;

/**
* @var datetime
*
* @ORM\Column(name="updated_at", type="datetime", nullable=true)
*/
protected $updatedAt;

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set code
*
* @param string $code
* @return Country
*/
public function setCode($code)
{
$this->code = $code;

return $this;
}

/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}

/**
* Set name
*
* @param string $name
* @return Country
*/
public function setName($name)
{
$this->name = $name;

return $this;
}

/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}

/**
* @ORM\PrePersist
*/
public function onPrePersist()
{
$this->createdAt = new DateTime('now');
}

/**
* @ORM\PreUpdate
*/
public function onPreUpdate()
{
$this->updatedAt = new DateTime('now');
}

/**
* Get createdAt
*
* @return DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}

/**
* Get updatedAt
*
* @return DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}

FormType


namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CountryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add('code', 'text', ['required' => false, 'error_bubbling' => true])
->add('name', 'text', ['required' => false, 'error_bubbling' => true]);
}

public function getName()
{
return 'country';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\Country']
);
}
}

Controller


namespace Football\FrontendBundle\Controller;

use Doctrine\DBAL\DBALException;
use Doctrine\ORM\ORMException;
use Exception;
use Football\FrontendBundle\Entity\Country;
use Football\FrontendBundle\Exception\CountryException;
use Football\FrontendBundle\Form\Type\CountryType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/country")
*/
class CountryController extends Controller
{
const ROUTER_PREFIX = 'football_frontend_country_';

/**
* Landing page.
*
* @Route("")
* @Method({"GET"})
*
* @return Response
*/
public function listAction()
{
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Country');

return $this->getFormView('list', ['countryArray' => $repo->findAll()]);
}

/**
* Create page.
*
* @Route("/create")
* @Method({"GET"})
*
* @return Response
*/
public function createAction()
{
$form = $this->getForm(
new Country(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);

return $this->getFormView('create', ['form' => $form->createView()]);
}

/**
* Creates processing.
*
* @param Request $request
*
* @Route("/create")
* @Method({"POST"})
*
* @return RedirectResponse|Response
* @throws CountryException
*/
public function createProcessAction(Request $request)
{
if ($request->getMethod() != 'POST') {
throw new CountryException('Country create: only POST method is allowed.');
}

$form = $this->getForm(
new Country(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);
$form->handleRequest($request);

if (!$form->isSubmitted()) {
throw new CountryException('Country create: form is not submitted.');
}

if ($form->isValid() !== true) {
return $this->getFormView('create', ['form' => $form->createView()]);
}

try {
$data = $form->getData();

$country = new Country();
$country->setCode($data->getCode());
$country->setName($data->getName());

$em = $this->getDoctrine()->getManager();
$em->persist($country);
$em->flush();
} catch (DBALException $e) {
$message = sprintf('DBALException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%i]: %s', $e->getCode(), $e->getMessage());
}

if (isset($message)) {
throw new CountryException($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}

/**
* Update page.
*
* @Route("/update/{id}", requirements={"id"="\d+"})
* @Method({"GET"})
*/
public function updateAction($id)
{
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Country');
$country = $repo->findOneById($id);
if (!$country instanceof Country) {
throw new CountryException(sprintf('Country update: country [%i] cannot be found.', $id));
}

$form = $this->getForm(
$country,
'PATCH',
$this->generateUrl(self::ROUTER_PREFIX . 'updateprocess', ['id' => $id])
);

return $this->getFormView(
'update',
[
'form' => $form->createView(),
'id' => $country->getId()
]
);
}

/**
* Update processing.
*
* @param Request $request
* @param int $id
*
* @Route("/update/{id}", requirements={"id"="\d+"})
* @Method({"PATCH"})
*
* @return RedirectResponse|Response
* @throws CountryException
*/
public function updateProcessAction(Request $request, $id)
{
if ($request->getMethod() != 'PATCH') {
throw new CountryException('Country update: only PATCH method is allowed.');
}

$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Country');
$country = $repo->findOneById($id);
if (!$country instanceof Country) {
throw new CountryException(sprintf('Country update: country [%i] cannot be found.', $id));
}

$form = $this->getForm(
$country,
'PATCH',
$this->generateUrl(self::ROUTER_PREFIX . 'updateprocess', ['id' => $id])
);
$form->handleRequest($request);

if (!$form->isSubmitted()) {
throw new CountryException('Country update: form is not submitted.');
}

if ($form->isValid() !== true) {
return $this->getFormView('update', ['form' => $form->createView()]);
}

try {
$data = $form->getData();

$country->setCode($data->getCode());
$country->setName($data->getName());

$em = $this->getDoctrine()->getManager();
$em->flush();
} catch (DBALException $e) {
$message = sprintf('DBALException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%i]: %s', $e->getCode(), $e->getMessage());
}

if (isset($message)) {
throw new CountryException($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}

/**
* Fetches country.
*
* @param int $id
*
* @Route("/{id}", requirements={"id"="\d+"})
* @Method({"GET"})
*
* @return RedirectResponse|Response
* @throws CountryException
*/
public function readAction($id)
{
try {
$repo = $this->getDoctrine()->getRepository('FootballFrontendBundle:Country');
$country = $repo->findOneById($id);
if (!$country instanceof Country) {
throw new CountryException(sprintf('Country read: country [%i] cannot be found.', $id));
}

return $this->getFormView('read', ['country' => $country]);
} catch (DBALException $e) {
$message = sprintf('DBALException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%i]: %s', $e->getCode(), $e->getMessage());
}

if (isset($message)) {
throw new CountryException($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}

/**
* Deletes country.
*
* @param int $id
*
* @Route("/delete/{id}", requirements={"id"="\d+"})
* @Method({"GET"})
*
* @return RedirectResponse|Response
* @throws CountryException
*/
public function deleteAction($id)
{
try {
$em = $this->getDoctrine()->getEntityManager();
$repo = $em->getRepository('FootballFrontendBundle:Country');

$country = $repo->findOneById($id);
if (!$country instanceof Country) {
throw new CountryException(sprintf('Country delete: country [%i] cannot be found.', $id));
}

$em->remove($country);
$em->flush();
} catch (DBALException $e) {
$message = sprintf('DBALException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%i]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%i]: %s', $e->getCode(), $e->getMessage());
}

if (isset($message)) {
throw new CountryException($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}

/**
* Creates form object.
*
* @param Country $country
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm(Country $country, $method, $action)
{
return $this->createForm(
new CountryType(),
$country,
[
'method' => $method,
'action' => $action
]
);
}

/**
* Creates webform.
*
* @param string $template
* @param array $parameters
*
* @return Response
*/
private function getFormView($template, array $parameters = [])
{
return $this->render(
sprintf('FootballFrontendBundle:Country:%s.html.twig', $template),
$parameters
);
}
}

Routers


inanzzz-MBP:sport inanzzz$ php app/console router:debug
[router] Current routes
Name Method Scheme Host Path
_wdt ANY ANY ANY /_wdt/{token}
_profiler_home ANY ANY ANY /_profiler/
_profiler_search ANY ANY ANY /_profiler/search
_profiler_search_bar ANY ANY ANY /_profiler/search_bar
_profiler_purge ANY ANY ANY /_profiler/purge
_profiler_info ANY ANY ANY /_profiler/info/{about}
_profiler_phpinfo ANY ANY ANY /_profiler/phpinfo
_profiler_search_results ANY ANY ANY /_profiler/{token}/search/results
_profiler ANY ANY ANY /_profiler/{token}
_profiler_router ANY ANY ANY /_profiler/{token}/router
_profiler_exception ANY ANY ANY /_profiler/{token}/exception
_profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css
_configurator_home ANY ANY ANY /_configurator/
_configurator_step ANY ANY ANY /_configurator/step/{index}
_configurator_final ANY ANY ANY /_configurator/final
_twig_error_test ANY ANY ANY /_error/{code}.{_format}
football_frontend_country_list GET ANY ANY /country
football_frontend_country_create GET ANY ANY /country/create
football_frontend_country_createprocess POST ANY ANY /country/create
football_frontend_country_update GET ANY ANY /country/update/{id}
football_frontend_country_updateprocess PATCH ANY ANY /country/update/{id}
football_frontend_country_read GET ANY ANY /country/{id}
football_frontend_country_delete GET ANY ANY /country/delete/{id}
football_frontend_default_index GET ANY ANY /

List template


{% extends 'FootballFrontendBundle:Country:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
COUNTRY - List
<hr />
{% if countryArray is defined and countryArray|length != 0 %}
<table border="1px">
<tr>
<td>#</td>
<td>ID</td>
<td>CODE</td>
<td>NAME</td>
<td>CREATED</td>
<td>UPDATED</td>
<td> </td>
<td> </td>
<td> </td>
</tr>
{% for country in countryArray %}
<tr>
<td class="head">{{ loop.index }}</td>
<td>{{ country.id }}</td>
<td>{{ country.code }}</td>
<td>{{ country.name }}</td>
<td>{{ country.createdAt|date('d/m/Y H:i:s') }}</td>
<td>{{ country.updatedAt is not null ? country.updatedAt|date('d/m/Y H:i:s') : '' }}</td>
<td><a href="{{ path('football_frontend_country_read', {'id':country.id}) }}">view</a></td>
<td><a href="{{ path('football_frontend_country_delete', {'id':country.id}) }}">delete</a></td>
<td><a href="{{ path('football_frontend_country_update', {'id':country.id}) }}">update</a></td>
</tr>
{% endfor %}
</table>
{% else %}
No country found in database!
{% endif %}
{% endspaceless %}
{% endblock %}

Read template


{% extends 'FootballFrontendBundle:Country:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
COUNTRY - Read
<hr />
<table border="1px">
<tr>
<td>ID</td>
<td>CODE</td>
<td>NAME</td>
<td>CREATED</td>
<td>UPDATED</td>
</tr>
<tr>
<td>{{ country.id }}</td>
<td>{{ country.code }}</td>
<td>{{ country.name }}</td>
<td>{{ country.createdAt|date('d/m/Y H:i:s') }}</td>
<td>{{ country.updatedAt is not null ? country.updatedAt|date('d/m/Y H:i:s') : '' }}</td>
</tr>
</table>
{% endspaceless %}
{% endblock %}

Create template


{% extends 'FootballFrontendBundle:Country:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
COUNTRY - Create
<hr />
{{ form_start(form) }}
{% if form_errors(form) != '' %}

{{ form_errors(form) }}

{% endif %}

<p>CODE: {{ form_widget(form.code) }}</p>
<p>NAME: {{ form_widget(form.name) }}</p>

<p><button name="button">Submit</button></p>
{{ form_end(form) }}
{% endspaceless %}
{% endblock %}

Update template


{% extends 'FootballFrontendBundle:Country:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
COUNTRY - Update
<hr />
{{ form_start(form) }}
{% if form_errors(form) != '' %}

{{ form_errors(form) }}

{% endif %}

<p>CODE: {{ form_widget(form.code) }}</p>
<p>NAME: {{ form_widget(form.name) }}</p>

<p><button name="button">Submit</button></p>
{{ form_end(form) }}
{% endspaceless %}
{% endblock %}