of Doctrine ORM wie need at least the following libraries: • PHP: >=5.3.9 • MongoDB Server • Symfony: >=2.7 • Doctrine: 2.4.8 • Doctrine MongoDB-ODM: 1.0.3 • Doctrine MongoDB-ODM-Bundle: 3.1.x-dev For installation use composer and your prefered os package manager (e.g. apt-get). In this tutorial we try to parse the Twitter timeline API JSON and save some data in our MongoDB. February 26, 2016 3 c dknx01
have a folder structure like this (ex- cluding the framework folders like vendor, web etc.): APPROOT app cong cong.yml ... src AppBundle ... Document Subdocument OriginalData.php TwitterEntry.php Repository TwitterRepository.php You can see that we've nearly a normal structure with a folder for service and repository. But instead of "Entity" we've a folder called "Document" (the MongoDB equivalent for a table) and one called "Subdocument" (a document inside an other document). February 26, 2016 4 c dknx01
classes, that represent the MongoDB doc- uments. 2.2 'Subdocument' Folder If the MongoDB document has a eld that contains another document I put them into this folder, but that's not necessary it could be also another document. 2.3 Repository Here are all repositories for working (query, update, insert) a document. February 26, 2016 5 c dknx01
separate connection and therefore an extra conguration. 1 # Doctrine Configuration 2 doctrine: 3 dbal: 4 # NORMAL CONFIGURATION E . G . MySQL 5 doctrine_mongodb: 6 connections: 7 default: 8 server: mongodb:// l o c a l h o s t :27017 9 options: { username: " % mongodb_user % " , p as sw or d : " % mongodb_password % " } 10 default_database: t e s t 11 document_managers: 12 default: 13 auto_mapping: ' true ' Listing 1: Cong connection February 26, 2016 6 c dknx01
like. We try to parse the Twitter timeline API JSON response into our database. The whole class including the Setter and Getter methods can be seen in section 4.4. The whole subdocument can be seen in section 4.5. 4.1 Use statements We has to use MongoDb-Mapping-Annotation, so that Doctrine knows how to map PHP behaviour into MongoDB. 1 use Doctrine \ODM\MongoDB\Mapping\ Annotations as MongoDB; Listing 2: Needed annotation 4.2 (Sub)Document annotation 4.2.1 Document Doctrine has to know that this class is a MongoDB document and to which repository it belong, if we had one. 1 /∗∗ 2 ∗ @MongoDB\Document( repositoryClass="TwitterBundle\ Repository \ TwitterRepository ") 3 ∗/ Listing 3: Document annotation 4.2.2 Subdocument Doctrine has to know that this class is a MongoDB subdocument. 1 /∗∗ 2 ∗ @MongoDB\EmbeddedDocument 3 ∗/ Listing 4: Subdocument annotation 4.3 Property annotations 4.3.1 Mongo ObjectId Each objeect in MongoDB has it's own unique object id, which we should use to reference to it (like a primary key). So we tell Doctrine that this property February 26, 2016 7 c dknx01
4), to map it on eld '_id' (line 5) and to let the database take care about how to generate it (line 5). 1 /∗∗ 2 ∗ @var s t r i n g 3 ∗ @MongoDB\ String 4 ∗ @MongoDB\ ObjectId 5 ∗ @MongoDB\Id (name="_id " , strategy="Auto") 6 ∗/ 7 protected $id ; Listing 5: Object_Id annotations 4.3.2 Unique elds We have a eld that contains unique value. So Doctrine has to know about the data type string (line 3) and that it is an unique id (line 4). The eld name is the same as the property name. 1 /∗∗ 2 ∗ @var s t r i n g 3 ∗ @MongoDB\ String 4 ∗ @MongoDB\UniqueIndex 5 ∗/ 6 protected $twitterId ; Listing 6: Unique eld annotations 4.3.3 Strings and Integer elds We have one eld that contains normal strings and one that contains normal integer values. The eld name is the same as the property name. 1 /∗∗ 2 ∗ @var s t r i n g 3 ∗ @MongoDB\ String 4 ∗/ 5 protected $text ; 6 7 /∗∗ 8 ∗ @var int 9 ∗ @MongoDB\ Int 10 ∗/ 11 protected $retweetCount ; Listing 7: String and integer eld annotations February 26, 2016 8 c dknx01
PHP DateTime object that should (or bet- ter must) be save as an MongoDate object inside MongoDB. All conversion are made by the driver. The eld name is the same as the property name. 1 /∗∗ 2 ∗ @var DateTime 3 ∗ @MongoDB\Date 4 ∗/ 5 protected $createdAt ; Listing 8: DateTime eld annotations 4.3.5 Boolean elds We have one eld that contains a boolean value. If this value is NULL it's also treated like false and a null or not set value in the database is treated like false. The eld name is the same as the property name. 1 /∗∗ 2 ∗ @var boolean 3 ∗ @MongoDB\Boolean 4 ∗/ 5 protected $pinned ; Listing 9: Boolean eld annotations 4.3.6 Subdocument elds We have one eld holds a lot of other data or in other word that is an object inside this one. These are called subdocuments an represented like an external object (line 3). The eld name is the same as the property name. 1 /∗∗ 2 ∗ @var OriginalData 3 ∗ @MongoDB\EmbedOne( targetDocument="TwitterBundle\Document\ Subdocument\ OriginalData ") 4 ∗/ 5 protected $originalData ; Listing 10: Subdocument annotations 4.3.7 Array elds In the subdocument we've one eld that holds an array. This eld can be used in PHP like a normal array. So we can store multiple values for one eld. February 26, 2016 9 c dknx01
4 ∗/ 5 protected $mentions = array () ; Listing 11: Array annotations 4.4 The whole document class 1 <?php 2 3 namespace TwitterBundle\Document ; 4 5 use TwitterBundle\Document\Subdocument\ OriginalData ; 6 use DateTime ; 7 use Doctrine \ODM\MongoDB\Mapping\ Annotations as MongoDB; 8 9 /∗∗ 10 ∗ @MongoDB\Document( repositoryClass="TwitterBundle\ Repository \ TwitterRepository ") 11 ∗/ 12 c l a s s TwitterEntry 13 { 14 /∗∗ 15 ∗ @var s t r i n g 16 ∗ @MongoDB\ String 17 ∗ @MongoDB\ ObjectId 18 ∗ @MongoDB\Id (name="_id " , strategy="Auto") 19 ∗/ 20 protected $id ; 21 22 /∗∗ 23 ∗ @var s t r i n g 24 ∗ @MongoDB\ String 25 ∗ @MongoDB\UniqueIndex 26 ∗/ 27 protected $twitterId ; 28 29 /∗∗ 30 ∗ @var s t r i n g 31 ∗ @MongoDB\ String 32 ∗/ 33 protected $text ; 34 35 /∗∗ 36 ∗ @var int 37 ∗ @MongoDB\ Int February 26, 2016 10 c dknx01
∗ @var DateTime 43 ∗ @MongoDB\Date 44 ∗/ 45 protected $createdAt ; 46 47 /∗∗ 48 ∗ @var boolean 49 ∗ @MongoDB\Boolean 50 ∗/ 51 protected $pinned ; 52 53 /∗∗ 54 ∗ @var OriginalData 55 ∗ @MongoDB\EmbedOne( targetDocument="TwitterBundle\Document\ Subdocument\ OriginalData ") 56 ∗/ 57 protected $originalData ; 58 59 /∗∗ 60 ∗ @return s t r i n g 61 ∗ @MongoDB\ String 62 ∗/ 63 public function getTwitterId () 64 { 65 return $this −>twitterId ; 66 } 67 68 /∗∗ 69 ∗ @param s t r i n g $twitterId 70 ∗/ 71 public function setTwitterId ( $twitterId ) 72 { 73 $this −>twitterId = $twitterId ; 74 } 75 76 /∗∗ 77 ∗ @return s t r i n g 78 ∗/ 79 public function getText () 80 { 81 return $this −>text ; 82 } 83 84 /∗∗ 85 ∗ @param s t r i n g $text February 26, 2016 11 c dknx01
application we need of course a repository. The whole class can be seen in section 5.7. 5.1 Saving a document Saving a document is actually like a saving a normal MySQL entry. 1 /∗∗ 2 ∗ @param TwitterEntry $twitterEntry 3 ∗/ 4 public function save ( TwitterEntry $twitterEntry ) 5 { 6 $this −>dm−>p e r s i s t ( $twitterEntry ) ; 7 $this −>dm−>f l u s h ( $twitterEntry ) ; 8 } Listing 14: Saving document 5.2 Find by a eld Finding an entry by a eld is also not hard (herewe use our unique key eld). 1 /∗∗ 2 ∗ @param s t r i n g $id 3 ∗ @return null | TwitterEntry 4 ∗/ 5 public function findByTwitterId ( $id ) 6 { 7 return $this −>findOneBy ( array ( ' twitterId ' => $id ) ) ; 8 } Listing 15: Find by eld 5.3 Find with limit, skipping, sorting and one condition - part 1 Finding an entry by a eld is also not hard (here we use our unique key eld). We want all entries that have not set eld 'deleted' or where this eld is 'false' (line 10). We also can pass an limit (line 12) and skipping or starting value (line 11) (in SQL: LIMIT 10,5) and we want so sort it by 'twitterId' DESC (line 13). 1 /∗∗ 2 ∗ @param int $l imit February 26, 2016 17 c dknx01
∗/ 6 public function findWithLimit ( $ limit = 50 , $skip = 0) 7 { 8 $qb = $this −>createQueryBuilder () ; 9 /∗∗ @var Cursor $ r e s u l t ∗/ 10 $ r e s u l t = $qb−>f i e l d ( ' deleted ' )−>notEqual ( true ) 11 −>skip ( $skip ) 12 −>l i m i t ( $l imit ) 13 −>sort ( ' twitterId ' , −1) 14 −>getQuery () 15 −>execute () ; 16 return $result −>hydrate () ; 17 } Listing 16: More complex nding - part 1 5.4 Find with limit, skipping, sorting and one condition - part 2 This example is nearly the same than the previous one, beside that the eldname is dynamic and we want this eld as 'true' or 'set' (line 11). 1 /∗∗ 2 ∗ @param s t r i n g $ f i e l d 3 ∗ @param int $l imit 4 ∗ @param int $skip 5 ∗ @return Cursor 6 ∗/ 7 public function findByBooleanFieldWithLimit ( $ f i e l d , $limit = 50 , $skip = 0) 8 { 9 $qb = $this −>createQueryBuilder () ; 10 /∗∗ @var Cursor $ r e s u l t ∗/ 11 $ r e s u l t = $qb−>f i e l d ( $ f i e l d )−>equals ( true ) 12 −>skip ( $skip ) 13 −>l i m i t ( $l imit ) 14 −>sort ( ' twitterId ' , −1) 15 −>getQuery () 16 −>execute () ; 17 return $result −>hydrate () ; 18 } Listing 17: More complex nding - part 2 February 26, 2016 18 c dknx01
get the 'twitterId' from the last inserted entry based on our unique eld 'twitterId'. So we just want one eld in the result (line 9). 1 /∗∗ 2 ∗ @return s t r i n g 3 ∗ @throws \ Doctrine \ODM\MongoDB\MongoDBException 4 ∗/ 5 public function getLastId () 6 { 7 $qb = $this −>createQueryBuilder () ; 8 /∗∗ @var Cursor $ r e s u l t ∗/ 9 $ r e s u l t = $qb−>s e l e c t ( ' twitterId ' ) 10 −>l i m i t (1) 11 −>sort ( ' twitterId ' , −1) 12 −>getQuery () 13 −>execute () ; 14 return $result −>getNext ()−>getTwitterId () ; 15 } Listing 18: Get last unique eld value 5.6 Get all entries based on one eld value This example shows how to get all entries where the eld 'from' has exactly the value passed by $username (line 11). 1 /∗∗ 2 ∗ @param s t r i n g $userName 3 ∗ @param int $l imit 4 ∗ @param int $skip 5 ∗ @return Cursor 6 ∗/ 7 public function findByUserName ( $userName , $limit , $skip ) 8 { 9 $qb = $this −>createQueryBuilder () ; 10 /∗∗ @var Cursor $ r e s u l t ∗/ 11 $ r e s u l t = $qb−>f i e l d ( ' from ' )−>equals ( $userName ) 12 −>skip ( $skip ) 13 −>l i m i t ( $l imit ) 14 −>sort ( ' twitterId ' , −1) 15 −>getQuery () 16 −>execute () ; 17 return $result −>hydrate () ; Listing 19: Get all entries based on one eld value February 26, 2016 19 c dknx01
TwitterBundle\ Repository ; 4 5 use TwitterBundle\Document\ TwitterEntry ; 6 use Doctrine \ODM\MongoDB\Cursor ; 7 use Doctrine \ODM\MongoDB\DocumentRepository ; 8 9 c l a s s TwitterRepository extends DocumentRepository 10 { 11 12 /∗∗ 13 ∗ @param TwitterEntry $twitterEntry 14 ∗/ 15 public function save ( TwitterEntry $twitterEntry ) 16 { 17 $this −>dm−>p e r s i s t ( $twitterEntry ) ; 18 $this −>dm−>f l u s h ( $twitterEntry ) ; 19 } 20 21 /∗∗ 22 ∗ @param s t r i n g $id 23 ∗ @return null | TwitterEntry 24 ∗/ 25 public function findByTwitterId ( $id ) 26 { 27 return $this −>findOneBy ( array ( ' twitterId ' => $id ) ) ; 28 } 29 30 /∗∗ 31 ∗ @param int $l imit 32 ∗ @param int $skip 33 ∗ @return Cursor 34 ∗/ 35 public function findWithLimit ( $ limit = 50 , $skip = 0) 36 { 37 $qb = $this −>createQueryBuilder () ; 38 /∗∗ @var Cursor $ r e s u l t ∗/ 39 $ r e s u l t = $qb−>f i e l d ( ' deleted ' )−>notEqual ( true ) 40 −>skip ( $skip ) 41 −>l i m i t ( $l imit ) 42 −>sort ( ' twitterId ' , −1) 43 −>getQuery () 44 −>execute () ; 45 return $result −>hydrate () ; 46 } 47 February 26, 2016 20 c dknx01
g $ f i e l d 50 ∗ @param int $l imit 51 ∗ @param int $skip 52 ∗ @return Cursor 53 ∗/ 54 public function findByBooleanFieldWithLimit ( $ f i e l d , $limit = 50 , $skip = 0) 55 { 56 $qb = $this −>createQueryBuilder () ; 57 /∗∗ @var Cursor $ r e s u l t ∗/ 58 $ r e s u l t = $qb−>f i e l d ( $ f i e l d )−>equals ( true ) 59 −>skip ( $skip ) 60 −>l i m i t ( $l imit ) 61 −>sort ( ' twitterId ' , −1) 62 −>getQuery () 63 −>execute () ; 64 return $result −>hydrate () ; 65 } 66 67 /∗∗ 68 ∗ @return s t r i n g 69 ∗ @throws \ Doctrine \ODM\MongoDB\MongoDBException 70 ∗/ 71 public function getLastId () 72 { 73 $qb = $this −>createQueryBuilder () ; 74 /∗∗ @var Cursor $ r e s u l t ∗/ 75 $ r e s u l t = $qb−>s e l e c t ( ' twitterId ' ) 76 −>l i m i t (1) 77 −>sort ( ' twitterId ' , −1) 78 −>getQuery () 79 −>execute () ; 80 return $result −>getNext ()−>getTwitterId () ; 81 } 82 83 /∗∗ 84 ∗ @param s t r i n g $userName 85 ∗ @param int $l imit 86 ∗ @param int $skip 87 ∗ @return Cursor 88 ∗/ 89 public function findByUserName ( $userName , $limit , $skip ) 90 { 91 $qb = $this −>createQueryBuilder () ; 92 /∗∗ @var Cursor $ r e s u l t ∗/ 93 $ r e s u l t = $qb−>f i e l d ( ' from ' )−>equals ( $userName ) 94 −>skip ( $skip ) 95 −>l i m i t ( $l imit ) February 26, 2016 21 c dknx01