Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Remove Propel 1.x doc

  • Loading branch information...
commit 755f2e3bbc41d3a541e39644e83946f7a22e0f4b 1 parent 1d4ae9a
@willdurand willdurand authored
Showing with 0 additions and 16,092 deletions.
  1. +0 −21 _includes/footer.html
  2. +0 −17 _includes/navbar.html
  3. +0 −46 _layouts/blog.html
  4. +0 −7 _layouts/default.html
  5. +0 −16 _layouts/documentation.html
  6. +0 −14 _layouts/post.html
  7. +0 −42 _layouts/search.html
  8. +0 −161 behaviors/aggregate-column.markdown
  9. +0 −91 behaviors/alternative-coding-standards.markdown
  10. +0 −249 behaviors/archivable.markdown
  11. +0 −74 behaviors/auto-add-pk.markdown
  12. +0 −204 behaviors/delegate.markdown
  13. +0 −222 behaviors/i18n.markdown
  14. +0 −363 behaviors/nested-set.markdown
  15. +0 −103 behaviors/query-cache.markdown
  16. +0 −134 behaviors/sluggable.markdown
  17. +0 −130 behaviors/soft-delete.markdown
  18. +0 −371 behaviors/sortable.markdown
  19. +0 −114 behaviors/timestampable.markdown
  20. +0 −264 behaviors/versionable.markdown
  21. +0 −68 contribute.markdown
  22. +0 −38 cookbook/adding-additional-sql-files.markdown
  23. +0 −48 cookbook/copying-persisted-objects.markdown
  24. +0 −173 cookbook/customizing-build.markdown
  25. +0 −42 cookbook/dbdesigner.markdown
  26. +0 −179 cookbook/geocodable-behavior.markdown
  27. +0 −70 cookbook/index.markdown
  28. +0 −301 cookbook/multi-component-data-model.markdown
  29. +0 −130 cookbook/namespaces.markdown
  30. +0 −98 cookbook/replication.markdown
  31. +0 −158 cookbook/runtime-introspection.markdown
  32. +0 −8 cookbook/silex/index.markdown
  33. +0 −130 cookbook/silex/working-with-silex.markdown
  34. +0 −189 cookbook/symfony1/how-to-use-Propel i18n-behavior-with-sf1.4.markdown
  35. +0 −195 cookbook/symfony1/how-to-use-old-SfPropelBehaviori18n-with-sf1.4.markdown
  36. +0 −12 cookbook/symfony1/index.markdown
  37. +0 −144 cookbook/symfony1/init-a-Symfony-project-with-Propel-git-way.markdown
  38. +0 −44 cookbook/symfony2/adding-a-new-behavior-in-symfony2.markdown
  39. BIN  cookbook/symfony2/images/basic_form.png
  40. BIN  cookbook/symfony2/images/many_to_many_form.png
  41. BIN  cookbook/symfony2/images/many_to_many_form_with_existing_objects.png
  42. BIN  cookbook/symfony2/images/one_to_many_form.png
  43. BIN  cookbook/symfony2/images/one_to_many_form_with_collection.png
  44. +0 −18 cookbook/symfony2/index.markdown
  45. +0 −535 cookbook/symfony2/mastering-symfony2-forms-with-propel.markdown
  46. +0 −8 cookbook/symfony2/symfony2-and-propel-in-real-life.markdown
  47. +0 −201 cookbook/symfony2/testing.markdown
  48. +0 −73 cookbook/symfony2/the-symfony2-security-component-and-propel.markdown
  49. +0 −711 cookbook/symfony2/working-with-symfony2.markdown
  50. +0 −182 cookbook/testing-your-behaviors.markdown
  51. +0 −66 cookbook/user-contributed-behaviors.markdown
  52. +0 −171 cookbook/using-mssql-server.markdown
  53. +0 −100 cookbook/using-sql-schemas.markdown
  54. +0 −215 cookbook/working-with-advanced-column-types.markdown
  55. +0 −123 cookbook/working-with-existing-databases.markdown
  56. +0 −241 cookbook/working-with-unit-tests.markdown
  57. +0 −432 cookbook/writing-behavior.markdown
  58. +0 −68 css/base_syntax.css
  59. +0 −366 css/layout.css
  60. +0 −125 css/markdown.css
  61. +0 −61 css/mobile.css
  62. +0 −19 css/syntax.css
  63. +0 −183 documentation/01-installation.markdown
  64. +0 −355 documentation/02-buildtime.markdown
  65. +0 −342 documentation/03-basic-crud.markdown
  66. +0 −401 documentation/04-relationships.markdown
  67. +0 −246 documentation/05-validators.markdown
  68. +0 −282 documentation/06-transactions.markdown
  69. +0 −433 documentation/07-behaviors.markdown
  70. +0 −439 documentation/08-logging.markdown
  71. +0 −498 documentation/09-inheritance.markdown
  72. +0 −364 documentation/10-migrations.markdown
  73. +0 −97 documentation/index.markdown
  74. +0 −723 documentation/whats-new.markdown
  75. +0 −80 download.markdown
  76. +0 −83 index.markdown
  77. +0 −5 js/anchorify.min.js
  78. +0 −9 js/ga.js
  79. +0 −12 js/jquery.tableofcontents.min.js
  80. +0 −730 reference/active-record.markdown
  81. +0 −332 reference/buildtime-configuration.markdown
  82. +0 −16 reference/index.markdown
  83. +0 −1,257 reference/model-criteria.markdown
  84. +0 −311 reference/runtime-configuration.markdown
  85. +0 −469 reference/schema.markdown
  86. +0 −40 support.markdown
View
21 _includes/footer.html
@@ -1,21 +0,0 @@
-<div class="bottom">
- <p>
- <ul>
- <li class="element first">
- <a class="link" href="/documentation/">Documentation</a>&nbsp;|&nbsp;
- </li>
- <li class="element">
- <a class="link" href="/support.html">Support</a>&nbsp;|&nbsp;
- </li>
- <li class="element">
- <a class="link" href="/download.html">Download</a>&nbsp;|&nbsp;
- </li>
- <li class="element">
- <a class="link" href="/contribute.html">Contribute</a>&nbsp;|&nbsp;
- </li>
- <li class="last element">
- <a class="link" href="/blog/">Blog</a>
- </li>
- </ul>
- </p>
-</div>
View
17 _includes/navbar.html
@@ -1,17 +0,0 @@
-<ul class="topmenu">
- <li class="element first">
- <a class="link" href="/documentation/">Documentation</a>
- </li>
- <li class="element">
- <a class="link" href="/support.html">Support</a>
- </li>
- <li class="element">
- <a class="link" href="/download.html">Download</a>
- </li>
- <li class="element">
- <a class="link" href="/contribute.html">Contribute</a>
- </li>
- <li class="last element">
- <a class="link" href="/blog/">Blog</a>
- </li>
-</ul>
View
46 _layouts/blog.html
@@ -1,46 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
- <head>
- <title>{% if page.title %}{{ page.title }}{% else %}Blog{% endif %} - Propel</title>
- <meta name="language" content="en" />
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
- <!-- Stylesheets -->
- <link rel="stylesheet" type="text/css" media="all" href="http://yui.yahooapis.com/2.8.0r4/build/reset/reset-min.css" />
- <link rel="stylesheet" type="text/css" media="all" href="/css/layout.css" />
- <link rel="stylesheet" type="text/css" media="all" href="/css/markdown.css" />
- <link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" href="/css/mobile.css" />
- </head>
- <body>
- <div id="fond">
- <div class="layout">
- <ul class="rootnav">
- <li class="element">
- <a class="link" href="/">Propel</a>
- </li>
- </ul>
- {% include navbar.html %}
- <div class="ribbon">
- <a href="https://github.com/propelorm/Propel" rel="me">Fork me on GitHub</a>
- </div>
- <div class="content blog">
- {% if page.title %}
- <h1><a href="/blog/">Blog</a> > {{ page.title }}</h1>
- {% else %}
- <h1>Blog</h1>
- {% endif %}
-
- <div class="markdown">
- {{ content }}
- </div>
-
- <p class="fork_and_edit">
- Found a typo ? Something is wrong in this documentation ? Just <a href="http://github.com/propelorm/propelorm.github.com/edit/master/_posts/{{ page.url | replace: '/blog/', '' | replace: '/', '-' | replace:'.html','' }}.markdown">fork and edit</a> it !
- </p>
- </div>
- </div>
- {% include footer.html %}
- </div>
- <script type="text/javascript" src="/js/ga.js" charset="utf-8"></script>
- </body>
-</html>
View
7 _layouts/default.html
@@ -1,7 +0,0 @@
----
-layout: base
----
-
-<div class="markdown">
- {{ content }}
-</div>
View
16 _layouts/documentation.html
@@ -1,16 +0,0 @@
----
-layout: base
----
-
-<ul class="toc"></ul>
-<div class="markdown">
- {{ content }}
-</div>
-
-<script type="text/javascript" src="/js/anchorify.min.js"></script>
-<script type="text/javascript">
- $(document).ready(function(){
- $('.toc').tableOfContents(null, { startLevel: 2 });
- $('h2, h3, h4, h5, h6').anchorify({ cssClass: 'anchor_link' });
- });
-</script>
View
14 _layouts/post.html
@@ -1,14 +0,0 @@
----
-layout: blog
----
-
-<p class="author">
- {% if page.author %}
- {{ page.author }}
- {% else %}
- The Propel Team
- {% endif %}
- – <span class="date">{{ page.date | date_to_long_string }}</span>
-</p>
-
-{{ content }}
View
42 _layouts/search.html
@@ -1,42 +0,0 @@
----
-layout: base
----
-
-<div class="markdown">
- <div class="doc">
- {{ content }}
- </div>
-
- <div id="searchcontrol">
- </div>
-
- <div id="searchResults">
- </div>
-</div>
-
-<script src="https://www.google.com/jsapi?key=ABQIAAAAFmhC4M8xhUur1W64-vxMwxSVfK-M2qovUimjXgIl-8kDXzTZ-RTxn_kwP8G0SqjoYVjEc8dgl6Kxvw" type="text/javascript"></script>
-<script language="Javascript" type="text/javascript">
- google.load('search', '1');
-
- function getParamValue(param, url) {
- var u = url == undefined ? document.location.href : url;
- var reg = new RegExp('(\\?|&|^)'+param+'=(.*?)(&|$)');
- matches = u.match(reg);
- return matches[2] != undefined ? decodeURIComponent(matches[2]).replace(/\+/g,' ') : '';
- }
- function triggerOnLoad() {
- var searchControl = new google.search.SearchControl();
- var options = new google.search.SearcherOptions();
- var siteSearch = new google.search.WebSearch();
- options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
- options.setRoot(document.getElementById("searchResults"));
- siteSearch.setResultSetSize(google.search.WebSearch.LARGE_RESULTSET);
- siteSearch.setUserDefinedLabel("propelorm.org");
- siteSearch.setUserDefinedClassSuffix("siteSearch");
- siteSearch.setSiteRestriction("propelorm.org");
- searchControl.addSearcher(siteSearch, options);
- searchControl.draw(document.getElementById("searchcontrol"));
- searchControl.execute(getParamValue('q'));
- }
- google.setOnLoadCallback(triggerOnLoad);
-</script>
View
161 behaviors/aggregate-column.markdown
@@ -1,161 +0,0 @@
----
-layout: documentation
-title: Aggregate Column Behavior
----
-
-# Aggregate Column Behavior #
-
-The `aggregate_column` behavior keeps a column updated using an aggregate function executed on a related table.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `aggregate_column` behavior to a table. You must provide parameters for the aggregate column `name`, the foreign table name, and the aggegate `expression`. For instance, to add an aggregate column keeping the comment count in a `post` table:
-
-```xml
-<table name="post">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="aggregate_column">
- <parameter name="name" value="nb_comments" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="COUNT(id)" />
- </behavior>
-</table>
-<table name="comment">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="post_id" type="INTEGER" />
- <foreign-key foreignTable="post" onDelete="cascade">
- <reference local="post_id" foreign="id" />
- </foreign-key>
-</table>
-```
-
-Rebuild your model, and insert the table creation sql again. The model now has an additional `nb_comments` column, of type `integer` by default. And each time an record from the foreign table is added, modified, or removed, the aggregate column is updated:
-
-```php
-<?php
-$post = new Post();
-$post->setTitle('How Is Life On Earth?');
-$post->save();
-echo $post->getNbComments(); // 0
-$comment1 = new Comment();
-$comment1->setPost($post);
-$comment1->save();
-echo $post->getNbComments(); // 1
-$comment2 = new Comment();
-$comment2->setPost($post);
-$comment2->save();
-echo $post->getNbComments(); // 2
-$comment2->delete();
-echo $post->getNbComments(); // 1
-```
-
-The aggregate column is also kept up to date when related records get modified through a Query object:
-
-```php
-<?php
-CommentQuery::create()
- ->filterByPost($post)
- ->delete():
-echo $post->getNbComments(); // 0
-```
-
-## Customizing The Aggregate Calculation ##
-
-Any aggregate function can be used on any of the foreign columns. For instance, you can use the `aggregate_column` behavior to keep the latest update date of the related comments, or the total votes on the comments. You can even keep several aggregate columns in a single table:
-
-```xml
-<table name="post">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="aggregate_column">
- <parameter name="name" value="nb_comments" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="COUNT(id)" />
- </behavior>
- <behavior name="aggregate_column">
- <parameter name="name" value="last_comment" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="MAX(created_at)" />
- </behavior>
- <behavior name="aggregate_column">
- <parameter name="name" value="total_votes" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="SUM(vote)" />
- </behavior>
-</table>
-<table name="comment">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="post_id" type="INTEGER" />
- <foreign-key foreignTable="post" onDelete="cascade">
- <reference local="post_id" foreign="id" />
- </foreign-key>
- <column name="created_at" type="TIMESTAMP" />
- <column name="vote" type="INTEGER" />
-</table>
-```
-
-The behavior adds a `computeXXX()` method to the `Post` class to compute the value of the aggregate function. This method, called each time records are modified in the related `comment` table, is the translation of the behavior settings into a SQL query:
-
-```php
-<?php
-// in om/BasePost.php
-public function computeNbComments(PropelPDO $con)
-{
- $stmt = $con->prepare('SELECT COUNT(id) FROM `comment` WHERE comment.POST_ID = :p1');
- $stmt->bindValue(':p1', $this->getId());
- $stmt->execute();
- return $stmt->fetchColumn();
-}
-```
-
-You can override this method in the model class to customize the aggregate column calculation.
-
-## Additional Condition ##
-
-What if you use your own soft deletion and want to calculate only comments which are not marked as deleted?
-It is possible to add a custom SQL condition:
-
-```xml
-<table name="post">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="aggregate_column">
- <parameter name="name" value="nb_comments" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="COUNT(id)" />
- <parameter name="condition" value="is_deleted = false" />
- </behavior>
-</table>
-```
-
-Which will result in generated SQL query:
-
-```php
-<?php
-// in om/BasePost.php
-public function computeNbComments(PropelPDO $con)
-{
- $stmt = $con->prepare('SELECT COUNT(id) FROM `comment` WHERE is_deleted = false AND comment.POST_ID = :p1');
- $stmt->bindValue(':p1', $this->getId());
- $stmt->execute();
- return $stmt->fetchColumn();
-}
-```
-
-## Customizing The Aggregate Column ##
-
-By default, the behavior adds one columns to the model. If this column is already described in the schema, the behavior detects it and doesn't add it a second time. This can be useful if you need to use a custom `type` or `phpName` for the aggregate column:
-
-```xml
-<table name="post">
- <column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="nb_comments" phpName="CommentCount" type="INTEGER" />
- <behavior name="aggregate_column">
- <parameter name="name" value="nb_comments" />
- <parameter name="foreign_table" value="comment" />
- <parameter name="expression" value="COUNT(id)" />
- </behavior>
-</table>
-```
View
91 behaviors/alternative-coding-standards.markdown
@@ -1,91 +0,0 @@
----
-layout: documentation
-title: Alternative Coding Standards Behavior
----
-
-# Alternative Coding Standards Behavior #
-
-The `alternative_coding_standards` behavior changes the coding standards of the model classes generated by Propel to match your own coding style.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `alternative_coding_standards` behavior to a table:
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="alternative_coding_standards" />
-</table>
-```
-
-Rebuild your model, and you're ready to go. The code of the model classes now uses an alternative set of coding standards:
-
-```php
-<?php
-// in om/BaseBook.php
- /**
- * Set the value of [title] column.
- *
- * @param string $v new value
- * @return Table4 The current object (for fluent API support)
- */
- public function setTitle($v)
- {
- if ($v !== null)
- {
- $v = (string) $v;
- }
-
- if ($this->title !== $v)
- {
- $this->title = $v;
- $this->modifiedColumns[] = BookPeer::TITLE;
- }
-
- return $this;
- }
-
-// instead of
-
- /**
- * Set the value of [title] column.
- *
- * @param string $v new value
- * @return Table4 The current object (for fluent API support)
- */
- public function setTitle($v)
- {
- if ($v !== null) {
- $v = (string) $v;
- }
-
- if ($this->title !== $v) {
- $this->title = $v;
- $this->modifiedColumns[] = BookPeer::TITLE;
- }
-
- return $this;
- } // setTitle()
-```
-
-The behavior replaces tabulations by whitespace (2 spaces by default), places opening brackets on newlines, removes closing brackets comments, and can even strip every comments in the generated classes if you wish.
-
-## Parameters ##
-
-Each of the new coding style rules has corresponding parameter in the behavior description. Here is the default configuration:
-
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="alternative_coding_standards">
- <parameter name="brackets_newline" value="true" />
- <parameter name="remove_closing_comments" value="true" />
- <parameter name="use_whitespace" value="true" />
- <parameter name="tab_size" value="2" />
- <parameter name="strip_comments" value="false" />
- </behavior>
-</table>
-```
-
-You can change these settings to better match your own coding styles.
View
249 behaviors/archivable.markdown
@@ -1,249 +0,0 @@
----
-layout: documentation
-title: Archivable Behavior
----
-
-# Archivable Behavior #
-
-The `archivable` behavior gives model objects the ability to be copied to an archive table. By default, the behavior archives objects on deletion, acting as a replacement of the [`soft_delete`](./soft-delete) behavior, which is deprecated.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `archivable` behavior to a table:
-
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="archivable" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has one new table, `book_archive`, with the same columns as the original `book` table. This table stores the archived `books` together with their archive date. To archive an object, call the `archive()` method:
-
-```php
-<?php
-$book = new Book();
-$book->setTitle('War And Peace');
-$book->save();
-// copy the current Book to a BookArchive object and save it
-$archivedBook = $book->archive();
-```
-
-The archive table contains only the freshest copy of each archived objects. Archiving an object twice doesn't create a new record in the archive table, but updates the existing archive.
-
-The `book_archive` table has generated ActiveRecord and ActiveQuery classes, so you can browse the archive at will. The archived objects have the same primary key as the original objects. In addition, they contain an `ArchivedAt` property storing the date where the object was archived.
-
-```php
-<?php
-// find the archived book
-$archivedBook = BookArchiveQuery::create()->findPk($book->getId());
-echo $archivedBook->getTitle(); // 'War And Peace'
-echo $archivedBook->getArchivedAt(); // 2011-08-23 18:14:23
-```
-
-The ActiveRecord class of an `archivable` model has more methods to deal with the archive:
-
-```php
-// restore an object to the state it had when last archived
-$book->restoreFromArchive();
-// find the archived version of an existing book
-$archivedBook = $book->getArchive();
-// populate a book based on an archive
-$book = new book();
-$book->populateFromArchive($archivedBook);
-```
-
-By default, an `archivable` model is archived just before deletion:
-
-```php
-<?php
-$book = new Book();
-$book->setTitle('Sense and Sensibility');
-$book->save();
-// delete and archive the book
-$book->delete();
-echo BookQuery::create()->count(); // 0
-// find the archived book
-$archivedBook = BookArchiveQuery::create()
- ->findOneByTitle('Sense and Sensibility');
-```
-
->**Tip**<br />The behavior does not take care of archiving the related objects. This may be surprising on deletions if the deleted object has 'ON DELETE CASCADE' foreign keys. If you want to archive relations, override the generated `archive()` method in the ActiveRecord class with your custom logic.
-
-To recover deleted objects, use `populateFromArchive()` on a new object and save it:
-
-```php
-<?php
-// create a new object based on the archive
-$book = new Book();
-$book->populateFromArchive($archivedBook);
-$book->save();
-echo $book->getTitle(); // 'Sense and Sensibility'
-```
-
-If you want to delete an `archivable` object without archiving it, use the `deleteWithoutArchive()` method generated by the behavior:
-
-```php
-<?php
-// delete the book but don't archive it
-$book->deleteWithoutArchive();
-```
-
-## Archiving A Set Of Objects ##
-
-The `archivable` behavior also generates an `archive()` method on the generated ActiveQuery class. That means you can easily archive a set of objects, in the same way you archive a single object:
-
-```php
-<?php
-// archive all books having a title starting with "war"
-$nbArchivedObjects = BookQuery::create()
- ->filterByTitle('War%')
- ->archive();
-```
-
-`archive()` returns the number of archived objects, and not the current ActiveQuery object, so it's a termination method.
-
->**Tip**<br />Since the `archive()` method doesn't duplicate archived objects, it must iterate over the results of the query to check whether each object has already been archived. In practice, `archive()` issues 2n+1 database queries, where `n` is the number of results of the query as returned by a `count()`.
-
-As explained earlier, an `archivable` model is archived just before deletion by default. This is also true when using the `delete()` and `deleteAll()` methods of the ActiveQuery class:
-
-```php
-<?php
-// delete and archive all books having a title starting with "war"
-$nbDeletedObjects = BookQuery::create()
- ->filterByTitle('War%')
- ->delete();
-
-// use deleteWithoutArchive() if you just want to delete
-$nbDeletedObjects = BookQuery::create()
- ->filterByTitle('War%')
- ->deleteWithoutArchive();
-
-// you can also turn off the query alteration on the current query
-// by calling setArchiveOnDelete(false) before deleting
-$nbDeletedObjects = BookQuery::create()
- ->filterByTitle('War%')
- ->setArchiveOnDelete(false)
- ->delete();
-```
-
-## Archiving on Insert, Update, or Delete ##
-
-As explained earlier, the `archivable` behavior archives objects on deletion by default, but insertions and updates don't trigger the `archive()` method. You can disable the auto archiving on deletion, as well as enable it for insertion and update, in the behavior `<parameter>` tags. Here is the default configuration:
-
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="archivable">
- <parameter name="archive_on_insert" value="false" />
- <parameter name="archive_on_update" value="false" />
- <parameter name="archive_on_delete" value="true" />
- </behavior>
-</table>
-```
-
-If you turn on `archive_on_insert`, a call to `save()` on a new ActiveRecord object archives it - unless you call `saveWithoutArchive()`.
-
-If you turn on `archive_on_update`, a call to `save()` on an existing ActiveRecord object archives it, and a call to `update()` on an ActiveQuery object archives the results as well. You can still use `saveWithoutArchive()` on the ActiveRecord class and `updateWithoutArchive()` on the ActiveQuery class to skip archiving on updates.
-
-Of course, even if `archive_on_insert` or any of the similar parameters isn't turned on, you can always archive manually an object after persisting it by simply calling `archive()`:
-
-```php
-<?php
-// create a new object, save it, and archive it
-$book = new Book();
-$book->save();
-$book->archive();
-```
-
-## Archiving To Another Database ##
-
-The behavior can use another database connection for the archive table, to make it safer. To allow cross-database archives, you must declare the archive schema manually in another XML schema, and reference the archive class on in the behavior parameter:
-
-```xml
-<database name="main">
- <table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="archivable">
- <parameter name="archive_class" value="MyBookArchive" />
- </behavior>
- </table>
-</database>
-<database name="backup">
- <table name="my_book_archive" phpName="MyBookArchive">
- <column name="id" required="true" primaryKey="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="archived_at" type="TIMESTAMP" />
- </table>
-</database>
-```
-
-The archive table must have the same columns as the archivable table, but without autoIncrements, and without foreign keys.
-
-With this setup, the behavior uses `MyBookArchive` and `MyBookArchiveQuery` for all operations on archives, and therefore uses the `backup` connection.
-
-## Migrating From `soft_delete` ##
-
-If you use `archivable` as a replacement for the `soft_delete` behavior, here is how you should update your code:
-
-```php
-<?php
-// do a soft delete
-$book->delete(); // with soft_delete
-$book->delete(); // with archivable
-
-// do a hard delete
-// with soft_delete
-$book->forceDelete();
-// with archivable
-$book->deleteWithoutArchive();
-
-// find deleted objects
-// with soft_delete
-$books = BookQuery::create()
- ->includeDeleted()
- ->where('Book.DeletedAt IS NOT NULL')
- ->find();
-// with archivable
-$bookArchives = BookArchiveQuery::create()
- ->find();
-
-// recover a deleted object
-// with soft_delete
-$book->unDelete();
-// with archivable
-$book = new Book();
-$book->populateFromArchive($bookArchive);
-$book->save();
-```
-
-## Additional Parameters ##
-
-You can change the name of the archive table added by the behavior by setting the `archive_table` parameter. If the table doesn't exist, the behavior creates it for you.
-
-```xml
-<behavior name="archivable">
- <parameter name="archive_table" value="special_book_archive" />
-</behavior>
-```
-
->**Tip**<br />The `archive_table` and `archive_class` parameters are mutually exclusive. You can only use either one of the two.
-
-You can also change the name of the column storing the archive date:
-
-```xml
-<behavior name="archivable">
- <parameter name="archived_at_column" value="archive_date" />
-</behavior>
-```
-
-Alternatively, you can disable the addition of an archive date column altogether:
-
-```xml
-<behavior name="archivable">
- <parameter name="log_archived_at" value="false" />
-</behavior>
-```
View
74 behaviors/auto-add-pk.markdown
@@ -1,74 +0,0 @@
----
-layout: documentation
-title: AutoAddPk Behavior
----
-
-# AutoAddPk Behavior #
-
-The `auto_add_pk` behavior adds a primary key columns to the tables that don't have one. Using this behavior allows you to omit the declaration of primary keys in your tables.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `auto_add_pk` behavior to a table:
-
-```xml
-<table name="book">
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="auto_add_pk" />
-</table>
-```
-
-Rebuild your model, and insert the table creation sql. You will notice that the `book` table has two columns and not just one. The behavior added an `id` column, of type integer and autoincremented. This column can be used as any other column:
-
-```php
-<?php
-$b = new Book();
-$b->setTitle('War And Peace');
-$b->save();
-echo $b->getId(); // 1
-```
-
-This behavior is more powerful if you add it to the database instead of a table. That way, it will alter all tables not defining a primary key column - and leave the others unchanged.
-
-```xml
-<database name="bookstore" defaultIdMethod="native">
- <behavior name="auto_add_pk" />
- <table name="book">
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- </table>
-</database>
-```
-
-You can even enable it for all your databases by adding it to the default behaviors in your `build.properties` file:
-
-```ini
-propel.behavior.default = auto_add_pk
-```
-
-## Parameters ##
-
-By default, the behavior adds a column named `id` to the table if the table has no primary key. You can customize all the attributes of the added column by setting corresponding parameters in the behavior definition:
-
-```xml
-<database name="bookstore" defaultIdMethod="native">
- <behavior name="auto_add_pk">
- <parameter name="name" value="identifier" />
- <parameter name="autoIncrement" value="false" />
- <parameter name="type" value="BIGINT" />
- </behavior>
- <table name="book">
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- </table>
-</database>
-```
-
-Once you regenerate your model, the column is now named differently:
-
-```php
-<?php
-$b = new Book();
-$b->setTitle('War And Peace');
-$b->setIdentifier(1);
-$b->save();
-echo $b->getIdentifier(); // 1
-```
View
204 behaviors/delegate.markdown
@@ -1,204 +0,0 @@
----
-layout: documentation
-title: Delegate Behavior
----
-
-# Delegate Behavior #
-
-The `delegate` behavior allows a model to delegate methods to one of its relationships. This helps to isolate logic in a dedicated model, or to simulate [class table inheritance](http://martinfowler.com/eaaCatalog/classTableInheritance.html).
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `delegate` behavior to a table. In the `<parameter>` tag, specify the table that the current table delegates to as the `to` parameter:
-
-```xml
-<table name="account">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="login" type="VARCHAR" required="true" />
- <column name="password" type="VARCHAR" required="true" />
- <behavior name="delegate">
- <parameter name="to" value="profile" />
- </behavior>
-</table>
-<table name="profile">
- <column name="email" type="VARCHAR" />
- <column name="telephone" type="VARCHAR" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The delegate `profile` table is now related to the `account` table using a one-to-one relationship. That means that the behavior creates a foreign primary key in the `profile` table. In fact, everything happens as if you had defined the following schema:
-
-```xml
-<table name="account">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="login" type="VARCHAR" required="true" />
- <column name="password" type="VARCHAR" required="true" />
-</table>
-<table name="profile">
- <column name="id" required="true" primaryKey="true" type="INTEGER" />
- <column name="email" type="VARCHAR" />
- <column name="telephone" type="VARCHAR" />
- <foreign-key foreignTable="account" onDelete="setnull" onUpdate="cascade">
- <reference local="id" foreign="id" />
- </foreign-key>
-</table>
-```
-
->**Tip**<br />If the delegate table already has a foreign key to the main table, the behavior doesn't recreate it. It allows you to have full control over the relationship between the two tables.
-
-In addition, the ActiveRecord `Account` class now provides integrated delegation capabilities. That means that it offers to handle directly the columns of the `Profile` model, while in reality it finds or create a related `Profile` object and calls the methods on this delegate:
-
-```php
-<?php
-$account = new Account();
-$account->setLogin('francois');
-$account->setPassword('S€cr3t');
-
-// Fill the profile via delegation
-$account->setEmail('francois@example.com');
-$account->setTelephone('202-555-9355');
-// same as
-$profile = new Profile();
-$profile->setEmail('francois@example.com');
-$profile->setTelephone('202-555-9355');
-$account->setProfile($profile);
-
-// save the account and its profile
-$account->save();
-
-// retrieve delegated data directly from the main object
-echo $account->getEmail(); // francois@example.com
-```
-
-Getter and setter methods for delegate columns don't exist on the main object ; the delegation is handled by the magical `__call()` method. Therefore, the delegation also works for custom methods in the delegate table.
-
-```php
-<?php
-class Profile extends BaseProfile
-{
- public function setFakeEmail()
- {
- $n = rand(10e16, 10e20);
- $fakeEmail = base_convert($n, 10, 36) . '@example.com';
- $this->setEmail($fakeEmail);
- }
-}
-
-$account = new Account();
-$account->setFakeEmail(); // delegates to Profile::setFakeEmail()
-```
-
-## Delegating Using a Many-To-One Relationship ##
-
-Instead of adding a one-to-one relationship, the `delegate` behavior can take advantage of an existing many-to-one relationship. For instance:
-
-```xml
-<table name="player">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="first_name" type="VARCHAR" />
- <column name="last_name" type="VARCHAR" />
-</table>
-<table name="basketballer">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="points" type="INTEGER" />
- <column name="field_goals" type="INTEGER" />
- <column name="three_points_field_goals" type="INTEGER" />
- <column name="player_id" type="INTEGER" />
- <foreign-key foreignTable="player">
- <reference local="player_id" foreign="id" />
- </foreign-key>
- <behavior name="delegate">
- <parameter name="to" value="player" />
- </behavior>
-</table>
-
-```
-
-In that case, the behavior doesn't modify the foreign keys, it just proxies method called on `Basketballer` to the related `Player`, or creates one if it doesn't exist:
-
-```php
-<?php
-$basketballer = new Basketballer();
-$basketballer->setPoints(101);
-$basketballer->setFieldGoals(47);
-$basketballer->setThreePointsFieldGoals(7);
-// set player identity via delegation
-$basketballer->setFirstName('Michael');
-$basketballer->setLastName('Giordano');
-// same as
-$player = new Player();
-$player->setFirstName('Michael');
-$player->setLastName('Giordano');
-$basketballer->setPlayer($player);
-
-// save basketballer and player
-$basketballer->save();
-
-// retrieve delegated data directly from the main object
-echo $basketballer->getFirstName(); // Michael
-```
-
-And since several models can delegate to the same player object, that means that a single player can have both basketball and soccer stats!
-
->**Tip**<br />In this example, table delegation is used to implement [Class Table Inheritance](http://martinfowler.com/eaaCatalog/classTableInheritance.html). See how Propel implements this inheritance type, and others, in the [inheritance chapter](../documentation/09-inheritance.html).
-
-## Delegating To Several Tables ##
-
-Delegation allows to delegate to several tables. Just separate the name of the delegate tables by commas in the `to` parameter of the `delegate` behavior tag in your schema to delegate to several tables:
-
-```xml
-<table name="account">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="login" type="VARCHAR" required="true" />
- <column name="password" type="VARCHAR" required="true" />
- <behavior name="delegate">
- <parameter name="to" value="profile, preference" />
- </behavior>
-</table>
-<table name="profile">
- <column name="email" type="VARCHAR" />
- <column name="telephone" type="VARCHAR" />
-</table>
-<table name="preference">
- <column name="preferred_color" type="VARCHAR" />
- <column name="max_size" type="INTEGER" />
-</table>
-```
-
-Now the `Account` class has two delegates, that can be addressed seamlessly:
-
-```php
-<?php
-$account = new Account();
-$account->setLogin('francois');
-$account->setPassword('S€cr3t');
-
-// Fill the profile via delegation
-$account->setEmail('francois@example.com');
-$account->setTelephone('202-555-9355');
-// Fill the preference via delegation
-$account->setPreferredColor('orange');
-$account->setMaxSize('200');
-
-// save the account and its profile and its preference
-$account->save();
-```
-
-On the other hand, it is not possible to cascade delegation to yet another model. So even if the `profile` table delegates to another `detail` table, the methods of the `Detail` model won't be accessibe to the `Profile` objects.
-
-## Parameters ##
-
-The `delegate` behavior takes only one parameter, the list of delegate tables:
-
-```xml
-<table name="account">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="login" type="VARCHAR" required="true" />
- <column name="password" type="VARCHAR" required="true" />
- <behavior name="delegate">
- <parameter name="to" value="profile, preference" />
- </behavior>
-</table>
-```
-
-Note that the delegate tables must exist, but they don't need to share a relationship with the main table (in which case the behavior creates a one-to-one relationship).
View
222 behaviors/i18n.markdown
@@ -1,222 +0,0 @@
----
-layout: documentation
-title: I18n Behavior
----
-
-# I18n Behavior #
-
-The `i18n` behavior provides internationalization to any !ActiveRecord object. Using this behavior, you can separate text data from the other data, and keep several translations of the text data for a single object. Applications supporting several languages always use the `i18n` behavior.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `i18n` behavior to a table. In the `<parameters>` tag, list the columns that need internationalization as the `i18n_columns` parameter:
-
-```xml
-<table name="item">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="name" type="VARCHAR" required="true" />
- <column name="description" type="LONGVARCHAR" />
- <column name="price" type="FLOAT" />
- <column name="is_in_store" type="BOOLEAN" />
- <behavior name="i18n">
- <parameter name="i18n_columns" value="name, description" />
- </behavior>
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The internationalized columns have now been moved to a new translation table called `item_i18n`; this new table contains a `locale` column, and shares a many-to-one relationship with the `item` table. In fact, everything happens as if you had defined the following schema:
-
-```xml
-<table name="item">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="price" type="FLOAT" />
- <column name="is_in_store" type="BOOLEAN" />
-</table>
-<table name="item_i18n">
- <column name="id" type="INTEGER" required="true" primaryKey="true" />
- <column name="locale" type="VARCHAR" size="5" required="true" primaryKey="true" />
- <column name="name" type="VARCHAR" required="true" />
- <column name="description" type="LONGVARCHAR" />
- <foreign-key foreignTable="item" onDelete="setnull" onUpdate="cascade">
- <reference local="id" foreign="id" />
- </foreign-key>
-</table>
-```
-
-In addition, the ActiveRecord Item class now provides integrated translation capabilities.
-
-```php
-<?php
-$item = new Item();
-$item->setPrice('12.99');
-
-// add an English translation
-$item->setLocale('en_US');
-$item->setName('Microwave oven');
-// same as
-$itemI18n = new ItemI18n();
-$itemI18n->setLocale('en_US');
-$itemI18n->setName('Microwave oven');
-$item->addItemI18n($itemI18n);
-
-// add a French translation
-$item->setLocale('fr_FR');
-$item->setName('Four micro-ondes');
-
-// save the item and its translations
-$item->save();
-
-// retrieve text and non-text translations directly from the main object
-echo $item->getPrice(); // 12.99
-$item->setLocale('en_US');
-echo $item->getName(); // Microwave oven
-$item->setLocale('fr_FR');
-echo $item->getName(); // Four micro-ondes
-```
-
-Getter an setter methods for internationalized columns still exist on the main object ; they are just proxy methods to the current translation object, using the same signature and phpDoc for better IDE integration.
-
->**Tip**<br />Propel uses the [locale](http://en.wikipedia.org/wiki/Locale) concept to identify translations. A locale is a string composed of a language code and a territory code (such as 'en_US' and 'fr_FR'). This allows different translations for two countries using the same language (such as 'fr_FR' and 'fr_CA');
-
-## Dealing With Locale And Translations ##
-
-If you prefer to deal with real translation objects, the behavior generates a `getTranslation()` method on the !ActiveRecord class, which returns a translation object with the required locale.
-
-```php
-<?php
-$item = new Item();
-$item->setPrice('12.99');
-
-// get the English translation
-$t1 = $item->getTranslation('en_US');
-// same as
-$t1 = new ItemI18n();
-$t1->setLocale('en_US');
-$item->addItemI18n($t1);
-
-$t1->setName('Microwave oven');
-
-// get the French translation
-$t2 = $item->getTranslation('fr_FR');
-
-$t2->setName('Four micro-ondes');
-
-// these translation objects are already related to the main item
-// and therefore get saved together with it
-$item->save(); // already saves the two translations
-```
-
->**Tip**<br />Or course, if a translation already exists for a given locale, `getTranslation()` returns the existing translation and not a new one.
-
-You can remove a translation using `removeTranslation()` and a locale:
-
-```php
-<?php
-$item = ItemQuery::create()->findPk(1);
-// remove the French translation
-$item->removeTranslation('fr_FR');
-```
-
-## Querying For Objects With Translations ##
-
-If you need to display a list, the following code will issue n+1 SQL queries, n being the number of items:
-
-```php
-<?php
-$items = ItemQuery::create()->find(); // one query to retrieve all items
-$locale = 'en_US';
-foreach ($items as $item) {
- echo $item->getPrice();
- $item->setLocale($locale);
- echo $item->getName(); // one query to retrieve the English translation
-}
-```
-
-Fortunately, the behavior adds methods to the Query class, allowing you to hydrate both the `Item` objects and the related `ItemI18n` objects for the given locale:
-
-```php
-<?php
-$items = ItemQuery::create()
- ->joinWithI18n('en_US')
- ->find(); // one query to retrieve both all items and their translations
-foreach ($items as $item) {
- echo $item->getPrice();
- echo $item->getName(); // no additional query
-}
-```
-
-In addition to hydrating translations, `joinWithI18n()` sets the correct locale on results, so you don't need to call `setLocale()` for each result.
-
->**Tip**<br />`joinWithI18n()` adds a left join with two conditions. That means that the query returns all items, including those with no translation. If you need to return only objects having translations, add `Criteria::INNER_JOIN` as second parameter to `joinWithI18n()`.
-
-If you need to search items using a condition on a translation, use the generated `useI18nQuery()` as you would with any `useXXXQuery()` method:
-
-```php
-<?php
-$items = ItemQuery::create()
- ->useI18nQuery('en_US') // tests the condition on the English translation
- ->filterByName('Microwave oven')
- ->endUse()
- ->find();
-```
-
-## Symfony Compatibility ##
-
-This behavior is entirely compatible with the i18n behavior for symfony. That means that it can generate `setCulture()` and `getCulture()` methods as aliases to `setLocale()` and `getLocale()`, provided that you add a `locale_alias` parameter. That also means that if you add the behavior to a table without translated columns, and that the translation table is present in the schema, the behavior recognizes them.
-
-So the following schema is exactly equivalent to the first one in this tutorial:
-
-```xml
-<table name="item">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="price" type="FLOAT" />
- <column name="is_in_store" type="BOOLEAN" />
- <behavior name="i18n">
- <parameter name="locale_alias" value="culture" />
- </behavior>
-</table>
-<table name="item_i18n">
- <column name="id" type="INTEGER" required="true" primaryKey="true" />
- <column name="name" type="VARCHAR" required="true" />
- <column name="description" type="LONGVARCHAR" />
-</table>
-```
-
-Such a schema is almost similar to a schema built for symfony; that means that the Propel i18n behavior is a drop-in replacement for symfony's i18n behavior, keeping BC but improving performance and usability.
-
-## Parameters ##
-
-If you don't specify a locale when dealing with a translatable object, Propel uses the default English locale 'en_US'. This default can be overridden in the schema using the `default_locale` parameter:
-
-```xml
-<table name="item">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="name" type="VARCHAR" required="true" />
- <column name="description" type="LONGVARCHAR" />
- <column name="price" type="FLOAT" />
- <column name="is_in_store" type="BOOLEAN" />
- <behavior name="i18n">
- <parameter name="i18n_columns" value="name, description" />
- <parameter name="default_locale" value="fr_FR" />
- </behavior>
-</table>
-```
-
-You can change the name of the locale column added by the behavior by setting the `locale_column` parameter. Also, you can change the table name, the primary key name and the phpName of the i18n table by setting the `i18n_table`, `i18n_pk_name` and `i18n_phpname` parameters:
-
-```xml
-<table name="item">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="name" type="VARCHAR" required="true" />
- <column name="description" type="LONGVARCHAR" />
- <column name="price" type="FLOAT" />
- <column name="is_in_store" type="BOOLEAN" />
- <behavior name="i18n">
- <parameter name="i18n_columns" value="name, description" />
- <parameter name="locale_column" value="language" />
- <parameter name="i18n_table" value="item_translation" />
- <parameter name="i18n_pk_name" value="TransId" />
- <parameter name="i18n_phpname" value="ItemTranslation" />
- </behavior>
-</table>
-```
View
363 behaviors/nested-set.markdown
@@ -1,363 +0,0 @@
----
-layout: documentation
-title: NestedSet Behavior
----
-
-# NestedSet Behavior #
-
-The `nested_set` behavior allows a model to become a tree structure, and provides numerous methods to traverse the tree in an efficient way.
-
-Many applications need to store hierarchical data in the model. For instance, a forum stores a tree of messages for each discussion. A CMS sees sections and subsections as a navigation tree. In a business organization chart, each person is a leaf of the organization tree. [Nested sets](http://en.wikipedia.org/wiki/Nested_set_model) are the best way to store such hierachical data in a relational database and manipulate it. The name "nested sets" describes the algorithm used to store the position of a model in the tree; it is also known as "modified preorder tree traversal".
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `nested_set` behavior to a table:
-
-```xml
-<table name="section">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="nested_set" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has the ability to be inserted into a tree structure, as follows:
-
-```php
-<?php
-$s1 = new Section();
-$s1->setTitle('Home');
-$s1->makeRoot(); // make this node the root of the tree
-$s1->save();
-$s2 = new Section();
-$s2->setTitle('World');
-$s2->insertAsFirstChildOf($s1); // insert the node in the tree
-$s2->save();
-$s3 = new Section();
-$s3->setTitle('Europe');
-$s3->insertAsFirstChildOf($s2); // insert the node in the tree
-$s3->save();
-$s4 = new Section();
-$s4->setTitle('Business');
-$s4->insertAsNextSiblingOf($s2); // insert the node in the tree
-$s4->save();
-
-/*
-The sections are now stored in the database as a tree:
-
-$s1:Home
- |
-$s2:World
- | \
-$s3:Europe $s4:Business
-
-*/
-```
-
-You can continue to insert new nodes as children or siblings of existing nodes, using any of the `insertAsFirstChildOf()`, `insertAsLastChildOf()`, `insertAsPrevSiblingOf()`, and `insertAsNextSiblingOf()` methods.
-
-Once you have built a tree, you can traverse it using any of the numerous methods the `nested_set` behavior adds to the query and model objects. For instance:
-
-```php
-<?php
-$rootNode = SectionQuery::create()->findRoot(); // $s1
-$worldNode = $rootNode->getFirstChild(); // $s2
-$businessNode = $worldNode->getNextSibling(); // $s4
-$firstLevelSections = $rootNode->getChildren(); // array($s2, $s4)
-$allSections = $rootNode->getDescendants(); // array($s2, $s3, $s4)
-// you can also chain the methods
-$europeNode = $rootNode->getLastChild()->getPrevSibling()->getFirstChild(); // $s3
-$path = $europeNode->getAncestors(); // array($s1, $s2)
-```
-
-The nodes returned by these methods are regular Propel model objects, with access to the properties and related models. The `nested_set` behavior also adds inspection methods to nodes:
-
-```php
-<?php
-echo $s2->isRoot(); // false
-echo $s2->isLeaf(); // false
-echo $s2->getLevel(); // 1
-echo $s2->hasChildren(); // true
-echo $s2->countChildren(); // 1
-echo $s2->hasSiblings(); // true
-```
-
-Each of the traversal and inspection methods result in a single database query, whatever the position of the node in the tree. This is because the information about the node position in the tree is stored in three columns of the model, named `tree_left`, `tree_right`, and `tree_level`. The value given to these columns is determined by the nested set algorithm, and it makes read queries much more effective than trees using a simple `parent_id` foreign key.
-
-## Manipulating Nodes ##
-
-You can move a node - and its subtree - across the tree using any of the `moveToFirstChildOf()`, `moveToLastChildOf()`, `moveToPrevSiblingOf()`, and `moveToLastSiblingOf()` methods. These operations are immediate and don't require that you save the model afterwards:
-
-```php
-<?php
-// move the entire "World" section under "Business"
-$s2->moveToFirstChildOf($s4);
-/* The tree is modified as follows:
-$s1:Home
- |
-$s4:Business
- |
-$s2:World
- |
-$s3:Europe
-*/
-// now move the "Europe" section directly under root, after "Business"
-$s3->moveToFirstChildOf($s4);
-/* The tree is modified as follows:
- $s1:Home
- | \
-$s4:Business $s3:Europe
- |
-$s2:World
-*/
-```
-
-You can delete the descendants of a node using `deleteDescendants()`:
-
-```php
-<?php
-// move the entire "World" section under "Business"
-$s4->deleteDescendants($s4);
-/* The tree is modified as follows:
- $s1:Home
- | \
-$s4:Business $s3:Europe
-*/
-```
-
-If you `delete()` a node, all its descendants are deleted in cascade. To avoid accidental deletion of an entire tree, calling `delete()` on a root node throws an exception. Use the `delete()` Query method instead to delete an entire tree.
-
-## Filtering Results ##
-
-The `nested_set` behavior adds numerous methods to the generated Query object. You can use these methods to build more complex queries. For instance, to get all the children of the root node ordered by title, build a Query as follows:
-
-```php
-<?php
-$children = SectionQuery::create()
- ->childrenOf($rootNode)
- ->orderByTitle()
- ->find();
-```
-
-Alternatively, if you already have an existing query method, you can pass it to the model object's methods to filter the results:
-
-```php
-<?php
-$orderQuery = SectionQuery::create()->orderByTitle();
-$children = $rootNode->getChildren($orderQuery);
-```
-
-## Multiple Trees ##
-
-When you need to store several trees for a single model - for instance, several threads of posts in a forum - use a _scope_ for each tree. This requires that you enable scope tree support in the behavior definition by setting the `use_scope` parameter to `true`:
-
-```xml
-<table name="post">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="body" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="nested_set">
- <parameter name="use_scope" value="true" />
- <parameter name="scope_column" value="thread_id" />
- </behavior>
- <foreign-key foreignTable="thread" onDelete="cascade">
- <reference local="thread_id" foreign="id" />
- </foreign-key>
-</table>
-```
-
-Now, after rebuilding your model, you can have as many trees as required:
-
-```php
-<?php
-$thread = ThreadQuery::create()->findPk(123);
-$firstPost = PostQuery::create()->findRoot($thread->getId()); // first message of the discussion
-$discussion = PostQuery::create()->findTree(thread->getId()); // all messages of the discussion
-PostQuery::create()->inTree($thread->getId())->delete(); // delete an entire discussion
-$firstPostOfEveryDiscussion = PostQuery::create()->findRoots();
-```
-
-## Using a RecursiveIterator ##
-
-An alternative way to browse a tree structure extensively is to use a [RecursiveIterator](http://php.net/RecursiveIterator). The `nested_set` behavior provides an easy way to retrieve such an iterator from a node, and to parse the entire branch in a single iteration.
-
-For instance, to display an entire tree structure, you can use the following code:
-
-```php
-<?php
-$root = SectionQuery::create()->findRoot();
-foreach (new RecursiveIteratorIterator($root->getIterator(), RecursiveIteratorIterator::SELF_FIRST) as $node) {
- echo str_repeat(' ', $node->getLevel()) . $node->getTitle() . "\n";
-}
-```
-
-The iterator parses the tree in a recursive way by retrieving the children of every node. This can be quite effective on very large trees, since the iterator hydrates only a few objects at a time.
-
-Beware, though, that the iterator executes many queries to parse a tree. On smaller trees, prefer the `getBranch()` method to execute only one query, and hydrate all records at once:
-
-```php
-<?php
-$root = SectionQuery::create()->findRoot();
-foreach ($root->getBranch() as $node) {
- echo str_repeat(' ', $node->getLevel()) . $node->getTitle() . "\n";
-}
-```
-
-## Parameters ##
-
-By default, the behavior adds three columns to the model - four if you use the scope feature. You can use custom names for the nested sets columns. The following schema illustrates a complete customization of the behavior:
-
-```xml
-<table name="post">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="lft" type="INTEGER" />
- <column name="rgt" type="INTEGER" />
- <column name="lvl" type="INTEGER" />
- <column name="thread_id" type="INTEGER" />
- <column name="body" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="nested_set">
- <parameter name="left_column" value="lft" />
- <parameter name="right_column" value="rgt" />
- <parameter name="level_column" value="lvl" />
- <parameter name="use_scope" value="true" />
- <parameter name="scope_column" value="thread_id" />
- </behavior>
- <foreign-key foreignTable="thread" onDelete="cascade">
- <reference local="thread_id" foreign="id" />
- </foreign-key>
-</table>
-```
-
-Whatever name you give to your columns, the `nested_sets` behavior always adds the following proxy methods, which are mapped to the correct column:
-
-```php
-<?php
-$post->getLeftValue(); // returns $post->lft
-$post->setLeftValue($left);
-$post->getRightValue(); // returns $post->rgt
-$post->setRightValue($right);
-$post->getLevel(); // returns $post->lvl
-$post->setLevel($level);
-$post->getScopeValue(); // returns $post->thread_id
-$post->setScopeValue($scope);
-```
-
-If your application used the old nested sets builder from Propel 1.4, you can enable the `method_proxies` parameter so that the behavior generates method proxies for the methods that used a different name (e.g. `createRoot()` for `makeRoot()`, `retrieveFirstChild()` for `getFirstChild()`, etc.
-
-```xml
-<table name="section">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="nested_set">
- <parameter name="method_proxies" value="true" />
- </behavior>
-</table>
-```
-
-## Complete API ##
-
-Here is a list of the methods added by the behavior to the model objects:
-
-```php
-<?php
-// storage columns accessors
-int getLeftValue()
-$node setLeftValue(int $left)
-int getRightValue()
-$node setRightValue(int $right)
-int getLevel()
-$node setLevel(int $level)
-// only for behavior with use_scope
-int getScopeValue()
-$node setScopeValue(int $scope)
-
-// root maker (requires calling save() afterwards)
-$node makeRoot()
-
-// inspection methods
-bool isInTree()
-bool isRoot()
-bool isLeaf()
-bool isDescendantOf()
-bool isAncestorOf()
-bool hasParent()
-bool hasPrevSibling()
-bool hasNextSibling()
-bool hasChildren()
-int countChildren()
-int countDescendants()
-
-// tree traversal methods
-$node getParent()
-$node getPrevSibling()
-$node getNextSibling()
-array getChildren()
-$node getFirstChild()
-$node getLastChild()
-array getSiblings($includeCurrent = false, Criteria $c = null)
-array getDescendants(Criteria $c = null)
-array getBranch(Criteria $c = null)
-array getAncestors(Criteria $c = null)
-
-// node insertion methods (require calling save() afterwards)
-$node addChild($node)
-$node insertAsFirstChildOf($node)
-$node insertAsLastChildOf($node)
-$node insertAsPrevSiblingOf($node)
-$node insertAsNextSiblingOf($node)
-
-// node move methods (immediate, no need to save() afterwards)
-$node moveToFirstChildOf($node)
-$node moveToLastChildOf($node)
-$node moveToPrevSiblingOf($node)
-$node moveToNextSiblingOf($node)
-
-// deletion methods
-$node deleteDescendants()
-
-// only for behavior with method_proxies
-$node createRoot()
-$node retrieveParent()
-$node retrievePrevSibling()
-$node retrieveNextSibling()
-$node retrieveFirstChild()
-$node retrieveLastChild()
-array getPath()
-```
-
-The behavior also adds some methods to the Query classes:
-
-```php
-<?php
-// tree filter methods
-query descendantsOf($node)
-query branchOf($node)
-query childrenOf($node)
-query siblingsOf($node)
-query ancestorsOf($node)
-query rootsOf($node)
-// only for behavior with use_scope
-query treeRoots()
-query inTree($scope = null)
-coll findRoots()
-// order methods
-query orderByBranch($reverse = false)
-query orderByLevel($reverse = false)
-// termination methods
-$node findRoot($scope = null)
-coll findTree($scope = null)
-```
-
-Lastly, the behavior adds a few methods to the Peer classes:
-
-```php
-<?php
-$node retrieveRoot($scope = null)
-array retrieveTree($scope = null)
-int deleteTree($scope = null)
-// only for behavior with use_scope
-array retrieveRoots(Criteria $c = null)
-```
-
-## TODO ##
-
-* InsertAsParentOf
View
103 behaviors/query-cache.markdown
@@ -1,103 +0,0 @@
----
-layout: documentation
-title: Query Cache Behavior
----
-
-# Query Cache Behavior #
-
-The `query_cache` behavior gives a speed boost to Propel queries by caching the transformation of a PHP Query object into reusable SQL code.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `query_cache` behavior to a table:
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="query_cache" />
-</table>
-```
-
-After you rebuild your model, all the queries on this object can now be cached. To trigger the query cache on a particular query, just give it a query key using the `setQueryKey()` method. The key is a unique identifier that you can choose, later used for cache lookups:
-
-```php
-<?php
-$title = 'War And Peace';
-$books = BookQuery::create()
- ->setQueryKey('search book by title')
- ->filterByTitle($title)
- ->findOne();
-```
-
-The first time Propel executes the termination method, it computes the SQL translation of the Query object and stores it into a cache backend (APC by default). Next time you run the same query, it executes faster, even with different parameters:
-
-```php
-<?php
-$title = 'Anna Karenina';
-$books = BookQuery::create()
- ->setQueryKey('search book by title')
- ->filterByTitle($title)
- ->findOne();
-```
-
->**Tip**<br />The more complex the query, the greater the boost you get from the query cache behavior.
-
-## Parameters ##
-
-You can change the cache backend and the cache lifetime (in seconds) by setting the `backend` and `lifetime` parameters:
-
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="query_cache">
- <parameter name="backend" value="custom" />
- <parameter name="lifetime" value="600" />
- </behavior>
-</table>
-```
-
-To implement a custom cache backend, just override the generated `cacheContains()`, `cacheFetch()` and `cacheStore()` methods in the Query object. For instance, to implement query cache using Zend_Cache and memcached, try the following:
-
-```php
-<?php
-class BookQuery extends BaseBookQuery
-{
- public function cacheContains($key)
- {
- return $this->getCacheBackend()->test($key);
- }
-
- public function cacheFetch($key)
- {
- return $this->getCacheBackend()->load($key);
- }
-
- public function cacheStore($key, $value)
- {
- return $this->getCacheBackend()->save($key, $value);
- }
-
- protected function getCacheBackend()
- {
- if (self::$cacheBackend === null) {
- $frontendOptions = array(
- 'lifetime' => 7200,
- 'automatic_serialization' => true
- );
- $backendOptions = array(
- 'servers' => array(
- array(
- 'host' => 'localhost',
- 'port' => 11211,
- 'persistent' => true
- )
- )
- );
- self::$cacheBackend = Zend_Cache::factory('Core', 'Memcached', $frontendOptions, $backendOptions);
- }
-
- return self::$cacheBackend;
- }
-}
-```
View
134 behaviors/sluggable.markdown
@@ -1,134 +0,0 @@
----
-layout: documentation
-title: Sluggable Behavior
----
-
-# Sluggable Behavior #
-
-The `sluggable` behavior allows a model to offer a human readable identifier that can be used for search engine friendly URLs.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `sluggable` behavior to a table:
-
-```xml
-<table name="post">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="sluggable" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has an additional getter for its slug, which is automatically set before the object is saved:
-
-```php
-<?php
-$p1 = new Post();
-$p1->setTitle('Hello, World!');
-$p1->save();
-echo $p1->getSlug(); // 'hello-world'
-```
-
-By default, the behavior uses the string representation of the object to build the slug. In the example above, the `title` column is defined as `primaryString`, so the slug uses this column as a base string. The string is then cleaned up in order to allow it to appear in a URL. In the process, blanks and special characters are replaced by a dash, and the string is lowercased.
-
->**Tip**<br />The slug is unique by design. That means that if you create a new object and that the behavior calculates a slug that already exists, the string is modified to be unique:
-
-```php
-<?php
-$p2 = new Post();
-$p2->setTitle('Hello, World!');
-$p2->save();
-echo $p2->getSlug(); // 'hello-world-1'
-```
-
-The generated model query offers a `findOneBySlug()` method to easily retrieve a model object based on its slug:
-
-```php
-<?php
-$p = PostQuery::create()->findOneBySlug('hello-world');
-```
-
-## Parameters ##
-
-By default, the behavior adds one column to the model. If this column is already described in the schema, the behavior detects it and doesn't add it a second time. The behavior parameters allow you to use custom patterns for the slug composition. The following schema illustrates a complete customization of the behavior:
-
-```xml
-<table name="post">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="url" type="VARCHAR" size="100" />
- <behavior name="sluggable">
- <parameter name="slug_column" value="url" />
- <parameter name="slug_pattern" value="/posts/{Title}" />
- <parameter name="replace_pattern" value="/[^\w\/]+/u" />
- <parameter name="replacement" value="-" />
- <parameter name="separator" value="/" />
- <parameter name="permanent" value="true" />
- <parameter name="scope_column" value="" />
- </behavior>
-</table>
-```
-
-Whatever `slug_column` name you choose, the `sluggable` behavior always adds the following proxy methods, which are mapped to the correct column:
-
-```php
-<?php
-$post->getSlug(); // returns $post->url
-$post->setSlug($slug); // $post->url = $slug
-```
-
-The `slug_pattern` parameter is the rule used to build the raw slug based on the object properties. Any substring enclosed between brackets '{}' is turned into a getter, so the `Post` class generates slugs as follows:
-
-```php
-<?php
-protected function createRawSlug()
-{
- return '/posts/' . $this->getTitle();
-}
-```
-
-Incidentally, that means that you can use names that don't match a real column phpName, as long as your model provides a getter for it.
-
-The `replace_pattern` parameter is a regular expression that shows all the characters that will end up replaced by the `replacement` parameter. In the above example, special characters like '!' or ':' are replaced by '-', but not letters, digits, nor '/'.
-
-The `separator` parameter is the character that separates the slug from the incremental index added in case of non-unicity. Set as '/', it makes `Post` objects sharing the same title have the following slugs:
-
-```text
-'posts/hello-world'
-'posts/hello-world/1'
-'posts/hello-world/2'
-...
-```
-
-A `permanent` slug is not automatically updated when the fields that constitute it change. This is useful when the slug serves as a permalink, that should work even when the model object properties change. Note that you can still manually change the slug in a model using the `permanent` setting by calling `setSlug()`;
-
-## Further Customization ##
-
-The slug is generated by the object when it is saved, via the `createSlug()` method. This method does several operations on a simple string:
-
-```php
-<?php
-protected function createSlug()
-{
- // create the slug based on the `slug_pattern` and the object properties
- $slug = $this->createRawSlug();
- // truncate the slug to accomodate the size of the slug column
- $slug = $this->limitSlugSize($slug);
- // add an incremental index to make sure the slug is unique
- $slug = $this->makeSlugUnique($slug);
-
- return $slug;
-}
-
-protected function createRawSlug()
-{
- // here comes the string composition code, generated according to `slug_pattern`
- $slug = 'posts/' . $this->cleanupSlugPart($this->getTitle());
- // cleanupSlugPart() cleans up the slug part
- // based on the `replace_pattern` and `replacement` parameters
-
- return $slug;
-}
-```
-
-You can override any of these methods in your model class, in order to implement a custom slug logic.
View
130 behaviors/soft-delete.markdown
@@ -1,130 +0,0 @@
----
-layout: documentation
-title: SoftDelete Behavior
----
-
-# SoftDelete Behavior #
-
-The `soft_delete` behavior overrides the deletion methods of a model object to make them 'hide' the deleted rows but keep them in the database. Deleted objects still don't show up on select queries, but they can be retrieved or undeleted when necessary.
-
-**Warning**: This behavior is deprecated due to limitations that can't be fixed. Use the [`archivable`](archivable.html) behavior instead.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `soft_delete` behavior to a table:
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="soft_delete" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has one new column, `deleted_at`, that stores the deletion date. Select queries don't return the deleted objects:
-
-```php
-<?php
-$b = new Book();
-$b->setTitle('War And Peace');
-$b->save();
-$b->delete();
-echo $b->isDeleted(); // false
-echo $b->getDeletedAt(); // 2009-10-02 18:14:23
-$books = BookQuery::create()->find(); // empty collection
-```
-
-Behind the curtain, the behavior adds a condition to every SELECT query to return only records where the `deleted_at` column is null. That's why the deleted objects don't appear anymore upon selection.
-
-_Warning_gg Deleted results may show up in related results (i.e. when you use `joinWith()` on a query and point to a `soft_delete` model). This is something that can't be fixed, and a good reason to use the `archivable` behavior instead.
-
-You can include deleted results in a query by calling the `includeDeleted()` filter:
-
-```php
-<?php
-$book = BookQuery::create()
- ->includeDeleted()
- ->findOne();
-echo $book->getTitle(); // 'War And Peace'
-```
-
-You can also turn off the query alteration for the next query by calling the static method `disableSoftDelete()` on the related Query object:
-
-```php
-<?php
-BookQuery::disableSoftDelete();
-$book = BookQuery::create()->findOne();
-echo $book->getTitle(); // 'War And Peace'
-```
-
-Note that `find()` and other selection methods automatically re-enable the `soft_delete` filter, so `disableSoftDelete()` is really a single shot method. You can also enable the query alteration manually by calling the `enableSoftDelete()` method on Query objects.
-
->**Tip**<br />`ModelCriteria::paginate()` executes two queries, so `disableSoftDelete()` doesn't work in this case. Prefer `includeDeleted()` in queries using `paginate()`.
-
-If you want to recover a deleted object, use the `unDelete()` method:
-
-```php
-<?php
-$book->unDelete();
-$books = BookQuery::create()->find();
-$book = $books[0];
-echo $book->getTitle(); // 'War And Peace'
-```
-
-If you want to force the real deletion of an object, call the `forceDelete()` method:
-
-```php
-<?php
-$book->forceDelete();
-echo $book->isDeleted(); // true
-$books = BookQuery::create()->find(); // empty collection
-```
-
-The query methods `delete()` and `deleteAll()` also perform a soft deletion, unless you disable the behavior on the peer class:
-
-```php
-<?php
-$b = new Book();
-$b->setTitle('War And Peace');
-$b->save();
-
-BookQuery::create()->delete($b);
-$books = BookQuery::create()->find(); // empty collection
-// the rows look deleted, but they are still there
-BookQuery::disableSoftDelete();
-$books = BookQuery::create()->find();
-$book = $books[0];
-echo $book->getTitle(); // 'War And Peace'
-
-// To perform a true deletion, disable the softDelete feature
-BookQuery::disableSoftDelete();
-BookQuery::create()->delete();
-// Alternatively, use forceDelete()
-BookQuery::create()->filterByTitle('%Lo%')->forceDelete();
-// To remove all use
-BookQuery::create()->forceDeleteAll();
-```
-
-## Parameters ##
-
-You can change the name of the column added by the behavior by setting the `deleted_column` parameter:
-
-```xml
-<table name="book">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="my_deletion_date" type="TIMESTAMP" />
- <behavior name="soft_delete">
- <parameter name="deleted_column" value="my_deletion_date" />
- </behavior>
-</table>
-```
-
-```php
-<?php
-$b = new Book();
-$b->setTitle('War And Peace');
-$b->save();
-$b->delete();
-echo $b->getMyDeletionDate(); // 2009-10-02 18:14:23
-$books = BookQuery::create()->find(); // empty collection
-```
View
371 behaviors/sortable.markdown
@@ -1,371 +0,0 @@
----
-layout: documentation
-title: Sortable Behavior
----
-
-# Sortable Behavior #
-
-The `sortable` behavior allows a model to become an ordered list, and provides numerous methods to traverse this list in an efficient way.
-
-## Basic Usage ##
-
-In the `schema.xml`, use the `<behavior>` tag to add the `sortable` behavior to a table:
-```xml
-<table name="task">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <behavior name="sortable" />
-</table>
-```
-
-Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has the ability to be inserted into an ordered list, as follows:
-
-```php
-<?php
-$t1 = new Task();
-$t1->setTitle('Wash the dishes');
-$t1->save();
-echo $t1->getRank(); // 1, the first rank to be given (not 0)
-$t2 = new Task();
-$t2->setTitle('Do the laundry');
-$t2->save();
-echo $t2->getRank(); // 2
-$t3 = new Task();
-$t3->setTitle('Rest a little');
-$t3->save()
-echo $t3->getRank(); // 3
-```
-
-As long as you save new objects, Propel gives them the first available rank in the list.
-
-Once you have built an ordered list, you can traverse it using any of the methods added by the `sortable` behavior. For instance:
-
-```php
-<?php
-$firstTask = TaskQuery::create()->findOneByRank(1); // $t1
-$secondTask = $firstTask->getNext(); // $t2
-$lastTask = $secondTask->getNext(); // $t3
-$secondTask = $lastTask->getPrevious(); // $t2
-
-$allTasks = TaskQuery::create()->findList();
-// => collection($t1, $t2, $t3)
-$allTasksInReverseOrder = TaskQuery::create()->orderByRank('desc')->find();
-// => collection($t3, $t2, $t2)
-```
-
-The results returned by these methods are regular Propel model objects, with access to the properties and related models. The `sortable` behavior also adds inspection methods to objects:
-
-```php
-<?php
-echo $t2->isFirst(); // false
-echo $t2->isLast(); // false
-echo $t2->getRank(); // 2
-```
-
-## Manipulating Objects In A List ##
-
-You can move an object in the list using any of the `moveUp()`, `moveDown()`, `moveToTop()`, `moveToBottom()`, `moveToRank()`, and `swapWith()` methods. These operations are immediate and don't require that you save the model afterwards:
-
-```php
-<?php
-// The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
-$t2->moveToTop();
-// The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
-$t2->moveToBottom();
-// The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
-$t2->moveUp();
-// The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
-$t2->swapWith($t1);
-// The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
-$t2->moveToRank(3);
-// The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
-$t2->moveToRank(2);
-```
-
-By default, new objects are added at the bottom of the list. But you can also insert them at a specific position, using any of the `insertAtTop()`, `insertAtBottom()`, and `insertAtRank()` methods. Note that the `insertAtXXX` methods don't save the object:
-
-```php
-<?php
-// The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
-$t4 = new Task();
-$t4->setTitle('Clean windows');
-$t4->insertAtRank(2);
-$t4->save();
-// The list is now 1 - Wash the dishes, 2 - Clean Windows, 3 - Do the laundry, 4 - Rest a little
-```
-
-Whenever you `delete()` an object, the ranks are rearranged to fill the gap:
-
-```php
-<?php
-$t4->delete();
-// The list is now 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
-```
-
->**Tip**<br />You can remove an object from the list without necessarily deleting it by calling `removeFromList()`. Don't forget to `save()` it afterwards so that the other objects in the lists are rearranged to fill the gap.
-
-## Multiple Lists ##
-
-When you need to store several lists for a single model - for instance, one task list for each user - use a _scope_ for each list. This requires that you enable scope support in the behavior definition by setting the `use_scope` parameter to `true`:
-
-```xml
-<table name="task">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="user_id" required="true" type="INTEGER" />
- <foreign-key foreignTable="user" onDelete="cascade">
- <reference local="user_id" foreign="id" />
- </foreign-key>
- <behavior name="sortable">
- <parameter name="use_scope" value="true" />
- <parameter name="scope_column" value="user_id" />
- </behavior>
-</table>
-```
-
-Now, after rebuilding your model, you can have as many lists as required:
-
-```php
-<?php
-// test users
-$paul = new User();
-$john = new User();
-// now onto the tasks
-$t1 = new Task();
-$t1->setTitle('Wash the dishes');
-$t1->setUser($paul);
-$t1->save();
-echo $t1->getRank(); // 1
-$t2 = new Task();
-$t2->setTitle('Do the laundry');
-$t2->setUser($paul);
-$t2->save();
-echo $t2->getRank(); // 2
-$t3 = new Task();
-$t3->setTitle('Rest a little');
-$t3->setUser($john);
-$t3->save()
-echo $t3->getRank(); // 1, because John has his own task list
-```
-
-The generated methods now accept a `$scope` parameter to restrict the query to a given scope:
-
-```php
-<?php
-$firstPaulTask = TaskQuery::create()->findOneByRank($rank = 1, $scope = $paul->getId()); // $t1
-$lastPaulTask = $firstTask->getNext(); // $t2
-$firstJohnTask = TaskPeer::create()->findOneByRank($rank = 1, $scope = $john->getId()); // $t1
-```
-
-Models using the sortable behavior with scope benefit from one additional Query method named `inList()`:
-
-```php
-<?php
-$allPaulsTasks = TaskPeer::create()->inList($scope = $paul->getId())->find();
-```
-
-## Multi-Column scopes ##
-
-As of Propel 1.7.0 we added support for Multi-Column scoped Sortable Behavior. This is defined using a comma separated list of column names as `scope_column` parameter.
-Note that API methods which are generated by the behavior take parameters in the order which they are defined in `scope_column` parameter.
-
-```xml
-<table name="task">
- <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
- <column name="title" type="VARCHAR" required="true" primaryString="true" />
- <column name="user_id" required="true" type="INTEGER" />
- <column name="group_id" required="true" type="INTEGER" />
- <foreign-key foreignTable="user" onDelete="cascade">
- <reference local="user_id" foreign="id" />
- </foreign-key>
- <behavior name="sortable">
- <parameter name="use_scope" value="true" />
- <parameter name=