⬆️ ⬇️

Symfony 2 and Doctrine 2

While continuing to learn Symfony 2, I decided to describe the use of the framework in conjunction with Doctrine 2, since this is one of the most frequently asked questions. And it is worth noting Doctrine 2 also underwent major changes in comparison with the 1.x branch. By itself, the Doctrine project is so large that a description of it will probably draw on a small but interesting book. Therefore, I will only very briefly describe an example of using Doctrine 2 in Symfony 2, which allows you to figure out how to run this bundle and do it on the example of a very small and simple application.



About Doctrine 2



For a start, a brief introduction on Doctrine 2. The project is now divided into three packages:



Common - contains common components that are used in other packages.

DBAL is a database access abstraction layer.

ORM - Object Relational Mapping Tools



Doctrine 2 does not have the usual classes * Table and * Record, which were before. This also means that now the model classes do not need to inherit the * Record classes, and the table classes inherit the * Table classes from the Doctrine package. Instead, two new concepts of “Entity” and “EntityManager” are introduced. “Entity” is, roughly speaking, a model object, and “EntityManager” is a class that allows you to manage objects “Entity”, respectively. Interestingly, “Entity” can be any PHP class and should not inherit any classes from the Doctrine package.



In Doctrine 1, the models were described in the Yaml file, but now you can use Yaml, XML or Docblock Annotations for this. In one form or another, Docblock annotations probably had to be used by everyone, for example, to describe methods under a PHPDocumentor. So now you can also describe the metadata of persistent objects.

Example:

Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  1. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  2. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  3. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  4. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  5. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  6. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  7. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  8. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }
  9. Copy Source | Copy HTML <?php /** <br/> * @Entity <br/> * @Table(name="my_model") <br/> */ class MyModel { /** <br/> * @Id @Column(name="id", type="integer") <br/> * @GeneratedValue(strategy="AUTO") <br/> */ private $id ; /** <br/> * @Column(name="title", type="string", length=255) <br/> */ private $title ; }


')

And of course you can read about all this and many other things on the Doctrine website.



We take off



I decided to consider using Doctrine 2 using a very small and very simple application. This will be the Readlater service, which allows you to save links in order to postpone reading something until later. That is, all its functionality is reduced to three simple actions:



But I immediately ran into a small problem - and Doctrine 2 and Symfony 2 are now being actively developed and there is no question of any stability. Moreover, everything changes very often and what works today may not take off tomorrow. Often these are various little things, like transferring code to another directory. Therefore, for this application I decided to freeze the versions of the libraries used. For this, I took:



Having collected it all together, I froze all versions on April 18, 2010 in the form in which they were and laid out with my application on github . So if you're interested, go for it.



Flew



So, the first is briefly about the structure. Approximately this type of directory:



Let's start with configs. In readlater / config / config.yml add:

Copy Source | Copy HTML
  1. kernel. config : ~
  2. web . web : ~
  3. web . templating : ~
  4. doctrine. dbal :
  5. default_connection : default
  6. connections :
  7. default :
  8. driver : PDOMySql
  9. dbname : sfbox2
  10. user : root
  11. password : 123
  12. host : localhost
  13. event_manager_class : Doctrine \ Common \ EventManager
  14. configuration_class : Doctrine \ DBAL \ Configuration
  15. doctrine. orm :
  16. default_entity_manager : default
  17. cache_driver : array
  18. entity_managers :
  19. default :
  20. connection : default


I described the connection to the doctrine.dbal database and the settings for the Doctrine \ ORM doctrine.orm package

I will not describe here the creation of the application kernel, since it is almost the same as what goes into symfony-sandbox.

Therefore, I will go to ReadlaterBundle , which I will put in src / Application . The structure of the bundle is:



In the Entities directory, I will create a single Link class, because I don’t need more. Doctrine 2 by default offers to create Entity in the Entities directory of the bundle, that is, each bundle can have its own set of model classes. By configuring, you can ask Doctrine to look for classes in a directory other than Entities, but I will not do this.

So here’s the Link class code:

Copy Source | Copy HTML
  1. <? php
  2. namespace Application \ ReadlaterBundle \ Entities;
  3. use Doctrine \ ORM \ EntityRepository;
  4. / ** <br/> * @Entity <br/> * @Table (name = "readlater_link") <br/> * /
  5. class Link
  6. {
  7. / ** <br/> * @Id @Column (name = "id", type = "integer") <br/> * @GeneratedValue (strategy = "AUTO") <br/> * /
  8. private $ id ;
  9. / ** <br/> * @Column (name = "url", type = "string", length = 255) <br/> * /
  10. private $ url ;
  11. / ** <br/> * @Column (name = "created_at", type = "datetime") <br/> * /
  12. private $ createdAt ;
  13. / ** <br/> * constructor <br/> * /
  14. public function __construct ()
  15. {
  16. $ this -> createdAt = new \ DateTime ();
  17. }
  18. / ** <br/> * to string <br/> * /
  19. public function __toString ()
  20. {
  21. return $ this -> getUrl ();
  22. }
  23. }


I defined three fields for a class:



And also added two methods that are already clear. As you can see from the example, I used Docblock to describe the model.

Now I executed several commands:



readlater/console doctrine:database:create

readlater/console doctrine:generate:entities

readlater/console doctrine:schema:create



The first team will create a database, the second will generate methods for our class and do some work, and the third will create a table in the database.

It will look something like this:







Now, if we look at our Link class , we will see that it has changed a little, and now it looks like this:

Copy Source | Copy HTML
  1. <? php
  2. namespace Application \ ReadlaterBundle \ Entities;
  3. use Doctrine \ ORM \ EntityRepository;
  4. / ** <br/> * @Entity <br/> * @Table (name = "readlater_link") <br/> * /
  5. class Link
  6. {
  7. / ** <br/> * @Id @Column (name = "id", type = "integer") <br/> * @GeneratedValue (strategy = "AUTO") <br/> * /
  8. private $ id ;
  9. / ** <br/> * @Column (name = "url", type = "string", length = 255) <br/> * /
  10. private $ url ;
  11. / ** <br/> * @Column (name = "created_at", type = "datetime") <br/> * /
  12. private $ createdAt ;
  13. / ** <br/> * constructor <br/> * /
  14. public function __construct ()
  15. {
  16. $ this -> createdAt = new \ DateTime ();
  17. }
  18. / ** <br/> * to string <br/> * /
  19. public function __toString ()
  20. {
  21. return $ this -> getUrl ();
  22. }
  23. / ** <br/> * Get id <br/> * <br/> * @return integer $ id <br/> * /
  24. public function getId ()
  25. {
  26. return $ this -> id;
  27. }
  28. / ** <br/> * Set url <br/> * <br/> * @param string $ url <br/> * /
  29. public function setUrl ( $ url )
  30. {
  31. $ this -> url = $ url ;
  32. }
  33. / ** <br/> * Get url <br/> * <br/> * @return string $ url <br/> * /
  34. public function getUrl ()
  35. {
  36. return $ this -> url;
  37. }
  38. / ** <br/> * Set createdAt <br/> * <br/> * @param datetime $ createdAt <br/> * /
  39. public function setCreatedAt ( $ createdAt )
  40. {
  41. $ this -> createdAt = $ createdAt ;
  42. }
  43. / ** <br/> * Get createdAt <br/> * <br/> * @return datetime $ createdAt <br/> * /
  44. public function getCreatedAt ()
  45. {
  46. return $ this -> createdAt;
  47. }
  48. }


It introduced methods for accessing class properties.



Well now add routing rules to routing.yml

Copy Source | Copy HTML
  1. link_list :
  2. pattern : /
  3. defaults : { _bundle : ReadlaterBundle, _controller : Readlater, _action : index }
  4. add_link :
  5. pattern : / add
  6. defaults : { _bundle : ReadlaterBundle, _controller : Readlater, _action : add }
  7. read_link :
  8. pattern : / read /: id
  9. defaults : { _bundle : ReadlaterBundle, _controller : Readlater, _action : read }


From the code I think everything is clear, there are 3 rules, listing, adding and deleting.



Now, finally, the controller, where you can and see how to work with objects running Doctrine. Immediately quote the entire code:

Copy Source | Copy HTML
  1. <? php
  2. namespace Application \ ReadlaterBundle \ Controller;
  3. use Symfony \ Framework \ WebBundle \ Controller,
  4. Application \ ReadlaterBundle \ Entities \ Link,
  5. Doctrine \ ORM \ QueryBuilder;
  6. class ReadlaterController extends Controller
  7. {
  8. public function indexAction ()
  9. {
  10. $ em = $ this -> container-> getDoctrine_ORM_EntityManagerService ();
  11. $ links = $ em -> getRepository ( 'Application \ ReadlaterBundle \ Entities \ Link' ) -> findAll ();
  12. return $ this -> render ( 'ReadlaterBundle: Readlater: index' , array ( 'links' => $ links ));
  13. }
  14. public function addAction ()
  15. {
  16. $ request = $ this -> getRequest ();
  17. $ url = $ request -> request-> get ( 'link' );
  18. $ link = new Link ();
  19. $ link -> setUrl ( $ url );
  20. $ em = $ this -> container-> getDoctrine_ORM_EntityManagerService ();
  21. $ em -> persist ( $ link );
  22. $ em -> flush ();
  23. return $ this -> redirect ( $ this -> generateUrl ( 'link_list' ));
  24. }
  25. public function readAction ( $ id )
  26. {
  27. $ em = $ this -> container-> getDoctrine_ORM_EntityManagerService ();
  28. $ link = $ em -> find ( 'Application \\ ReadlaterBundle \\ Entities \\ Link' , $ id );
  29. $ em -> remove ( $ link );
  30. $ em -> flush ();
  31. return $ this -> redirect ( $ this -> generateUrl ( 'link_list' ));
  32. }
  33. }


A few words about the code.

$em = $this->container->getDoctrine_ORM_EntityManagerService(); so using the DI Container we access the EntityManager

$em->getRepository() - so we get a repository of objects of a certain type, that is, some kind of * Table from Doctrine 1.x.

$em->persist($link); - so we save the object

$em->flush() so we complete the transaction, that is, we actually send the data to the database.

$em->remove($link) - well, and so remove the object.

By the way, if I inherited my controller from Symfony \ Framework \ DoctrineBundle \ Controller \ DoctrineController , then the methods would be available:



That's all about the code, the rest I think is obvious.



But perhaps the question arose if now there are no classes of tables, then where to create methods for extracting model objects. For example, in this case, it would be good for the findAll method to get a list of objects sorted by adding date in descending order. To do this, you must create your own class storage repository for objects of a particular type.

To do this, open our class Link and add to the end of the file:

Copy Source | Copy HTML
  1. / ** <br/> * LinkRepository <br/> * /
  2. class LinkRepository extends EntityRepository
  3. {
  4. public function findAll ()
  5. {
  6. return $ this -> _ em-> createQuery ( 'SELECT l FROM Application \\ ReadlaterBundle \\ Entities \\ Link l ORDER BY l.createdAt DESC' ) -> getResult ();
  7. }
  8. }


this will be the repository.

Now let's say which repository to use for the Link class, for this we will slightly change Docblock and the Link class

Copy Source | Copy HTML
  1. / ** <br/> * @Entity (repositoryClass = "Application \ ReadlaterBundle \ Entities \ LinkRepository") <br/> * @Table (name = "readlater_link") <br/> * /
  2. class Link
  3. {
  4. // ...
  5. }


That's all. Now the list of links will be sorted in the right order and it will look something like this:





I did not give the template code, but all this can be viewed in the source code on github

as well as related links:

Source: https://habr.com/ru/post/91565/



All Articles