In Symfony web framework, model plays an important role. They are the business entities. They are either provided by customers or fetched from back-end database, manipulated according to business rules and persisted back into the database. They are the data presented by Views. Let us learn about models and how they interact with back-end system in this chapter.
We need to map our models to the back-end relational database items to safely and efficiently fetch and persist the models. This mapping can be done with an Object Relational Mapping (ORM) tool. Symfony provides a separate bundle, DoctrineBundle, which integrates Symfony with third party PHP database ORM tool, Doctrine.
By default, Symfony framework doesn't provide any component to work with databases. But, it integrates tightly with Doctrine ORM. Doctrine contains several PHP libraries used for database storage and object mapping.
Following example will help you understand how Doctrine works, how to configure a database and how to save and retrieve the data.
In this example, we will first configure the database and create a Student object, then perform some operations in it.
To do this we need to adhere to the following steps.
Create a Symfony application, dbsample using the following command.
symfony new dbsample
Generally, the database information is configured in “app/config/parameters.yml” file.
Open the file and add the following changes.
parameter.yml
parameters: database_host: 127.0.0.1 database_port: null database_name: studentsdb database_user: <user_name> database_password: <password> mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: null mailer_password: null secret: 037ab82c601c10402408b2b190d5530d602b5809 doctrine: dbal: driver: pdo_mysql host: '%database_host%' dbname: '%database_name%' user: '%database_user%' password: '%database_password%' charset: utf8mb4
Now, Doctrine ORM can connect to the database.
Issue the following command to generate “studentsdb” database. This step is used to bind the database in Doctrine ORM.
php bin/console doctrine:database:create
After executing the command, it automatically generates an empty “studentsdb” database. You can see the following response on your screen.
Created database `studentsdb` for connection named default
Mapping information is nothing but "metadata”. It is a collection of rules that informs Doctrine ORM exactly how the Student class and its properties are mapped to a specific database table.
Well, this metadata can be specified in a number of different formats, including YAML, XML or you can directly pass Student class using annotations. It is defined as follows.
Add the following changes in the file.
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name = "students") */ class Student { /** * @ORM\Column(type = "integer") * @ORM\Id * @ORM\GeneratedValue(strategy = "AUTO") */ private $id; /** * @ORM\Column(type = "string", length = 50) */ private $name; /** * @ORM\Column(type = "text") */ private $address; }
Here, the table name is optional. If the table name is not specified, then it will be determined automatically based on the name of the entity class.
Doctrine creates simple entity classes for you. It helps you build any entity.
Issue the following command to generate an entity.
php bin/console doctrine:generate:entities AppBundle/Entity/Student
Then you will see the following result and the entity will be updated.
Generating entity "AppBundle\Entity\Student" > backing up Student.php to Student.php~ > generating AppBundle\Entity\Student
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="students") */ class Student { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(type = "string", length = 50) */ private $name; /** * @ORM\Column(type = "text") */ private $address; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * * @return Student */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * * @return string */ public function getName() { return $this->name; } /** * Set address * * @param string $address * * @return Student */ public function setAddress($address) { $this->address = $address; return $this; } /** * Get address * * @return string */ public function getAddress() { return $this->address; } }
After creating entities, you should validate the mappings using the following command.
php bin/console doctrine:schema:validate
It will produce the following result −
[Mapping] OK - The mapping files are correct. [Database] FAIL - The database schema is not in sync with the current mapping file
Since we have not created the students table, the entity is out of sync. Let us create the students table using the Symfony command in the next step.
Doctrine can automatically create all the database tables needed for Student entity. This can be done using the following command.
php bin/console doctrine:schema:update --force
After executing the command, you can see the following response.
Updating database schema... Database schema updated successfully! "1" query was executed
This command compares what your database should look like with how it actually looks, and executes the SQL statements needed to update the database schema to where it should be.
Now, again validate the schema using the following command.
php bin/console doctrine:schema:validate
It will produce the following result −
[Mapping] OK - The mapping files are correct. [Database] OK - The database schema is in sync with the mapping files
As seen in the Bind an Entity section, the following command generates all the getters and setters for the Student class.
$ php bin/console doctrine:generate:entities AppBundle/Entity/Student
Now, we have mapped the Student entity to its corresponding Student table. We should now be able to persist Student objects to the database. Add the following method to the StudentController of the bundle.
<?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Response; use AppBundle\Entity\Student; class StudentController extends Controller { /** * @Route("/student/add") */ public function addAction() { $stud = new Student(); $stud->setName('Adam'); $stud->setAddress('12 north street'); $doct = $this->getDoctrine()->getManager(); // tells Doctrine you want to save the Product $doct->persist($stud); //executes the queries (i.e. the INSERT query) $doct->flush(); return new Response('Saved new student with id ' . $stud->getId()); } }
Here, we accessed the doctrine manager using getManager() method through getDoctrine() of base controller and then persist the current object using persist() method of doctrine manager. persist() method adds the command to the queue, but the flush() method does the actual work (persisting the student object).
Create a function in StudentController that will display the student details.
StudentController.php
/** * @Route("/student/display") */ public function displayAction() { $stud = $this->getDoctrine() ->getRepository('AppBundle:Student') ->findAll(); return $this->render('student/display.html.twig', array('data' => $stud)); }
Let’s create a view that points to display action. Move to the views directory and create a file “display.html.twig”. Add the following changes in the file.
display.html.twig
<style> .table { border-collapse: collapse; } .table th, td { border-bottom: 1px solid #ddd; width: 250px; text-align: left; align: left; } </style> <h2>Students database application!</h2> <table class = "table"> <tr> <th>Name</th> <th>Address</th> </tr> {% for x in data %} <tr> <td>{{ x.Name }}</td> <td>{{ x.Address }}</td> </tr> {% endfor %} </table>
You can obtain the result by requesting the URL “http://localhost:8000/student/display” in a browser.
It will produce the following output on screen −
To update an object in StudentController, create an action and add the following changes.
/** * @Route("/student/update/{id}") */ public function updateAction($id) { $doct = $this->getDoctrine()->getManager(); $stud = $doct->getRepository('AppBundle:Student')->find($id); if (!$stud) { throw $this->createNotFoundException( 'No student found for id '.$id ); } $stud->setAddress('7 south street'); $doct->flush(); return new Response('Changes updated!'); }
Now, request the URL “http://localhost:8000/Student/update/1” and it will produce the following result.
It will produce the following output on screen −
Deleting an object is similar and it requires a call to the remove() method of the entity (doctrine) manager.
This can be done using the following command.
/** * @Route("/student/delete/{id}") */ public function deleteAction($id) { $doct = $this->getDoctrine()->getManager(); $stud = $doct->getRepository('AppBundle:Student')->find($id); if (!$stud) { throw $this->createNotFoundException('No student found for id '.$id); } $doct->remove($stud); $doct->flush(); return new Response('Record deleted!'); }