Easy Symfony Tutorial on CRUD operations

In this brief tutorial I’m gonna show you how to build a simple Symfony CRUD Web App, fully working.

We assume you have installed Symfony 3.4 on your machine, if not, please refer to one of my previous tutorials, at the following link.

The Symfony Php Framework

Read the following, https://www.benedict.cloud/2018/02/23/mini-tutorial-about-the-symfony-bundle-generator, on how to do it.

Now, the first thing is to configure Symfony to access to your MySQL database, go ahead and open your ‘parameters.yml‘ located under the ‘app/config‘ directory on your project’s root folder. Configure your database port to ‘3306‘, database user and database password to your MySQL installation values, leave the rest unaltered.

Alright, let’s see if we can create the database on our MySQL local server, open your shell, switch to your project’s root folder and digit the following command:

$ php bin/console doctrine:database:create

You should see that the database is created if you log in on your MySQL console. For this tutorial we’re gonna create a Blog Article entity.

So go to your ‘src/AppBundle‘ folder and create a directory called ‘Entity‘ in which you’ll put a file called ‘Article.php‘ with the following content inside the php tags:

namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="article")
*/
class Article {

  /**
  * @ORM\Column(type="integer")
  * @ORM\Id
  * @ORM\GeneratedValue(strategy="AUTO")
  */
  protected $id;

  /**
  * @ORM\Column(type="string", length=100)
  */
  protected $title;

  /**
  * @ORM\Column(type="string", length=50)
  */
  protected $author;

  /**
  * @ORM\Column(type="string", length=1000)
  */
  protected $body;

  /**
  * @ORM\Column(type="string", length=200, nullable=true)
  */
  protected $url;

}

Then go your terminal and type these commands:

$ php bin/console doctrine:generate:entities AppBundle:Article
$ php bin/console doctrine:schema:update --force

You just created your Entity as a class with all the getters and setters (Symfony created also a backup for your old Article.php) and an Entity as database table. Fine, let’s now create the app controller.

Put the following content on a php file named ‘ArticleController.php‘, inside ‘src/AppBundle/Controller‘:

namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

use AppBundle\Entity\Article;

class ArticleController extends Controller {

}

Alright then, let’s now create our first route, the create action of our CRUD operations, put this method inside your controller class:

/**
* @Route("/create-article")
*/
public function createAction(Request $request) {

  $article = new Article();
  $form = $this->createFormBuilder($article)
    ->add('title', TextType::class)
    ->add('author', TextType::class)
    ->add('body', TextareaType::class)
    ->add('url', TextType::class,
    array('required' => false, 'attr' => array('placeholder' => 'www.example.com')))
    ->add('save', SubmitType::class, array('label' => 'New Article'))
    ->getForm();

  $form->handleRequest($request);

  if ($form->isSubmitted()) {

    $article = $form->getData();

    $em = $this->getDoctrine()->getManager();
    $em->persist($article);
    $em->flush();

    return $this->redirect('/view-article/' . $article->getId());

  }

  return $this->render(
    'article/edit.html.twig',
    array('form' => $form->createView())
    );

}

Add then the following template named ‘edit.html.twig‘ under ‘app/Resources/views/article‘, after creating the ‘article‘ folder under ‘views‘:

<style>
th, td {
  padding: 10px;
}
th {
  text-align: right;
}
</style>

<h1>Create or Update Article:</h1>

{{ form_start(form) }}

<table>
  <tbody>
    <tr>
      <th>Title:</th>
      <td>{{ form_widget(form.title) }}</td>
    </tr>
    <tr>
      <th>Author:</th>
      <td>{{ form_widget(form.author) }}</td>
    </tr>
    <tr>
      <th>Article Body:</th>
      <td>{{ form_widget(form.body) }}</td>
    </tr>
    <tr>
      <th>Homepage:</th>
      <td>{{ form_widget(form.url) }}</td>
    </tr>
    <tr>
      <th></th>
      <td>{{ form_widget(form.save) }}</td>
    </tr>
  </tbody>
</table>

{{ form_end(form) }}

You should see a form now, if you run the Symfony server and go to ‘/create-article‘ with your browser. You should also be able to create an article (and then view it) if you add the following template: ‘view.html.twig‘ and its view action.

<style>
th, td {
  padding: 10px;
}
th {
  text-align: right;
}
</style>

<h1>Article Created or Modified</h1>

<table>
  <tbody>
    <tr>
      <th>Title:</th>
    <td>{{ article.title }}</td>
    </tr>
    <tr>
      <th>Author:</th>
      <td>{{ article.author }}</td>
    </tr>
    <tr>
      <th>Article Body:</th>
      <td>{{ article.body }}</td>
    </tr>
    <tr>
      <th>Homepage:</th>
      <td>{{ article.url }}</td>
    </tr>
  </tbody>
</table>

<br/>
<a href="/show-articles">View All</a> -
<a href="/update-article/{{ article.id }}">Modify</a>
/**
* @Route("/view-article/{id}")
*/   
public function viewAction($id) {

  $article = $this->getDoctrine()
    ->getRepository('AppBundle:Article')
    ->find($id);

  if (!$article) {
    throw $this->createNotFoundException(
    'There are no articles with the following id: ' . $id
    );
  }

  return $this->render(
    'article/view.html.twig',
    array('article' => $article)
    );

}

Now that we have those in place, we can create our ‘/show-articles‘ route:

/**
* @Route("/show-articles")
*/  
public function showAction() {

  $articles = $this->getDoctrine()
    ->getRepository('AppBundle:Article')
    ->findAll();

  return $this->render(
    'article/show.html.twig',
    array('articles' => $articles)
    );

}

And the relative ‘show.html.twig‘, always under the articles folder:

<style>
th, td {
  padding: 10px;
}
th {
  text-align: left;
}
</style>

<h1>All Articles</h1>

{% if articles is not empty %}
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Author</th>
      <th>Body</th>
      <th>Homepage</th>
      <th>Delete</th>
      <th>Update</th>
    </tr>
  </thead>
  <tbody>
  {% for article in articles %}
  <tr>
    <td>{{ article.title }}</td>
    <td>{{ article.author }}</td>
    <td>{{ article.body | slice(0, 20) }}...</td>
    <td>
    {% if article.url != "" %}
      <a href="http://{{ article.url }}">Visit Homepage</a>
    {% else %}
      Void Field
    {% endif %}
    </td>
    <td><a href="delete-article/{{ article.id }}">Delete</a></td>
    <td><a href="update-article/{{ article.id }}">Update</a></td>
  </tr>
  {% endfor %}
  </tbody>
</table>
{% else %}
  <p>No article found.</p>
{% endif %}

<br/>
<a href="/create-article">Create New</a>

As you can see in the ‘/show-articles‘ page, if you go to it with your browser, you can also delete and update the articles, which are the last two CRUD operations left, add them in the controller:

/**
* @Route("/delete-article/{id}")
*/ 
public function deleteAction($id) {

  $em = $this->getDoctrine()->getManager();
  $article = $em->getRepository('AppBundle:Article')->find($id);

  if (!$article) {
    throw $this->createNotFoundException(
    'There are no articles with the following id: ' . $id
    );
  }

  $em->remove($article);
  $em->flush();

  return $this->redirect('/show-articles');

}
/**
* @Route("/update-article/{id}")
*/  
public function updateAction(Request $request, $id) {

  $em = $this->getDoctrine()->getManager();
  $article = $em->getRepository('AppBundle:Article')->find($id);

  if (!$article) {
    throw $this->createNotFoundException(
    'There are no articles with the following id: ' . $id
    );
  }

  $form = $this->createFormBuilder($article)
    ->add('title', TextType::class)
    ->add('author', TextType::class)
    ->add('body', TextareaType::class)
    ->add('url', TextType::class,
    array('required' => false, 'attr' => array('placeholder' => 'www.example.com')))
    ->add('save', SubmitType::class, array('label' => 'Update'))
    ->getForm();

  $form->handleRequest($request);

  if ($form->isSubmitted()) {

    $article = $form->getData();
    $em->flush();

    return $this->redirect('/view-article/' . $id);

  }

  return $this->render(
    'article/edit.html.twig',
    array('form' => $form->createView())
    );

}

And it will be working! The code is self-explanatory. We could have done so many more things like validating and so on, and we could have refactored to eliminate duplicated code, but for the sake of the example and to keep it simple, it is more than enough.

Please refer to this GitHub repo, for the full code of the tutorial: https://github.com/mirkobenedetti/symfony_tutorial.

Please contact me in person if you have any questions. In addition, you can refer to the Symfony documentation Symfony documentation, which is really well made.

Did you like this post? Please comment here below and share it on your preferred social networks, thank you!

Published by

Mirko Benedetti

Hi there, my name is Mirko Benedetti and I'm a Software Developer. Excellence is what I consider to be our ultimate goal, and passion for technology constantly drives me to it. I began programming self-taught at a very young age; since then I learned a lot and every day I love learning new things. Here is my Linkedin Curriculum, feel free to connect.

4 thoughts on “Easy Symfony Tutorial on CRUD operations”

  1. Hello,
    I have a question for you, what does CRUD stand for?
    Regards

  2. Hello Andrey,
    thanks for your question. Well, CRUD stands for Create, Read, Update and Delete: which are the verbs a database query can execute.
    Bye

Leave a Reply

Give me your opinion, I will be grateful.