<li\>Negative numbers are not permitted as indices.
</ul>
<ul>
<li\>An indexed array may be started with any non-negative number, however this is discouraged and it is recommended that all arrays have a base index of 0.
</ul>
<ul>
<li\>When declaring indexed arrays with the array construct, a trailing space must be added after each comma delimiter to improve readability.
</ul>
<ul>
<li\>It is also permitted to declare multiline indexed arrays using the "array" construct. In this case, each successive line must be padded with spaces.
</ul>
<ul>
<li\>When declaring associative arrays with the array construct, it is encouraged to break the statement into multiple lines. In this case, each successive line must be padded with whitespace such that both the keys and the values are aligned:
<li\>Control statements based on the if and elseif constructs must have a single space before the opening parenthesis of the conditional, and a single space after the closing parenthesis.
</ul>
<ul>
<li\>Within the conditional statements between the parentheses, operators must be separated by spaces for readability. Inner parentheses are encouraged to improve logical grouping of larger conditionals.
</ul>
<ul>
<li\>The opening brace is written on the same line as the conditional statement. The closing brace is always written on its own line. Any content within the braces must be indented four spaces.
</ul>
<?php
renderCode("<?php
* Control statements based on the if and elseif constructs must have a single space before the opening parenthesis of the conditional, and a single space after the closing parenthesis.
* Within the conditional statements between the parentheses, operators must be separated by spaces for readability. Inner parentheses are encouraged to improve logical grouping of larger conditionals.
* The opening brace is written on the same line as the conditional statement. The closing brace is always written on its own line. Any content within the braces must be indented four spaces.
<codetype="php">
if (\$foo != 2) {
\$foo = 2;
}");
?>
<ul>
<li\>For "if" statements that include "elseif" or "else", the formatting must be as in these examples:
</ul>
<?php
renderCode("<?php
}</code>
* For "if" statements that include "elseif" or "else", the formatting must be as in these examples:
<codetype="php">
if (\$foo != 1) {
\$foo = 1;
} else {
...
...
@@ -30,14 +27,12 @@ if (\$foo != 2) {
\$foo = 3;
} else {
\$foo = 11;
}");
?>
<ul>
<li\>PHP allows for these statements to be written without braces in some circumstances, the following format for if statements is also allowed:
</ul>
<?php
renderCode("<?php
}</code>
* PHP allows for these statements to be written without braces in some circumstances, the following format for if statements is also allowed:
<codetype="php">
if (\$foo != 1)
\$foo = 1;
else
...
...
@@ -49,16 +44,14 @@ elseif (\$foo == 1)
\$foo = 3;
else
\$foo = 11;
");
?>
<ul>
<li\>Control statements written with the "switch" construct must have a single space before the opening parenthesis of the conditional statement, and also a single space after the closing parenthesis.
</ul>
<ul>
<li\>All content within the "switch" statement must be indented four spaces. Content under each "case" statement must be indented an additional four spaces but the breaks must be at the same indentation level as the "case" statements.
</ul>
<?php
renderCode("<?php
</code>
* Control statements written with the "switch" construct must have a single space before the opening parenthesis of the conditional statement, and also a single space after the closing parenthesis.
* All content within the "switch" statement must be indented four spaces. Content under each "case" statement must be indented an additional four spaces but the breaks must be at the same indentation level as the "case" statements.
<codetype="php">
switch (\$case) {
case 1:
case 2:
...
...
@@ -68,9 +61,8 @@ switch (\$case) {
default:
break;
}
?>");
?>
<ul>
<li\>The construct default may never be omitted from a switch statement.
</ul>
?></code>
* The construct default may never be omitted from a switch statement.
<li\>When a string is literal (contains no variable substitutions), the apostrophe or "single quote" must always used to demarcate the string:
</ul>
<ul>
<li\>When a literal string itself contains apostrophes, it is permitted to demarcate the string with quotation marks or "double quotes". This is especially encouraged for SQL statements:
</ul>
<ul>
<li\>Variable substitution is permitted using the following form:
</ul>
<ul>
<li\>Strings may be concatenated using the "." operator. A space must always be added before and after the "." operator to improve readability:
</ul>
<ul>
<li\>When concatenating strings with the "." operator, it is permitted to break the statement into multiple lines to improve readability. In these cases, each successive line should be padded with whitespace such that the "."; operator is aligned under the "=" operator:
<li\>For all other files, only alphanumeric characters, underscores, and the dash character ("-") are permitted. Spaces are prohibited.
</ul>
<ul>
<li\>Any file that contains any PHP code must end with the extension ".php". These examples show the acceptable filenames for containing the class names from the examples in the section above:
<br\><br\>
Doctrine/Db.php <br\>
<br\>
Doctrine/Connection/Transaction.php <br\>
</ul>
<ul>
<li\>File names must follow the mapping to class names described above.
<li>Function names may only contain alphanumeric characters. Underscores are not permitted. Numbers are permitted in function names but are discouraged.
</ul>
<ul>
<li>Function names must always start with a lowercase letter. When a function name consists of more than one word, the first letter of each new word must be capitalized. This is commonly called the "studlyCaps" or "camelCaps" method.
</ul>
<ul>
<li>Verbosity is encouraged. Function names should be as verbose as is practical to enhance the understandability of code.
</ul>
<ul>
<li>For object-oriented programming, accessors for objects should always be prefixed with either "get" or "set". This applies to all classes except for Doctrine_Record which has some accessor methods prefixed with 'obtain' and 'assign'. The reason
for this is that since all user defined ActiveRecords inherit Doctrine_Record, it should populate the get / set namespace as little as possible.
</ul>
<ul>
<li>Functions in the global scope ("floating functions") are NOT permmitted. All static functions should be wrapped in a static class.
<li\>Line termination is the standard way for Unix text files. Lines must end only with a linefeed (LF). Linefeeds are represented as ordinal 10, or hexadecimal 0x0A.
</ul>
<ul>
<li\>Do not use carriage returns (CR) like Macintosh computers (0x0D).
</ul>
<ul>
<li\>Do not use the carriage return/linefeed combination (CRLF) as Windows computers (0x0D, 0x0A).
<bclass='title'>Doctrine_Export_Pgsql_TestCase::testCreateTable()</b> would not be allowed!
<br\><br\>
<li\>Test method names can often be long. However the content within the methods should rarely be more than dozen lines long. If you need several assert-calls
@@ -4,48 +4,56 @@ You can quote the db identifiers (table and field names) with quoteIdentifier().
Some of the internal Doctrine methods generate queries. Enabling the "quote_identifier" attribute of Doctrine you can tell Doctrine to quote the identifiers in these generated queries. For all user supplied queries this option is irrelevant.
Portability is broken by using the following characters inside delimited identifiers:
<br\><br\>
<ul>
<li\>backtick (`) -- due to MySQL
<li\>double quote (") -- due to Oracle
<li\>brackets ([ or ]) -- due to Access
</ul>
* backtick (`) -- due to MySQL
* double quote (") -- due to Oracle
* brackets ([ or ]) -- due to Access
Delimited identifiers are known to generally work correctly under the following drivers:
<br\>
<ul>
<li\>Mssql
<li\>Mysql
<li\>Oracle
<li\>Pgsql
* Mssql
* Mysql
<li\>Sqlite
* Oracle
* Pgsql
* Sqlite
* Firebird
<li\>Firebird
</ul>
When using the quoteIdentifiers option, all of the field identifiers will be automatically quoted in the resulting SQL statements:
<br\><br\>
<?php
renderCode("<?php
<codetype="php">
\$conn->setAttribute('quote_identifiers', true);
?>");
?>
<br\><br\>
?></code>
will result in a SQL statement that all the field names are quoted with the backtick '`' operator (in MySQL).
<br\>
<divclass='sql'>SELECT * FROM `sometable` WHERE `id` = '123'</div>
<br\>
as opposed to:
<br\>
<divclass='sql'>SELECT * FROM sometable WHERE id='123'</div>
@@ -4,26 +4,46 @@ Each database management system (DBMS) has it's own behaviors. For example, some
You control which portability modes are enabled by using the portability configuration option. Configuration options are set via factory() and setOption().
The portability modes are bitwised, so they can be combined using | and removed using ^. See the examples section below on how to do this.
<br\><br\>
Portability Mode Constants
<br\><br\>
<i>Doctrine::PORTABILITY_ALL (default)</i>
<br\><br\>
//Doctrine::PORTABILITY_ALL (default)//
turn on all portability features. this is the default setting.
<br\><br\>
<i>Doctrine::PORTABILITY_DELETE_COUNT</i>
<br\><br\>
//Doctrine::PORTABILITY_DELETE_COUNT//
Force reporting the number of rows deleted. Some DBMS's don't count the number of rows deleted when performing simple DELETE FROM tablename queries. This mode tricks such DBMS's into telling the count by adding WHERE 1=1 to the end of DELETE queries.
<br\><br\>
<i>Doctrine::PORTABILITY_EMPTY_TO_NULL</i>
<br\><br\>
//Doctrine::PORTABILITY_EMPTY_TO_NULL//
Convert empty strings values to null in data in and output. Needed because Oracle considers empty strings to be null, while most other DBMS's know the difference between empty and null.
<br\><br\>
<i>Doctrine::PORTABILITY_ERRORS</i>
<br\><br\>
//Doctrine::PORTABILITY_ERRORS//
Makes certain error messages in certain drivers compatible with those from other DBMS's
<br\><br\>
Table 33-1. Error Code Re-mappings
...
...
@@ -31,49 +51,73 @@ Driver Description Old Constant New Constant
mysql, mysqli unique and primary key constraints Doctrine::ERROR_ALREADY_EXISTS Doctrine::ERROR_CONSTRAINT
This removes any qualifiers from keys in associative fetches. some RDBMS , like for example SQLite, will be default use the fully qualified name for a column in assoc fetches if it is qualified in a query.
<br\><br\>
<i>Doctrine::PORTABILITY_FIX_CASE</i>
<br\><br\>
//Doctrine::PORTABILITY_FIX_CASE//
Convert names of tables and fields to lower or upper case in all methods. The case depends on the 'field_case' option that may be set to either CASE_LOWER (default) or CASE_UPPER
<br\><br\>
<i>Doctrine::PORTABILITY_NONE</i>
<br\><br\>
//Doctrine::PORTABILITY_NONE//
Turn off all portability features
<br\><br\>
<i>Doctrine::PORTABILITY_NUMROWS</i>
<br\><br\>
//Doctrine::PORTABILITY_NUMROWS//
Enable hack that makes numRows() work in Oracle
<br\><br\>
<i>Doctrine::PORTABILITY_RTRIM</i>
<br\><br\>
//Doctrine::PORTABILITY_RTRIM//
Right trim the data output for all data fetches. This does not applied in drivers for RDBMS that automatically right trim values of fixed length character values, even if they do not right trim value of variable length character values.
<br\><br\>
Using setAttribute() to enable portability for lowercasing and trimming
Lazy-connecting to database is handled via Doctrine_Db wrapper. When using Doctrine_Db instead of PDO / Doctrine_Adapter, lazy-connecting
to database is being performed (that means Doctrine will only connect to database when needed). <br\><br\>This feature can be very useful
to database is being performed (that means Doctrine will only connect to database when needed).
This feature can be very useful
when using for example page caching, hence not actually needing a database connection on every request. Remember connecting to database is an expensive operation.
<br\><br\>
<?php
renderCode("<?php
<codetype="php">
// we may use PDO / PEAR like DSN
// here we use PEAR like DSN
\$dbh = new Doctrine_Db('mysql://username:password@localhost/test');
Doctrine_Export drivers provide an easy database portable way of altering existing database tables.
<br\><br\>
NOTE: if you only want to get the generated sql (and not execute it) use Doctrine_Export::alterTableSql()
<br\><br\>
<?php
renderCode("<?php
<codetype="php">
\$dbh = new PDO('dsn','username','pw');
\$conn = Doctrine_Manager::getInstance()
->openConnection(\$dbh);
...
...
@@ -16,41 +19,39 @@ renderCode("<?php
// On mysql this method returns:
// ALTER TABLE mytable ADD COLUMN name VARCHAR(255)
?>");
?>
<br\><br\>
?></code>
Doctrine_Export::alterTable() takes two parameters:
<br\><br\>
string <i>$name</i>
<dd>name of the table that is intended to be changed. <br\>
array <i>$changes</i>
: string //$name// : name of the table that is intended to be changed.
<dd>associative array that contains the details of each type of change that is intended to be performed.
: array //$changes// : associative array that contains the details of each type of change that is intended to be performed.
The types of changes that are currently supported are defined as follows:
<ul>
<li\><i>name</i>
* //name//
New name for the table.
<li\><i>add</i>
* //add//
Associative array with the names of fields to be added as indexes of the array. The value of each entry of the array should be set to another associative array with the properties of the fields to be added. The properties of the fields should be the same as defined by the Doctrine parser.
<li\><i>remove</i>
* //remove//
Associative array with the names of fields to be removed as indexes of the array. Currently the values assigned to each entry are ignored. An empty array should be used for future compatibility.
<li\><i>rename</i>
* //rename//
Associative array with the names of fields to be renamed as indexes of the array. The value of each entry of the array should be set to another associative array with the entry named name with the new field name and the entry named Declaration that is expected to contain the portion of the field declaration already in DBMS specific SQL code as it is used in the CREATE TABLE statement.
<li\><i>change</i>
* //change//
Associative array with the names of the fields to be changed as indexes of the array. Keep in mind that if it is intended to change either the name of a field and any other properties, the change array entries should have the new names of the fields as array indexes.
</ul>
The value of each entry of the array should be set to another associative array with the properties of the fields to that are meant to be changed as array entries. These entries should be assigned to the new values of the respective properties. The properties of the fields should be the same as defined by the Doctrine parser.
*Theyare,inmanypeople's opinion, readable. Indeed, it was the innovation of subqueries that gave people the original idea of calling the early SQL “Structured Query Language.”
<li\>Theyare,inmanypeople's opinion, readable. Indeed, it was the innovation of subqueries that gave people the original idea of calling the early SQL “Structured Query Language.”
<li \> The default join type is <i>LEFT JOIN</i>. This join can be indicated by the use of either 'LEFTJOIN' clause or simply ',', hence the following queries are equal:
<div class='sql'>
<pre>
*Thedefaultjointypeis//LEFT JOIN//. This join can be indicated by the use of either 'LEFT JOIN' clause or simply ',', hence the following queries are equal:
<code>
SELECTu.*,p.*FROMUseruLEFTJOINu.Phonenumber
SELECTu.*,p.*FROMUseru,u.Phonenumberp
</pre>
</div>
</code>
<li \><i>INNER JOIN</i> produces an intersection between two specified components (that is, each and every record in the first component is joined to each and every record in the second component).
So basically <i>INNER JOIN</i> can be used when you want to efficiently fetch for example all users which have one or more phonenumbers.
<div class='sql'>
<pre>
*//INNER JOIN// produces an intersection between two specified components (that is, each and every record in the first component is joined to each and every record in the second component).
Sobasically//INNER JOIN// can be used when you want to efficiently fetch for example all users which have one or more phonenumbers.
<li \>Each <i>select_expr</i> indicates a column or an aggregate function value that you want to retrieve. There must be at least one <i>select_expr</i> in every SELECT statement.
<div class='sql'>
<pre>
*Each//select_expr// indicates a column or an aggregate function value that you want to retrieve. There must be at least one //select_expr// in every SELECT statement.
<code>
SELECTa.name,a.amountFROMAccounta
</pre>
</div>
</code>
<li \>An asterisk can be used for selecting all columns from given component. Even when using an asterisk the executed sql queries never actually use it
<li \>The WHERE clause, if given, indicates the condition or conditions that the records must satisfy to be selected. <i>where_condition</i> is an expression that evaluates to true for each row to be selected. The statement selects all rows if there is no WHERE clause.
<div class='sql'>
<pre>
</code>
*TheWHEREclause,ifgiven,indicatestheconditionorconditionsthattherecordsmustsatisfytobeselected.//where_condition// is an expression that evaluates to true for each row to be selected. The statement selects all rows if there is no WHERE clause.
<code>
SELECTa.*FROMAccountaWHEREa.amount>2000
</pre>
</div>
<li \>In the WHERE clause, you can use any of the functions and operators that DQL supports, except for aggregate (summary) functions
|| **primary** || bool true || Defines column as a primary key column. ||
|| **autoincrement** || bool true || Defines column as autoincremented column. If the underlying database doesn'tsupportautoincrementationnativelyitsemulatedwithtriggersandsequencetables.
||**default**||mixeddefault||Sets//default// as an application level default value for a column. When default value has been set for a column every time a record is created the specified column has the //default// as its value.
Defines column as autoincremented column. If the underlying database doesn'tsupportautoincrementationnativelyitsemulatedwithtriggersandsequencetables.
</td>
</tr>
<tr>
<tdclass='title' valign='top'>
<b>default</b>
</td>
<td class='title' valign='top'>
mixed default
</td>
<td class='title' valign='top'>
Sets <i>default</i> as an application level default value for a column. When default value has been set for a column every time a record is created the specified column has the <i>default</i> as its value.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>zerofill</b>
</td>
<td class='title' valign='top'>
boolean zerofill
</td>
<td class='title' valign='top'>
Defines column as zerofilled column. Only supported by some drivers.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>unsigned</b>
</td>
<td class='title' valign='top'>
boolean true
</td>
<td class='title' valign='top'>
Defines column with integer type as unsigned. Only supported by some drivers.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>fixed</b>
</td>
<td class='title' valign='top'>
boolean true
</td>
<td class='title' valign='top'>
Defines string typed column as fixed length.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>enum</b>
</td>
<td class='title' valign='top'>
array enum
</td>
<td class='title' valign='top'>
Sets <i>enum</i> as an application level enum value list for a column.
</td>
</tr>
<tr>
<td colspan=3>
»» Basic validators
<hr class='small'>
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>unique</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Acts as database level unique constraint. Also validates that the specified column is unique.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>nospace</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Nospace validator. This validator validates that specified column doesn'tcontainanyspace/newlinecharacters.<br/>
</td>
</tr>
<tr>
<tdclass='title' valign='top'>
<b>notblank</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Notblank validator. This validator validates that specified column doesn'tcontainonlyspace/newlinecharacters.Usefulinforexamplecommentpostingapplications
whereusersarenotallowedtopostemptycomments.
</td>
</tr>
<tr>
<tdclass='title' valign='top'>
<b>notnull</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Acts as database level notnull constraint as well as notnull validator for the specified column.
</td>
</tr>
<tr>
<td colspan=3>
»» Advanced validators
<hr class='small'>
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>email</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Email validator. Validates that specified column is a valid email address.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>date</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Date validator.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>range</b>
</td>
<td class='title' valign='top'>
array(min, max)
</td>
<td class='title' valign='top'>
Range validator. Validates that the column is between <i>min</i> and <i>max</i>.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>country</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Country code validator validates that specified column has a valid country code.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>regexp </b>
</td>
<td class='title' valign='top'>
string regexp
</td>
<td class='title' valign='top'>
Regular expression validator validates that specified column matches <i>regexp</i>.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>ip</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Ip validator validates that specified column is a valid internet protocol address.
The example array at the end of the data types section may be used with createTable()
to create a portable table on the DBMS of choice (please refer to the main Doctrine
documentation to find out what DBMS back ends are properly supported).
It should also be noted that the following examples do not cover the creation
and maintenance of indices, this chapter is only concerned with data types and the proper usage thereof.
<br \><br \>
It should be noted that the length of the column affects in database level type as well as application level validated length (the length that is validated with Doctrine validators).
<br \><br \>
Example 1. Column named 'content' with type 'string' and length 3000 results in database type 'TEXT' of which has database level length of 4000. However when the record is validated it is only allowed to have 'content' -column with maximum length of 3000.
<br \><br \>
Example 2. Column with type 'integer' and length 1 results in 'TINYINT'onmanydatabases.
Within the Doctrine API there are a few modifiers that have been designed to aid in optimal table design. These are:
<br\>
<ul>
<li\>The notnull modifiers
<li\>The length modifiers
<li\>The default modifiers
<li\>unsigned modifiers for some field definitions, although not all DBMS's support this modifier for integer field types.
* The notnull modifiers
<li\>zerofill modifiers (not supported by all drivers)
* The length modifiers
<li\>collation modifiers (not supported by all drivers)
* The default modifiers
* unsigned modifiers for some field definitions, although not all DBMS's support this modifier for integer field types.
* zerofill modifiers (not supported by all drivers)
* collation modifiers (not supported by all drivers)
* fixed length modifiers for some field definitions.
<li\>fixed length modifiers for some field definitions.
</ul>
Building upon the above, we can say that the modifiers alter the field definition to create more specific field types for specific usage scenarios. The notnull modifier will be used in the following way to set the default DBMS NOT NULL Flag on the field to true or false, depending on the DBMS's definition of the field value: In PostgreSQL the "NOT NULL" definition will be set to "NOT NULL", whilst in MySQL (for example) the "NULL" option will be set to "NO". In order to define a "NOT NULL" field type, we simply add an extra parameter to our definition array (See the examples in the following section)
<br\><br\>
<?php
'sometime' = array(
'type' = 'time',
...
...
@@ -26,9 +29,13 @@ Building upon the above, we can say that the modifiers alter the field definitio
?>
<br\><br\>
Using the above example, we can also explore the default field operator. Default is set in the same way as the notnull operator to set a default value for the field. This value may be set in any character set that the DBMS supports for text fields, and any other valid data for the field's data type. In the above example, we have specified a valid time for the "Time" data type, '12:34:05'. Remember that when setting default dates and times, as well as datetimes, you should research and stay within the epoch of your chosen DBMS, otherwise you will encounter difficult to diagnose errors!
<br\><br\>
<?php
...
...
@@ -42,7 +49,9 @@ Using the above example, we can also explore the default field operator. Default
<br\><br\>
The above example will create a character varying field of length 12 characters in the database table. If the length definition is left out, Doctrine will create a length of the maximum allowable length for the data type specified, which may create a problem with some field types and indexing. Best practice is to define lengths for all or most of your fields.
<p>Most users at one time or another have dealt with hierarchical data in a SQL database and no doubt learned that the management of hierarchical data is not what a relational database is intended for. The tables of a relational database are not hierarchical (like XML), but are simply a flat list. Hierarchical data has a parent-child relationship that is not naturally represented in a relational database table.</p>
<p>For our purposes, hierarchical data is a collection of data where each item has a single parent and zero or more children (with the exception of the root item, which has no parent). Hierarchical data can be found in a variety of database applications, including forum and mailing list threads, business organization charts, content management categories, and product categories.</p>
<p>In a hierarchical data model, data is organized into a tree-like structure. The tree structure allows repeating information using parent/child relationships. For an explanation of the tree data structure, see here <ahref="http://en.wikipedia.org/wiki/Tree_data_structure">http://en.wikipedia.org/wiki/Tree_data_structure</a></p>
<p>The node interface, for inserting and manipulating nodes within the tree, is accessed on a record level. A full implementation of this interface will be as follows:</p>
<p>If performing batch tree manipulation tasks, then remember to refresh your records (see record::refresh()), as any transformations of the tree are likely to affect all instances of records that you have in your scope.</p>
<p>You can save an already existing node using record::save() without affecting it's position within the tree. Remember to never set the tree specific record attributes manually.</p>
<p>If you are inserting or moving a node within the tree, you must use the appropriate node method. Note: you do not need to save a record once you have inserted it or moved it within the tree, any other changes to your record will also be saved within these operations. You cannot save a new record without inserting it into the tree.</p>
<p>If you wish to delete a record, you MUST delete the node and not the record, using $record->deleteNode() or $record->getNode()->delete(). Deleting a node, will by default delete all its descendants. if you delete a record without using the node::delete() method you tree is likely to become corrupt (and fall down)!</p>
<p>The difference between descendants and children is that descendants include children of children whereas children are direct descendants of their parent (real children not gran children and great gran children etc etc).</p>
<p>Managing tree structures in doctrine is easy. Doctrine currently fully supports Nested Set, and plans to support the other implementations soon. To set your model to act as a tree, simply add the code below to your models table definition.</p>
<p>Now that Doctrine knows that this model acts as a tree, it will automatically add any required columns for your chosen implementation, so you do not need to set any tree specific columns within your table definition.</p>
<p>Doctrine has standard interface's for managing tree's, that are used by all the implementations. Every record in the table represents a node within the tree (the table), so doctrine provides two interfaces, Tree and Node.</p>
Doctrinehasstandardinterface's for managing tree's,thatareusedbyalltheimplementations.Everyrecordinthetablerepresentsanodewithinthetree(thetable),sodoctrineprovidestwointerfaces,TreeandNode.
<p>You can traverse a Tree in different ways, please see here for more information <ahref="http://en.wikipedia.org/wiki/Tree_traversal">http://en.wikipedia.org/wiki/Tree_traversal</a>.</p>
<p>The most common way of traversing a tree is Pre Order Traversal as explained in the link above, this is also what is known as walking the tree, this is the default approach when traversing a tree in Doctrine, however Doctrine does plan to provide support for Post and Level Order Traversal (not currently implemented)</p>
<p>The tree interface, for creating and accessing the tree, is accessed on a table level. A full implementation of this interface would be as follows:</p>
<p>Basically Nested Set is optimized for traversing trees, as this can be done with minimal queries, however updating the tree can be costly as it will affect all rows within the table.</p>
<p>The most effective way to traverse a tree from the root node, is to use the tree::fetchTree() method.
It will by default include the root node in the tree and will return an iterator to traverse the tree.</p>
<p>To traverse a tree from a given node, it will normally cost 3 queries, one to fetch the starting node, one to fetch the branch from this node, and one to determine the level of the start node, the traversal algorithm with then determine the level of each subsequent node for you.</p>
<p>The nested implementation can be configured to allow your table to have multiple root nodes, and therefore multiple trees within the same table.</p>
<p>The example below shows how to setup and use multiple roots based upon the set up above:</p>
Doctrine supports sequences for generating record identifiers. Sequences are a way of offering unique IDs for data rows. If you do most of your work with e.g. MySQL, think of sequences as another way of doing AUTO_INCREMENT.
<br\><br\>
Doctrine knows how to do sequence generation in the background so you don't have to worry about calling database specific queries - Doctrine does it for you, all you need to do
is define a column as a sequence column and optionally provide the name of the sequence table and the id column name of the sequence table.
<br\><br\>
Consider the following record definition:
<br\><br\>
<?php
renderCode("<?php
<codetype="php">
class Book extends Doctrine_Record {
public function setTableDefinition()
{
...
...
@@ -15,35 +20,41 @@ class Book extends Doctrine_Record {
\$this->hasColumn('name', 'string');
}
}
?>");
?>
<br\><br\>
?></code>
By default Doctrine uses the following format for sequence tables [tablename]_seq. If you wish to change this you can use the following
Doctrine uses column named id as the sequence generator column of the sequence table. If you wish to change this globally (for all connections and all tables)
Doctrine knows how to fetch collections efficiently using a subselect.
<br\><br\>
<li\><bclass="title">Executing SQL statements later, when needed</b><br\>
* <b class="title">Executing SQL statements later, when needed**
The connection never issues an INSERT or UPDATE until it is actually needed. So if an exception occurs and you need to abort the transaction, some statements will never actually be issued. Furthermore, this keeps lock times in the database as short as possible (from the late UPDATE to the transaction end).
<br\><br\>
<li\><bclass="title">Join fetching</b><br\>
* <b class="title">Join fetching**
Doctrine knows how to fetch complex object graphs using joins and subselects
<li><ahref="http://en.wikipedia.org/wiki/Atomicity">Atomicity</a>referstotheabilityoftheDBMStoguaranteethateitherallofthetasksofatransactionareperformedornoneofthemare.Thetransferoffundscanbecompletedoritcanfailforamultitudeofreasons,butatomicityguaranteesthatoneaccountwon't be debited if the other is not credited as well.</li>
</ul>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Database_consistency" title="Database consistency">Consistency</a> refers to the database being in a legal state when the transaction begins and when it ends. This means that a transaction can'tbreaktherules,or<i>integrityconstraints</i>,ofthedatabase.Ifanintegrityconstraintstatesthatallaccountsmusthaveapositivebalance,thenanytransactionviolatingthisrulewillbeaborted.</li>
<li><ahref="http://en.wikipedia.org/wiki/Durability_%28computer_science%29"title="Durability (computer science)">Durability</a>referstotheguaranteethatoncetheuserhasbeennotifiedofsuccess,thetransactionwillpersist,andnotbeundone.Thismeansitwillsurvivesystemfailure,andthatthe<ahref="http://en.wikipedia.org/wiki/Database_system"title="Database system">databasesystem</a>hascheckedtheintegrityconstraintsandwon't need to abort the transaction. Typically, all transactions are written into a <a href="http://en.wikipedia.org/wiki/Database_log" title="Database log">log</a> that can be played back to recreate the system to its state right before the failure. A transaction can only be deemed committed after it is safely in the log.</li>
<li>[http://en.wikipedia.org/wiki/AtomicityAtomicity]referstotheabilityoftheDBMStoguaranteethateitherallofthetasksofatransactionareperformedornoneofthemare.Thetransferoffundscanbecompletedoritcanfailforamultitudeofreasons,butatomicityguaranteesthatoneaccountwon't be debited if the other is not credited as well.</li>
<li>[http://en.wikipedia.org/wiki/Database_consistency Consistency] refers to the database being in a legal state when the transaction begins and when it ends. This means that a transaction can'tbreaktherules,or//integrity constraints//, of the database. If an integrity constraint states that all accounts must have a positive balance, then any transaction violating this rule will be aborted.</li>
<li>[http://en.wikipedia.org/wiki/Durability_%28computer_science%29Durability]referstotheguaranteethatoncetheuserhasbeennotifiedofsuccess,thetransactionwillpersist,andnotbeundone.Thismeansitwillsurvivesystemfailure,andthatthe[http://en.wikipedia.org/wiki/Database_systemdatabasesystem]hascheckedtheintegrityconstraintsandwon't need to abort the transaction. Typically, all transactions are written into a [http://en.wikipedia.org/wiki/Database_log log] that can be played back to recreate the system to its state right before the failure. A transaction can only be deemed committed after it is safely in the log.</li>
- //from [http://www.wikipedia.org wikipedia]//
In Doctrine all operations are wrapped in transactions by default. There are some things that should be noticed about how Doctrine works internally:
Doctrine_Query provides having() method for adding HAVING conditions to the DQL query. This method is identical in function to the Doctrine_Query::where() method.
<br\><br\>
If you call having() multiple times, the conditions are ANDed together; if you want to OR a condition, use orHaving(). <br\><br\>
<?php
renderCode("<?php
If you call having() multiple times, the conditions are ANDed together; if you want to OR a condition, use orHaving().