Symfony is an open source PHP web development framework - a set of tools and methodology to help you build great applications. Some of the traits of this framework are its speed, flexibility, scalability, and stability. You can use it for a full blown web application but also for smaller functionalities needed for your project.
In the previous tutorial, we began creating a small application to illustrate how to perform CRUD (create, read, update, delete) operations using the Symfony2 framework. For this, we installed the Symfony2 Standard Distribution and started working with our news articles from the database - that if you followed all the steps, you should see at www.example.com/Symfony/web/
What we’ll do in this tutorial is see how to perform the rest of the CRUD operations - create, update and delete - using the same data module we started working with in the last one. Therefore it’s assumed that you have gone through the previous tutorial before starting this one.
In order to create data, let's first create another route to the form page for adding our news article. So let’s edit the routing.yml file from our NewsBundle:
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/config/ routing.yml
And add a new route:
foo_news_add: pattern: /news/add/ defaults: { _controller: FooNewsBundle:Default:add }
What you may notice now is that you’ll have a conflict with the pattern from the route we created previously (foo_news_show) as it will look at the word add and think it’s the ID of the news that needs to be found. So let’s edit the foo_news_show route and create a requirement that the {id} variable is a integer. So transform it like so:
foo_news_show: pattern: /news/{id} defaults: { _controller: FooNewsBundle:Default:show } requirements: id: \d+
Okay, save the file and let’s now create our addAction() method in the DefaultController to act as the callback for this route. So let's edit the DefaultController.php file:
nano /var/www/Symfony/src/Foo/NewsBundle/Controller/ DefaultController.php
The first thing to do is we need to make sure the class loads the News entity class, the Symfony Request component for the Form processing, and the Response component. So at the top of the file below the namespace declaration, add the following line:
use Foo\NewsBundle\Entity\News; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\ HttpFoundation\Response;
Now, below the showAction() method, let’s add the new addAction() method:
public function addAction(Request $request) { $news = new News(); $news->setCreatedDate(new \DateTime()); $form = $this->createFormBuilder($news) ->add('title', 'text') ->add('body', 'text') ->add('save', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em = $this->getDoctrine()-> getManager(); $em->persist($news); $em->flush(); return new Response('News added successfuly'); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); }
What happens here then? First we instantiate a News object and set its created date to the current time. Then we use the Symfony form builder to create a form that has two fields (that match the properties in our News entity) and a submit button. At the end of the function, we then create the form and pass it to the news_add.html.twig template file in the form variable for it to be displayed. Let’s quickly create that file and then we’ll come back to explain the form processing part. So create the new template in the place where you created the rest:
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/views/ Default/news_add.html.twig
And add the following simple line in it:
{{ form(form) }}
Save and test the page by pointing your browser to the new route and you should see the form:
www.example.com/Symfony/web/app_dev.php/news/add
Now what happens if you submit it? If you look in the addAction() method we just created, you’ll see this block we didn’t talk yet about:
$form->handleRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()-> getManager(); $em->persist($news); $em->flush(); return new Response('News added successfully'); }
This basically checks if a form request has been passed (remember the Request object being passed as an argument?) and if it is valid, then automatically updates the $news object we just created and uses the doctrine manager to persist it to the database. Then it creates a new Response object with the success message to be printed on the screen. If no form requests comes in, it will skip these steps and display the form in the normal way. Simple.
Now that we know how to read the news pages and add new ones, updating them will be really easy - it is basically combining the two actions. Like always, let’s add a new route to our routing.yml file:
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/config/ routing.yml
And our route for editing:
foo_news_edit: pattern: /news/{id}/edit defaults: { _controller: FooNewsBundle:Default:edit } requirements: id: \d+
As you can see, we maintain the requirement for the {id} to be an integer and we point to a new method in the same Controller called editAction() - that we will now have to create. So let’s edit the class file again:
nano /var/www/Symfony/src/Foo/NewsBundle/Controller/ DefaultController.php
And add the new method:
public function editAction($id, Request $request) { $em = $this->getDoctrine()->getManager(); $news = $em->getRepository(' FooNewsBundle:News')->find($ id); if (!$news) { throw $this-> createNotFoundException( 'No news found for id ' . $id ); } $form = $this->createFormBuilder($ news) ->add('title', 'text') ->add('body', 'text') ->add('save', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em->flush(); return new Response('News updated successfully'); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); }
What happens here is quite simple again. The difference between this method and addAction() is that instead of us instantiating a new News entity, we retrieve an existing one from the database. The resulting $news object will then again be the basis for the form building. And as you notice, we can use the same template file for rendering the form in this simple example. Another small difference is that we get the entity manager early on and we won’t have to use its persist() method in the form processing because the $news object is already being managed by it. So using flush() is enough to save it into the database.
You can go ahead and try to edit the news page with the ID 1 at www.example.com/Symfony/web/
The last thing we’ll look at in this tutorial is how to remove our news pages. So like usual, let’s set up a route that will take us to a delete confirmation page. So edit the routing.yml file again:
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/config/ routing.yml
And paste in the following route:
foo_news_delete: pattern: /news/{id}/delete/ defaults: { _controller: FooNewsBundle:Default:delete } requirements: id: \d+
Next, let’s create the method deleteAction() in our DefaultController. Edit the class file:
nano /var/www/Symfony/src/Foo/NewsBundle/Controller/ DefaultController.php
And add the new method:
public function deleteAction($id, Request $request) { $em = $this->getDoctrine()->getManager(); $news = $em->getRepository(' FooNewsBundle:News')->find($ id); if (!$news) { throw $this-> createNotFoundException( 'No news found for id ' . $id ); } $form = $this->createFormBuilder($ news) ->add('delete', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em->remove($news); $em->flush(); return new Response('News deleted successfully'); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); }
This method does most of what editAction() does except that instead of building an edit form it just outputs a submit button for deleting the $news object that was retrieved. Then when the form is submitted, we use remove() method of the entity manager to delete the news page from the article. And you’ll notice that again we are using the same template file: news_add.html.twig.
If you now navigate to www.example.com/Symfony/web/
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/views/ Default/news_add.html.twig
And above what’s already in there, paste the following:
{% if form.delete is defined %} Are you sure you want to delete the news page: {{ form.vars.value.title }} ? {% endif %}
This will print out a confirmation text that includes the title of the news page in the question (taken from the form variable passed to the template). Now if you refresh the page, you’ll see it there and you can click delete in order to remove the news with the ID 1. You should then get a confirmation message delivered by the Response object we created.
We’ve now implemented the core CRUD operations so it’s time to finalize our small application by rounding it up a bit. This means adding some links on the news listing page that would quickly allow you to perform operations. We’ve seen how to create links in the template in the last tutorial so now it shouldn’t be a problem. Edit the news_show_all.html.twig template file and let’s add some admin links:
nano /var/www/Symfony/src/Foo/NewsBundle/Resources/views/ Default/news_show_all.html.twig
Below the following line:
<h3><a href="{{ path('foo_news_show', {'id': new.Id }) }}">{{ new.Title }}</a></h3>
Paste this:
<a href="{{ path('foo_news_edit', {'id': new.Id }) }}">Edit</a> | <a href="{{ path('foo_news_delete', {'id': new.Id }) }}">Delete</a><hr />
And at the top of the page paste this:
<a href="{{ path('foo_news_add') }}">Add news</a>
Now go to www.example.com/Symfony/web/
In this and the previous tutorials, we’ve seen how to build a small Symfony2 application that performs CRUD on our data model, the news pages. Your DefaultController.php class file should now look something like this:
<?php namespace Foo\NewsBundle\Controller; use Foo\NewsBundle\Entity\News; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\ HttpFoundation\Response; use Symfony\Bundle\ FrameworkBundle\Controller\ Controller; class DefaultController extends Controller { public function indexAction() { $news = $this->getDoctrine() ->getRepository(' FooNewsBundle:News') ->findAll(); if (!$news) { throw $this-> createNotFoundException('No news found'); } $build['news'] = $news; return $this->render('FooNewsBundle: Default:news_show_all.html. twig', $build); } public function showAction($id) { $news = $this->getDoctrine() ->getRepository(' FooNewsBundle:News') ->find($id); if (!$news) { throw $this-> createNotFoundException('No news found by id ' . $id); } $build['news_item'] = $news; return $this->render('FooNewsBundle: Default:news_show.html.twig', $build); } public function addAction(Request $request) { $news = new News(); $news->setCreatedDate(new \DateTime()); $form = $this->createFormBuilder($ news) ->add('title', 'text') ->add('body', 'text') ->add('save', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em = $this->getDoctrine()-> getManager(); $em->persist($news); $em->flush(); return new Response('News added successfuly'); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); } public function editAction($id, Request $request) { $em = $this->getDoctrine()-> getManager(); $news = $em->getRepository(' FooNewsBundle:News')->find($ id); if (!$news) { throw $this-> createNotFoundException( 'No news found for id ' . $id ); } $form = $this->createFormBuilder($ news) ->add('title', 'text') ->add('body', 'text') ->add('save', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em->flush(); return new Response('News updated successfully'); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); } public function deleteAction($id, Request $request) { $em = $this->getDoctrine()-> getManager(); $news = $em->getRepository(' FooNewsBundle:News')->find($ id); if (!$news) { throw $this-> createNotFoundException( 'No news found for id ' . $id ); } $form = $this->createFormBuilder($ news) ->add('delete', 'submit') ->getForm(); $form->handleRequest($request) ; if ($form->isValid()) { $em->remove($news); $em->flush(); } $build['form'] = $form->createView(); return $this->render('FooNewsBundle: Default:news_add.html.twig', $build); } }
Although this has been very simple, Symfony2 is capable of scaling up to much more complex data, structured in multiple tables and perform wonders when it comes to managing it. Lastly, using the Twig templating system with Symfony allows you to create very powerful and easy to manage websites. Good luck!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Thank Danny, it has been wonderful follow this tutorial, specially when you are starting at the open source programming. Big thanks for this!!!
Hello There is no javascript code ?
If you are using Doctrine, you also can use the generate:crud command using the Terminal. It will generate all CRUD methods in some seconds. Really useful.
See more information in the Documentation.
Great Tutorial… :)
coool…
Hello Danny,
Sir, you have created a door into Symfony2 for which I have been groping for months.
This tutorial is flawlessly conceived and produced. To use a hackneyed expression, I now feel empowered to place upon your foundation the rich edifice of Symfony2.
You have a profound talent, never to be underestimated, for eaking out the essential. (CS Lewis of the software industry?)
You have ‘changed the game’ for me. Thank you so much !
Kindest regards,
Chris
Thanx a dozen, very informative and helpful Danny!