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, share it on your preferred social networks or comment here below, thank you!

7 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 main verbs a database query can execute.
    Bye

  3. I want to know, using formbuilder method is an obligation or there are other methods

  4. Many thanks Saif for your question,
    formbuilder is an important feature of Symfony.
    Obviously you can do without it, writing your own HTML, but you would loose important integrations and facilitations of the framewok like the ones with its templating engine and ORM.

    It’s your choice, especially if you have very particular needs, in which case you couldn’t do otherwise.

    Stay tuned with us
    and goodbye,

    Mirko

Leave a Reply

Give me your opinion, I will be grateful.