Ebooks

Symfony Entity

Symfony Entity tutorial shows how to create entities in Symfony application.

Symfony

Symfony is one of the leading PHP frameworks. It is a set of reusable PHP components and a PHP framework for web projects. Symfony was published as free software in 2005. Symfony was inspired by Django, RoR, and Spring frameworks.

Entity

An entity is a lightweight domain object which is to be persisted. Typically, an entity represents a table in a relational database, and each entity instance corresponds to a row in the table.

A repository is an abstraction of the persistence functionality. It allows to store, retrieve and search for entity objects. In essence, a repository is a collection of entity objects.

Symfony entity example

In the following example, we work with the City entity.

$ symfony new syment
$ cd syment

We create a new Symfony skeleton project and locate to the newly created project directory.

$ php bin/console --version
Symfony 5.0.8 (env: dev, debug: true)

We work with Symfony 5.0.8 version.

$ composer req annot symfony/orm-pack

We install packages for annotations and Doctrine ORM.

$ composer req orm-fixtures maker --dev

We install the fixtures and the maker. Fixtures are used to load a fake set of data into a database and maker to do scaffolding.

.env
DATABASE_URL=sqlite:///%kernel.project_dir%/var/ydb.db

In the .env file, we define an URL to the database. In our case, we use the file-based SQLite.

$ php bin/console doctrine:database:create

With the doctrine:database:create command we create a new database from the provided URL.

$ php bin/console make:entity

With the make entity command, we create a new entity called City. The command creates two files: src/Entity/City.php and src/Repository/CityRepository.php. We add two properties: name of string having 255 characters and a population of integer.

src/Entity/City.php
<?php

namespace App\Entity;

use App\Repository\CityRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=CityRepository::class)
 * @ORM\Table(name="cities")
 */
class City
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="integer")
     */
    private $population;

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getPopulation(): ?int
    {
        return $this->population;
    }

    public function setPopulation(int $population): self
    {
        $this->population = $population;

        return $this;
    }
}

This is the City entity. The entity is mapped to the cities table with the help of the @ORM\Table(name="cities") annotation.

src/Entity/CityRepository.php
<?php

namespace App\Repository;

use App\Entity\City;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method City|null find($id, $lockMode = null, $lockVersion = null)
 * @method City|null findOneBy(array $criteria, array $orderBy = null)
 * @method City[]    findAll()
 * @method City[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class CityRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, City::class);
    }
}

This is the CityRepository. It represents a collection of city objects.

$ php bin/console make:migration

We create a new migration with the make:migration command. Database migrations help us version the changes in the database schema and apply them in a predictable way on every server running the application.

$ php bin/console doctrine:migrations:migrate

We apply the migration, which results in creating the Version20200505154504.php migration file in our case.

src/Migrations/Version20200505154504.php
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20200505154504 extends AbstractMigration
{
    public function getDescription(): string
    {
        return '';
    }

    public function up(Schema $schema): void
    {
      $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'sqlite',
        'Migration can only be executed safely on \'sqlite\'.');

      $this->addSql('CREATE TABLE cities(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
        name VARCHAR(255) NOT NULL, population INTEGER)');
    }

    public function down(Schema $schema): void
    {
      $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'sqlite',
        'Migration can only be executed safely on \'sqlite\'.');

      $this->addSql('DROP TABLE cities');
    }
}

We update the migration file; we have SQL code that creates and drops the cities table.

$ php bin/console make:fixtures

We create fixtures with the make:fixtures command. We call the new fixture CityFixtures. The fixture is used to load fake data into the database.

src/DataFixtures/CityFixtures.php
<?php

namespace App\DataFixtures;

use App\Entity\City;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

class CityFixtures extends Fixture
{
    public function load(ObjectManager $manager)
    {
        $city1 = new City();
        $city1->setName("Bratislava");
        $city1->setPopulation(432000);
        $manager->persist($city1);

        $city2 = new City();
        $city2->setName("Budapest");
        $city2->setPopulation(1759000);
        $manager->persist($city2);

        $city3 = new City();
        $city3->setName("Prague");
        $city3->setPopulation(1280000);
        $manager->persist($city3);

        $city4 = new City();
        $city4->setName("Warsaw");
        $city4->setPopulation(1748000);
        $manager->persist($city4);

        $city5 = new City();
        $city5->setName("Los Angeles");
        $city5->setPopulation(3971000);
        $manager->persist($city5);

        $city6 = new City();
        $city6->setName("New York");
        $city6->setPopulation(8550000);
        $manager->persist($city6);

        $city7 = new City();
        $city7->setName("Edinburgh");
        $city7->setPopulation(464000);
        $manager->persist($city7);

        $city8 = new City();
        $city8->setName("Berlin");
        $city8->setPopulation(3671000);
        $manager->persist($city8);

        $manager->flush();
    }
}

In the CityFixtures, we save eight cities into the table.

$ php bin/console doctrine:fixtures:load

We load the data into the table with the doctrine:fixtures:load command.

src/Repository/CityRepository.php
<?php

namespace App\Repository;

use App\Entity\City;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method City|null find($id, $lockMode = null, $lockVersion = null)
 * @method City|null findOneBy(array $criteria, array $orderBy = null)
 * @method City[]    findAll()
 * @method City[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class CityRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, City::class);
    }
}

CityRepository contains predefined methods for retrieving entity objects from the database. The findall method retrieves all entity objects.

src/Controller/CityController.php
<?php

namespace App\Controller;

use App\Repository\CityRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;


class CityController extends AbstractController
{
    /**
     * @Route("/cities", name="cities")
     * @param CityRepository $cityRepository
     * @return Response
     */
    public function index(CityRepository $cityRepository): Response
    {
        $cities = $cityRepository->findAll();

        if (empty($cities)) {
            return new Response("No data found", Response::HTTP_NOT_FOUND,
                ['content-type' => 'text/plain']);
        }

        return $this->json($cities);
    }
}

The CityController returns all rows from the cities table in a JSON format.

public function index(CityRepository $cityRepository): Response
{

The CityRepository is created via parameter injection.

$cities = $cityRepository->findAll();

With findall we retrieve all cities.

return $this->json($cities);

With the json helper method, we transform the array of cities into JSON format.

$ symfony serve

The web server is started.

$ curl localhost:8000/cities
[{"id":1,"name":"Bratislava","population":432000},
{"id":2,"name":"Budapest","population":1759000},
{"id":3,"name":"Prague","population":1280000},
{"id":4,"name":"Warsaw","population":1748000},
{"id":5,"name":"Los Angeles","population":3971000},
{"id":6,"name":"New York","population":8550000},
{"id":7,"name":"Edinburgh","population":464000},
{"id":8,"name":"Berlin","population":3671000}]

We create a request with the curl command.

In this tutorial we have worked with an entity in a Symfony application.

List all Symfony tutorials.