Categories
PHP Programming Symfony Tutorials Web Dev

Symfony 4: a CRUD tutorial, second edition

This tutorial follows another guide that I made, always about Symfony, but this time we’re talking about Symfony 4.

As the framework evolves into new releases there might be some changes in methods, classes and tools. They may become deprecated or obsolete and new tools may be added, although some kind of retrocompatibility may be assured.

Symfony the web framework

If you want to know more about that, you can visit Symfony’s roadmap and discover how it will evolve and how long the various versions will be maintained.

As a prerequisite for this tutorial I advice you to install Xampp as environment for the application and a good editor such as Visual Studio Code.

The first thing to do after installing Xampp is to create a Virtual Host and edit your hosts file. Let’s suppose we’re on Windows, but the same may apply on Linux and Mac, with some slight changes.

Let’s edit our ‘C:\xampp\apache\conf\extra\httpd-vhosts.conf‘ and add the following lines:

<VirtualHost blog.local:80>
    ##ServerAdmin webmaster@dummy-host2.example.com
    DocumentRoot "C:\xampp\htdocs\blog\public"
    ServerName blog.local
	
    <Directory "C:\xampp\htdocs\blog\public">
        AllowOverride None
        Require all granted
        Allow from All

        <IfModule mod_rewrite.c>
            Options -MultiViews
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^(.*)$ index.php [QSA,L]
        </IfModule>
    </Directory>	
	
    ##ErrorLog "logs/dummy-host2.example.com-error.log"
    ##CustomLog "logs/dummy-host2.example.com-access.log" common
</VirtualHost>

And open ‘C:\Windows\System32\drivers\etc\hosts‘ inside your Notepad, running it as administrator and pasting the following line:

127.0.0.1	blog.local

Next we can setup our project and install all the necessary dependencies, do it like this at your shell:

C:\Users\benedict>cd C:\xampp\htdocs
C:\xampp\htdocs>composer create-project symfony/skeleton blog

C:\xampp\htdocs>cd blog

C:\xampp\htdocs\blog>composer require symfony/orm-pack
C:\xampp\htdocs\blog>composer require --dev symfony/maker-bundle
C:\xampp\htdocs\blog>composer require symfony/twig-bundle
C:\xampp\htdocs\blog>composer require symfony/translation
C:\xampp\htdocs\blog>composer require symfony/form

Take a look at the output of the ‘create-project‘ command and make sure you’ve installed the 4.3.x version of the framework. We can now run our Xampp Apache and see the Symfony welcome page opening your browser at ‘http://blog.local/‘.

For our purposes we have to edit the ‘.env‘ file at the root of your project and modify the subsequent line of code, to make it look like this:

DATABASE_URL="mysql://root:yourpassword@127.0.0.1:3306/blog"

We can now create our database, which will be called ‘blog‘:

C:\xampp\htdocs\blog>php bin/console doctrine:database:create

Let’s create an entity, with four fields ‘title‘, ‘author‘, ‘body‘, ‘url‘. They are all of the ‘string‘ type, they’re relative lenghts are ‘100‘, ‘50‘, ‘1000‘, ‘200‘ and the last field is ‘nullable‘. Type those values when asked by the following command:

C:\xampp\htdocs\blog>php bin/console make:entity

and it will generate:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
 */
class Article
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

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

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

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

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getAuthor(): ?string
    {
        return $this->author;
    }

    public function setAuthor(string $author): self
    {
        $this->author = $author;

        return $this;
    }

    public function getBody(): ?string
    {
        return $this->body;
    }

    public function setBody(string $body): self
    {
        $this->body = $body;

        return $this;
    }

    public function getUrl(): ?string
    {
        return $this->url;
    }

    public function setUrl(?string $url): self
    {
        $this->url = $url;

        return $this;
    }
}

As you can see it is also generating all the getters and settes. And now we’re going to make a database ‘migration‘ and run it, the result is a database table with all the entity fields:

C:\xampp\htdocs\blog>php bin/console make:migration
C:\xampp\htdocs\blog>php bin/console doctrine:migrations:migrate

The next step is to create a new controller for our application:

C:\xampp\htdocs\blog>php bin/console make:controller ArticleController

And again, we have to create an ‘ArticleType‘ class, to make up our form. Let’s do it with the following command:

C:\xampp\htdocs\blog>php bin/console make:form

Let’s see what we created (our ‘ArticleController.php‘ and ‘ArticleType.php‘) and how to modify those files to make the application work, you’ll see the complete code here:

<?php

namespace App\Controller;

use App\Entity\Article;
use App\Form\ArticleType;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;

class ArticleController extends AbstractController
{
    public function createArticle(Request $request)
    {
        $article = new Article();
        $form = $this->createForm(ArticleType::class, $article);

        $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(
            'edit.html.twig',
            array('form' => $form->createView())
        );

    }

    public function viewArticle($id)
    {
        $article = $this->getDoctrine()
            ->getRepository('App\Entity\Article')
            ->find($id);

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

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

    public function showArticles()
    {
        $articles = $this->getDoctrine()
            ->getRepository('App\Entity\Article')
            ->findAll();

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

    public function deleteArticle($id)
    {
        $em = $this->getDoctrine()->getManager();
        $article = $em->getRepository('App\Entity\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');
    }

    public function updateArticle(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();
        $article = $em->getRepository('App\Entity\Article')->find($id);

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

        $form = $this->createForm(ArticleType::class, $article);

        $form->handleRequest($request);

        if ($form->isSubmitted()) {
            $article = $form->getData();
            $em->flush();
            return $this->redirect('/view-article/' . $id);
        }

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

and then:

<?php

namespace App\Form;

use App\Entity\Article;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

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

class ArticleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', TextType::class)
            ->add('author', TextType::class)
            ->add('body', TextareaType::class)
            ->add('url', TextType::class,
                    ['required' => false, 'attr' => ['placeholder' => 'www.example.com']])
            ->add('save', SubmitType::class,
                    ['label' => 'Save Article'])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Article::class,
        ]);
    }
}

One thing I wanted to make different from the previous tutorial is the way I’m handling routes. This time we will use ‘config/routes.yaml‘ instead of annotations, you will find them missing in the controller class.

create:
  path: /create-article
  controller: App\Controller\ArticleController::createArticle

view:
  path: /view-article/{id}
  controller: App\Controller\ArticleController::viewArticle

show:
  path: /show-articles
  controller: App\Controller\ArticleController::showArticles

delete:
  path: /delete-article/{id}
  controller: App\Controller\ArticleController::deleteArticle

update:
  path: /update-article/{id}
  controller: App\Controller\ArticleController::updateArticle

The last thing to remember is our Twig templates, you will find them on this repo, I didn’t include them here for brevity.

Another difference you may have noticed from the previous tutorial is the use of a separate class for building our form. That allows us to remove clutter from the controller class, making it more readable. And we will be able to reuse the form class code around all the application, should we need to.

The rest of the code is similar and reflects also a different directory structure of Symfony 4 compared to the 3rd version.

And to wrap up, you learned how to build a minimal CRUD Symfony 4 web application. Contact me, should you have any question. Otherwise please follow my GitHub profile for the code of this tutorial and all the others. See you next time.

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

By Mirko Benedetti

Hi there. My name is Mirko Benedetti, I'm a Software Developer and I founded this website. 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 enjoy learning new things. Here is my Linkedin Curriculum, feel free to connect.

6 replies on “Symfony 4: a CRUD tutorial, second edition”

I’m sorry,
I don’t like this tutorial.
It does not go deep into the matter and I don’t find it particularly interesting.
One thing though, you talk about many different technologies: seems like you have real passion… try to improve yourself!

My apologies,
my tutorials are meant for beginners,
and there is too little room for deepening here.
I’m only trying to tackle single aspects of languages and frameworks, without going too deep.
I chose a not too general nor too specific format for my tutorials, and thank you for your advice, I’ll be thinking of new topics and new ways of presenting them.

Any further advice from you and from other readers is welcome, and I’ll put it into practice.
Thanks again

I’ll speak to you bluntly:
I’m not particularly fond of internet tutorials, but this one is really well made, I don’t agree with any point that made Alexander.
Keep improving your stuff and I’ll be surely returning to visit your blog.
Thanks

Hello Judy,
thank you too for your support. I’m always eager to help people understand software development and I welcome them to visit this website.
See you

Thanks to the @author, I’ve found this post useful. Hope to return soon & find more stuff like this to benefit 🙂

Thanks Ellie,
It’s for people like you it is rewarding and worth it to blog on the internet.
Keep in touch,

Mirko

Leave a Reply

Give me your opinion, I will be grateful.