03-Basic-CRUD.txt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. = Basic C.R.U.D. Operations =
  2. [[PageOutline]]
  3. In this chapter, you will learn how to perform basic C.R.U.D. (Create, Retrieve, Update, Delete) operations on your database using Propel.
  4. == Creating Rows ==
  5. To add new data to the database, instantiate a Propel-generated object and then call the `save()` method. Propel will generate the appropriate INSERT SQL from the instantiated object.
  6. But before saving it, you probably want to define the value for its columns. For that purpose, Propel has generated a `setXXX()` method for each of the columns of the table in the model object. So in its simplest form, inserting a new row looks like the following:
  7. {{{
  8. #!php
  9. <?php
  10. /* initialize Propel, etc. */
  11. $author = new Author();
  12. $author->setFirstName('Jane');
  13. $author->setLastName('austen');
  14. $author->save();
  15. }}}
  16. The column names used in the `setXXX()` methods correspond to the `phpName` attribute of the `<column>` tag in your schema, or to a CamelCase version of the column name if the `phpName` is not set.
  17. In the background, the call to `save()` results in the following SQL being executed on the database:
  18. {{{
  19. #!sql
  20. INSERT INTO author (first_name, last_name) VALUES ('Jane', 'Austen');
  21. }}}
  22. == Reading Object Properties ==
  23. Propel maps the columns of a table into properties of the generated objects. For each property, you can use a generated getter to access it.
  24. {{{
  25. #!php
  26. <?php
  27. echo $author->getId(); // 1
  28. echo $author->getFirstName(); // 'Jane'
  29. echo $author->getLastName(); // 'austen'
  30. }}}
  31. The `id` column was set automatically by the database, since the `schema.xml` defines it as an `autoIncrement` column. The value is very easy to retrieve once the object is saved: just call the getter on the column phpName.
  32. These calls don't issue a database query, since the `Author` object is already loaded in memory.
  33. == Retrieving Rows ==
  34. Retrieving objects from the database, also referred to as ''hydrating'' objects, is essentially the process of executing a SELECT query against the database and populating a new instance of the appropriate object with the contents of each returned row.
  35. In Propel, you use the generated Query objects to select existing rows from the database.
  36. === Retrieving by Primary Key ===
  37. The simplest way to retrieve a row from the database, is to use the generated `findPK()` method. It simply expects the value of the primary key of the row to be retrieved.
  38. {{{
  39. #!php
  40. <?php
  41. $q = new AuthorQuery();
  42. $firstAuthor = $q->findPK(1);
  43. // now $firstBook is an Author object, or NULL if no match was found.
  44. }}}
  45. This issues a simple SELECT SQL query. For instance, for MySQL:
  46. {{{
  47. #!sql
  48. SELECT author.id, author.first_name, author.last_name
  49. FROM `author`
  50. WHERE author.id = 1
  51. LIMIT 1;
  52. }}}
  53. When the primary key consists of more than one column, `findPK()` accepts multiple parameters, one for each primary key column.
  54. '''Tip''': Every generated Query objects offers a factory method called `create()`. This methods creates a new instance of the query, and allows you to write queries in a single line:
  55. {{{
  56. #!php
  57. <?php
  58. $firstAuthor = AuthorQuery::create()->findPK(1);
  59. }}}
  60. You can also select multiple objects based on their primary keys, by calling the generated `findPKs()` method. It takes an array of primary keys as a parameter:
  61. {{{
  62. #!php
  63. <?php
  64. $selectedAuthors = AuthorQuery::create()->findPKs(array(1,2,3,4,5,6,7));
  65. // $selectedAuthors is a collection of Author objects
  66. }}}
  67. === Querying the Database ===
  68. To retrieve rows other than by the primary key, use the Query's `find()` method.
  69. An empty Query object carries no condition, and returns all the rows of the table
  70. {{{
  71. #!php
  72. <?php
  73. $authors = AuthorQuery::create()->find();
  74. // $authors contains a collection of Author objects
  75. // one object for every row of the author table
  76. foreach($authors as $author) {
  77. echo $author->getFirstName();
  78. }
  79. }}}
  80. To add a simple condition on a given column, use one of the generated `filterByXXX()` methods of the Query object, where `XXX` is a column phpName. Since `filterByXXX()` methods return the current query object, you can continue to add conditions or end the query with the result of the method call. For instance, to filter by first name:
  81. {{{
  82. #!php
  83. <?php
  84. $authors = AuthorQuery::create()
  85. ->filterByFirstName('Jane')
  86. ->find();
  87. }}}
  88. When you pass a value to a `filterByXXX()` method, Propel uses the column type to escape this value in PDO. This protects you from SQL injection risks.
  89. You can also easily limit and order the results on a query. Once again, the Query methods return the current Query object, so you can easily chain them:
  90. {{{
  91. #!php
  92. <?php
  93. $authors = AuthorQuery::create()
  94. ->orderByLastName()
  95. ->limit(10)
  96. ->find();
  97. }}}
  98. `find()` always returns a collection of objects, even if there is only one result. If you know that you need a single result, use `findOne()` instead of `find()`. It will add the limit and return a single object instead of an array:
  99. {{{
  100. #!php
  101. <?php
  102. $author = AuthorQuery::create()
  103. ->filterByFirstName('Jane')
  104. ->findOne();
  105. }}}
  106. '''Tip''': Propel provides magic methods for this simple use case. So you can write the above query as:
  107. {{{
  108. #!php
  109. <?php
  110. $author = AuthorQuery::create()->findOneByFirstName('Jane');
  111. }}}
  112. The Propel Query API is very powerful. The next chapter will teach you to use it to add conditions on related objects. If you can't wait, jump to the [wiki:Documentation/1.5/ModelCriteria Query API reference].
  113. === Using Custom SQL ===
  114. The `Query` class provides a relatively simple approach to constructing a query. Its database neutrality and logical simplicity make it a good choice for expressing many common queries. However, for a very complex query, it may prove more effective (and less painful) to simply use a custom SQL query to hydrate your Propel objects.
  115. As Propel uses PDO to query the underlying database, you can always write custom queries using the PDO syntax. For instance, if you have to use a sub-select:
  116. {{{
  117. #!php
  118. <?php
  119. $con = Propel::getConnection(BookPeer::DATABASE_NAME);
  120. $sql = "SELECT * FROM book WHERE id NOT IN "
  121. ."(SELECT book_review.book_id FROM book_review"
  122. ." INNER JOIN author ON (book_review.author_id=author.ID)"
  123. ." WHERE author.last_name = :name)";
  124. $stmt = $con->prepare($sql);
  125. $stmt->execute(array(':name' => 'Tolstoy');
  126. }}}
  127. With only a little bit more work, you can also populate `Book` objects from the resulting statement. Create a new `PropelObjectCollection` for the `Book` model, and call the `format()` method using the statement:
  128. {{{
  129. #!php
  130. <?php
  131. $coll = new PropelObjectCollection();
  132. $coll->setModelName('Book');
  133. $books = $coll->format($stmt);
  134. // $books contains a collection of Book objects
  135. }}}
  136. There are a few important things to remember when using custom SQL to populate Propel:
  137. * The resultset columns must be numerically indexed
  138. * The resultset must contain all columns in the object
  139. * The resultset must have columns ''in the same order'' as they are defined in the `schema.xml` file
  140. == Updating Objects ==
  141. Updating database rows basically involves retrieving objects, modifying the contents, and then saving them. In practice, for Propel, this is a combination of what you've already seen in the previous sections:
  142. {{{
  143. #!php
  144. <?php
  145. $author = AuthorQuery::create()->findOneByFirstName('Jane');
  146. $author->setLastName('Austen');
  147. $author->save();
  148. }}}
  149. Alternatively, you can update several rows based on a Query using the query object's `update()` method:
  150. {{{
  151. #!php
  152. <?php
  153. AuthorQuery::create()
  154. ->filterByFirstName('Jane')
  155. ->update(array('LastName' => 'Austen'));
  156. }}}
  157. This last method is better for updating several rows at once, or if you didn't retrieve the objects before.
  158. == Deleting Objects ==
  159. Deleting objects works the same as updating them. You can either delete an existing object:
  160. {{{
  161. #!php
  162. <?php
  163. $author = AuthorQuery::create()->findOneByFirstName('Jane');
  164. $author->delete();
  165. }}}
  166. Or use the `delete()` method in the query:
  167. {{{
  168. #!php
  169. <?php
  170. AuthorQuery::create()
  171. ->filterByFirstName('Jane')
  172. ->delete();
  173. }}}
  174. '''Tip''': A deleted object still lives in the PHP code. It is marked as deleted and cannot be saved anymore, but you can still read its properties:
  175. {{{
  176. #!php
  177. <?php
  178. echo $author->isDeleted(); // true
  179. echo $author->getFirstName(); // 'Jane'
  180. }}}
  181. == Termination Methods ==
  182. The Query methods that don't return the current query object are called "Termination Methods". You've alread seen come of them: `find()`, `findOne()`, `update()`, `delete()`. There are two more termination methods that you should know about:
  183. {{{
  184. #!php
  185. <?php
  186. // count() returns the number of results of the query.
  187. $nbAuthors = AuthorQuery::create()->count();
  188. // You could also count the number of results from a find(), but that would be less effective,
  189. // since it implies hydrating objects just to count them
  190. // paginate() returns a paginated list of results
  191. $authorPager = AuthorQuery::create()->paginate($page = 1, $maxPerPage = 10);
  192. // This method will compute an offset and a limit
  193. // based on the number of the page and the max number of results per page.
  194. // The result is a PropelModelPager object, over which you can iterate:
  195. foreach ($authorPager as $author) {
  196. echo $author->getFirstName();
  197. }
  198. // a pager object gives more information
  199. echo $pager->getNbResults(); // total number of results if not paginated
  200. echo $pager->haveToPaginate(); // return true if the total number of results exceeds the maximum per page
  201. echo $pager->getFirstIndex(); // index of the first result in the page
  202. echo $pager->getLastIndex(); // index of the last result in the page
  203. $links = $pager->getLinks(5); // array of page numbers around the current page; useful to display pagination controls
  204. }}}
  205. == Collections And On-Demand Hydration ==
  206. The `find()` method of generated Model Query objects returns a `PropelCollection` object. You can use this object just like an array of model objects, iterate over it using `foreach`, access the objects by key, etc.
  207. {{{
  208. #!php
  209. <?php
  210. $authors = AuthorQuery::create()
  211. ->limit(5)
  212. ->find();
  213. foreach ($authors as $author) {
  214. echo $authors->getFirstName();
  215. }
  216. }}}
  217. The advantage of using a collection instead of an array is that Propel can hydrate model objects on demand. Using this feature, you'll never fall short of memory when retrieving a large number of results. Available through the `setFormatter()` method of Model Queries, on-demand hydration is very easy to trigger:
  218. {{{
  219. #!php
  220. <?php
  221. $authors = AuthorQuery::create()
  222. ->limit(50000)
  223. ->setFormatter(ModelCriteria::FORMAT_ON_DEMAND) // just add this line
  224. ->find();
  225. foreach ($authors as $author) {
  226. echo $author->getFirstName();
  227. }
  228. }}}
  229. In this example, Propel will hydrate the `Author` objects row by row, after the `foreach` call, and reuse the memory between each iteration. The consequence is that the above code won't use more memory when the query returns 50,000 results than when it returns 5.
  230. `ModelCriteria::FORMAT_ON_DEMAND` is one of the many formatters provided by the Query objects. You can also get a collection of associative arrays instead of objects, if you don't need any of the logic stored in your model object, by using `ModelCriteria::FORMAT_ARRAY`.
  231. The [wiki:Documentation/1.5/ModelCriteria Query API reference] describes each formatter, and how to use it.
  232. == Propel Instance Pool ==
  233. Propel keeps a list of the objects that you already retrieved in memory to avoid calling the same request twice in a PHP script. This list is called the instance pool, and is automatically populated from your past requests:
  234. {{{
  235. #!php
  236. <?php
  237. // first call
  238. $author1 = AuthorQuery::create()->findPk(1);
  239. // Issues a SELECT query
  240. ...
  241. // second call
  242. $author2 = AuthorQuery::create()->findPk(1);
  243. // Skips the SQL query and returns the existing $author1 object
  244. }}}