I am new at Symfony. I still have a lot to learn. However, one of the things that bugged me for ages was that the docs didn’t describe any means for populating forms when editing an existing record. Yes, it presented a function for editing, and making changes to the database through doctrine, but no way for me to dynamically alter the data in the first place. Well , thanks to some help from other Symfony users, I discovered a way that’s not only simple, but most of the code is from the docs (with a few adjustments). Here’s one way that I found to do it.
Table of Contents
Groundwork
Some assumptions:
- You have enough PHP, HTML, CSS knowledge to be able code in Symfony. There are plenty of existing tutorials covering those.
- You’re using Symfony 7. I’m currently using 7.3.
- You have already set up doctrine, your entity, model, form type, and have a working create function in your controller. All of which is set up to use forms.
- You’ll also need a function for showing the existing entry once it’s been added.
- You’re using twig, and that you have a working create template (hopefully with some decent CSS, because the default is simply awful).
- Your database already has at least one record, that you wish to modify.
If you don’t already have all of those, then this tutorial won’t be much help because I won’t be covering any of that. Instead, check out the Symfony docs. They cover those areas fairly well. Then, come back when you’re ready to continue with the editing function.
Editing
Here’s the update/editing function in the current documentation (7.3). We’ll use this as our base for populating forms.
// src/Controller/ProductController.php
namespace App\Controller;
use App\Entity\Product;
use App\Repository\ProductRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
// ...
class ProductController extends AbstractController
{
#[Route('/product/edit/{id}', name: 'product_edit')]
public function update(EntityManagerInterface $entityManager, int $id): Response
{
$product = $entityManager->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
$product->setName('New product name!');
$entityManager->flush();
return $this->redirectToRoute('product_show', [
'id' => $product->getId()
]);
}
}
Basically, this function fetches the object from Doctrine, modifies it, and then updates the existing record with the new information. However, it only allows us to change the name, nothing else. And we have to edit the code each time we need to do that simple modification. Adding forms allows us to make changes dynamically.
Forms
Let’s start by adding the following to the top of the controller:
use App\Form\Type\ProductType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
These are likely already at the top of your Controller class, so you might not need to make any changes. The ProductType sets out the details for your existing form (which you’ve already used in your create function). The Abstract Controller allows you to extend the Controller and the Request allows you to issue requests to a function.
Then, once we’ve assigned a value to $product, and check it’s valid, we create a new form. It should look something like this:
$product = $entityManager->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
// Create the form
$form = $this->createForm(ProductType::class, $product);
It likely appears very similar to your existing create function, but we send the value of $product to the form to populate it with the existing data. Then, below that we handle the request to check the form submission.
// Handle the request to check for form submission
$form->handleRequest($request);
Since we’ll be using the controller for both creating the form, as well as injecting the data we need to check which one is happening when. So let’s do the injecting as the option and the rendering the form as the fallback.
if ($form->isSubmitted() && $form->isValid()) {
// Process the form data (e.g., save to database)
$entityManager->flush();
return $this->redirectToRoute('product_show', ['id' => $id]); // redirect after success
}
So, if the form is submitted and valid we process the form and save it to the database. Then, we redirect to product_show function using the id of the product as its identifier to show the details of that particular product.
Next we do the fallback. If the form is not submitted or not valid, we render the same template we use for the create function. Except we send through the data we have in the form, and it populates the form – all by itself.
// Render the form view
return $this->render('product/create.html.twig', [
'form' => $form,
]);
}
And that’s it. Here’s the function in its entirety:
// src/Controller/ProductController.php
namespace App\Controller;
use App\Form\Type\ProductType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Product;
use App\Repository\ProductRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ProductController extends AbstractController
{
//...
#[Route('/product/edit/{id}', name: 'product_edit')]
public function update(EntityManagerInterface $entityManager, int $id): Response
{
$product = $entityManager->getRepository(Product::class)->find($id);
if(!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
// Create the form
$form = $this->createForm(ProductType::class, $product);
// Handle the request to check for form submission
$form→handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Process the form data (e.g., save to database)
$entityManager->flush();
return $this->redirectToRoute('product_show', ['id' => $id]); // redirect after success
}
// Render the form view
return $this->render('product/create.html.twig', [
'form' => $form,
]);
}
//...
}
I hope that helps.
Leave a comment